背景
最近搞open cv識別物體的項目家卖,因為客戶需求纠炮,最終決定使用js來實現(xiàn)嘉裤。
捕獲攝像頭 是通過navigator.mediaDevices槐瑞,前提是 ios 需要11以上必須是https犹芹, android到還好崎页。
識別物體通過opencv js 。
雖然坎坎坷坷 但是第一版還是上線了 除了https和默認顯示后置攝像頭(Android的枚舉攝像頭 根據(jù)label判斷是否是后置的腰埂,IOS 中的facingMode:{exact:"environment"} 是有效的)飒焦,其它還算順利。
然后 然后 opencv js 10M 這個大小比較要命屿笼。牺荠。。
opencv是通過Emscripten 將c編譯成js的?
所以打算ctrl+c驴一, ctrl+v 精減下源碼志电,再編譯下,將js文件減少到500kb之內(nèi)是沒有問題的蛔趴。
安裝
安裝 Emscripten 還是比較簡單的?
https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html
git clone https://github.com/juj/emsdk.git
cd emsdk
git pull
emsdk install latest
我是windows下挑辆,安裝的時候 下載LLMV的 很慢總失敗,后來掛了代理 才下載成功孝情。鱼蝉。。
編譯
使用命令 emcc 可以把c++ 編譯成js箫荡,如果你一上來就敲emcc 那么不好意思 是不存在的 先要運行下面的命令設(shè)置好當前進程的環(huán)境才能編譯
C:\inno\libraries\emsdk\emsdk activate latest
C:\inno\libraries\emsdk\emsdk_env.bat
然后可以使用emcc了魁亦,說白了 每次打開cmd窗口 先要調(diào)用下上面那個命令
編譯很簡單?
emcc main.cpp -o main.js
main.cpp
void main()
{
print("Hello World");
}
輸出的main.js 內(nèi)容很多,內(nèi)嵌了一些基礎(chǔ)實現(xiàn)羔挡,但我搜索不到“Hello World”的字樣洁奈,雖然沒弄明白原理但覺得可以用來加密代碼
折騰了很久 最后我選用的編譯選項是
emcc --bind demo.cpp -o demo.js -s WASM=0 -O3 --memory-init-file 0 --pre-js pre.js --post-js post.js
我覺得這才是實戰(zhàn)所需要的,實在看不上-s EXPORTED_FUNCTIONS="['_main', '_myfunc']" 然后用module.ccall绞灼。利术。。
--bind 是指可以EMSCRIPTEN_BINDINGS來聲明需要導(dǎo)出的類以及單個函數(shù)
-s WASM=0 不要生成web assembly 即.wasm文件低矮,因為這里暫時用不上
-O3 優(yōu)化級別是3的 默認是不會優(yōu)化的
--memory-init-file 這個完全是為了填-O3的坑印叁,因為打開優(yōu)化后生成文件除了.js還有一個 .js.mem, js加載進來之后還要去加載.js.mem文件之后才能用導(dǎo)出的函數(shù) 這里是異步的,需要注冊Module.onRuntimeInitialized這個回調(diào)才能繼續(xù)轮蜕,那么這個參數(shù)是不要 生成mem文件,舍棄這些麻煩昨悼。
--pre-js pre.js? 和 --post-js post.js 是加在生成的js首尾的js,本來是用來做匿名的跃洛,但看生成的話 有點搞笑率触。。汇竭。
可以欣賞這里所有源碼
變量傳遞
https://kripken.github.io/emscripten-site/docs/api_reference/emscripten.h.html#inline-assembly-javascript 這里講了使用EM_XX 怎么在c++里內(nèi)聯(lián)js代碼葱蝗,然后怎么交互,c++怎么傳值(數(shù)字和指針)給js韩玩,js怎么返回值(數(shù)字和指針)給c++ 寫的很詳細 足夠了
在EMSCRIPTEN_BINDINGS中傳遞Uint8Array
cpp
#include <string>
#include <malloc.h>
#include <functional>
#include <emscripten/bind.h>
typedef? unsigned char uchar;
class MyClass
{
public:
? ? MyClass()
? ? {
? ? ? ? data = new uchar[100];
? ? ? ? size = 100;
? ? }
? ? emscripten::val getData () const
? ? {
? ? ? ? return emscripten::val(emscripten::memory_view<uchar>(size,data));
? ? }
private:
? ? int size;
? ? uchar* data;
};
EMSCRIPTEN_BINDINGS(my_class) {
? emscripten::class_<MyClass>("MyClass")
? ? .constructor()
? ? .property("data", &MyClass::getData)
? ? ;
}
js
var my = new inno.MyClass();
var ctx = cin.getContext('2d');
my.data.set(ctx.getImageData(0, 0, w, h).data);
var imgData = new ImageData(new Uint8ClampedArray(my.data), w, h);
ctx.putImageData(imgData, 0, 0);
另一種方式
cpp
class MyClass{
public:
? ? MyClass(int w, int h){
? ? }
? ? void setData(intptr_t frame4b_ptr)
? ? {
? ? //frame4b_ptr[0]
? ? }
};
EMSCRIPTEN_BINDINGS(my_class) {
? emscripten::class_<MyClass>("MyClass")
? ? .constructor()
? .function("update", &MyClass::updasetDatate, emscripten::allow_raw_pointers())
? ? ;
}
JS
var data = this.ctx.getImageData(0, 0, w, h);
var bytes = arrayToHeap(data.data);
my.setData(bytes.byteOffset);
data.data.set(bytes);
this.ctx.putImageData(data, 0, 0)
;
function freeArray(heapBytes) {
? ? Module._free(heapBytes.byteOffset);
}
function arrayToHeap(typedArray) {
? ? var numBytes = typedArray.length * typedArray.BYTES_PER_ELEMENT;
? ? var ptr = Module._malloc(numBytes);
? ? heapBytes = Module.HEAPU8.subarray(ptr, ptr + numBytes);
? ? heapBytes.set(typedArray);
? ? return heapBytes;
}