Skip to content

Add experimental support for WebAssembly #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
40 changes: 35 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
This is a JavaScript binding that exposes OpenCV library to the web. This project is made possible by support of Intel corporation. Currently, this is based on OpenCV 3.1.0.

### How to Build

You can build two different versions of OpenCV.js: asm.js or WebAssembly (experimental). If you want to build the later, you will need the "incoming" version of Emscripten.

#### asm.js version (default)

1. Get the source code

```
Expand All @@ -23,19 +28,44 @@ This is a JavaScript binding that exposes OpenCV library to the web. This projec
3. Patch Emscripten & Rebuild.

```
patch -p1 < PATH/TO/patch_emscripten_master.diff
```
4. Rebuild emscripten
```
patch -p1 < PATH/TO/patch_emscripten.diff -d emscripten/master
./emsdk install sdk-master-64bit --shallow
```

5. Compile OpenCV and generate bindings by executing make.py script.
4. Compile OpenCV and generate bindings by executing make.py script.

```
python make.py
```

#### WebAssembly version (experimental)

1. Get the source code (just like above)

2. Install the "incoming" branch of emscripten. You can obtain emscripten by using [Emscripten SDK](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, something like that ?
2. Install the "incoming" branch of emscripten. You can obtain emscripten by using [Emscripten SDK](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html). At the time of writing, the current version of Emscripten does not support WebAssembly yet, so you will need to install the "incoming" branch (see as well WebAssembly’s [Developer’s Guide](http://webassembly.org/getting-started/developers-guide/) for a short introduction about WebAssembly).

At the time of writing, the current version of Emscripten does not support WebAssembly yet, so you will need to install the "incoming" branch.

```
./emsdk update
./emsdk install sdk-incoming-64bit --shallow
./emsdk activate sdk-incoming-64bit
source ./emsdk_env.sh
```
3. Patch Emscripten & Rebuild.

```
patch -p1 < PATH/TO/patch_emscripten.diff -d emscripten/incoming
./emsdk install sdk-incoming-64bit --shallow
```

4. Compile OpenCV and generate bindings by executing make.py script.

```
python make.py --wasm
```



### Tests
Test suite contains several tests and examples demonstrating how the API can be used. Run the tests by launching test/tests.html file usig a browser.

Expand Down
15 changes: 15 additions & 0 deletions bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,15 @@ EMSCRIPTEN_BINDINGS(Utils) {
.element(&Point2f::x)
.element(&Point2f::y);

value_array<Size2f>("Size2f")
.element(&Size2f::height)
.element(&Size2f::width);

value_object<RotatedRect>("RotatedRect")
.field("angle", &RotatedRect::angle)
.field("center", &RotatedRect::center)
.field("size", &RotatedRect::size);

emscripten::class_<cv::Rect_<int>> ("Rect")
.constructor<>()
.constructor<const cv::Point_<int>&, const cv::Size_<int>&>()
Expand Down Expand Up @@ -437,6 +446,10 @@ namespace Wrappers {
return cv::adaptiveThreshold(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}

void magnitudeThreshold_wrapper(const cv::Mat& arg1, const cv::Mat& arg2, cv::Mat& arg3, double thresh, double minValue, double maxValue ){
return cv::magnitudeThreshold(arg1, arg2, arg3, thresh, minValue, maxValue);
}

void add_wrapper(const cv::Mat& arg1, const cv::Mat& arg2, cv::Mat& arg3, const cv::Mat& arg4, int arg5) {
return cv::add(arg1, arg2, arg3, arg4, arg5);
}
Expand Down Expand Up @@ -2458,6 +2471,8 @@ EMSCRIPTEN_BINDINGS(testBinding) {

function("adaptiveThreshold", select_overload<void(const cv::Mat&, cv::Mat&, double, int, int, int, double)>(&Wrappers::adaptiveThreshold_wrapper));

function("magnitudeThreshold", select_overload<void(const cv::Mat&, const cv::Mat&, cv::Mat&, double, double, double)>(&Wrappers::magnitudeThreshold_wrapper));

function("add", select_overload<void(const cv::Mat&, const cv::Mat&, cv::Mat&, const cv::Mat&, int)>(&Wrappers::add_wrapper));

function("addWeighted", select_overload<void(const cv::Mat&, double, const cv::Mat&, double, double, cv::Mat&, int)>(&Wrappers::addWeighted_wrapper));
Expand Down
Binary file added build/cv-wasm.data
Binary file not shown.
30 changes: 30 additions & 0 deletions build/cv-wasm.js

Large diffs are not rendered by default.

Binary file added build/cv-wasm.wasm
Binary file not shown.
25 changes: 19 additions & 6 deletions make.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
#!/usr/bin/python
import os, sys, re, json, shutil
import argparse
from subprocess import Popen, PIPE, STDOUT


# parse the command-line options
parser = argparse.ArgumentParser()
parser.add_argument( "--wasm", action="store_true", help="Create a .wasm file (WebAssembly format, experimental) instead of asm.js format." )
clArguments = parser.parse_args()

# Startup
exec(open(os.path.expanduser('~/.emscripten'), 'r').read())

Expand Down Expand Up @@ -184,8 +191,15 @@ def stage(text):
assert os.path.exists('bindings.bc')

stage('Building OpenCV.js')
opencv = os.path.join('..', '..', 'build', 'cv.js')
data = os.path.join('..', '..', 'build', 'cv.data')

if clArguments.wasm:
emcc_args += "-s WASM=1".split( " " )
basename = "cv-wasm"
else:
basename = "cv"

destFiles = [ os.path.join('..', '..', 'build', basename + ext ) for ext in [ ".js", ".data", ".wasm" ] ]
opencv = destFiles[0]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about following directory structure?

build
  |----js
  |      |---- cv.js
  |      |---- cv.data
  |----wasm
            |----cv.wasm
            |----cv.data

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, putting all the files in the same folder with different names seemed simpler to me for the time being, as the .js file generated by emscripten expects to find its related .data (and .wasm, in case of WebAssembly) in the same folder as the .html file.
Having all the files in the same folder with different names depending on the technology used (WebAssembly or asm.js) simplifies the use: the user can just copy everything in build/ to the folder of his/her .html file, and then either load cv.js or cv-wasm.js depending on what technology s/he wants to use.


tests = os.path.join('..', '..', 'test')

Expand Down Expand Up @@ -245,10 +259,9 @@ def stage(text):
}));
""" % (out,)).lstrip())


shutil.copy2(opencv, tests)
if os.path.exists(data):
shutil.copy2(data, tests)
for f in destFiles:
if os.path.exists(f):
shutil.copy2(f, tests)

finally:
os.chdir(this_dir)
8 changes: 4 additions & 4 deletions patch_emscripten_master.diff → patch_emscripten.diff
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
--- ./emscripten/master/system/include/emscripten/bind.h 2016-12-20 13:05:20.498980029 -0800
+++ ./emscripten/master/system/include/emscripten/bind.h 2016-12-20 13:12:11.599146929 -0800
--- ./system/include/emscripten/bind.h 2016-12-20 13:05:20.498980029 -0800
+++ ./system/include/emscripten/bind.h 2016-12-20 13:12:11.599146929 -0800
@@ -999,7 +999,7 @@
};
}
Expand Down Expand Up @@ -54,8 +54,8 @@
template<typename WrapperType>
val wrapped_extend(const std::string& name, const val& properties) {

--- ./emscripten/master/src/embind/embind.js 2016-12-20 13:05:20.498980029 -0800
+++ ./emscripten/master/src/embind/embind.js 2016-12-20 13:12:11.599146929 -0800
--- ./src/embind/embind.js 2016-12-20 13:05:20.498980029 -0800
+++ ./src/embind/embind.js 2016-12-20 13:12:11.599146929 -0800
Copy link
Collaborator

@huningxin huningxin May 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this file change needed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes the patch more generic, and usable with both emscripten/master and emscripten/incoming by changing just the patch command line (cf readme).

@@ -2077,6 +2077,7 @@
var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */);
var func = craftInvokerFunction(humanName, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn);
Expand Down