业务需要,将C/C++算法代码集成到h5/小程序进行展示,需要进行WebAssembly封装成 .js和 .wasm
首先是C/C++代码和CMakeList.txt,都要进行修改,C/C++的那一套在Emscripten可是行不通的。
工程代码结构:CMakeList.txt, src/xxx.cpp, src/xxx.hpp。
1. ubuntu下安装Emscripten
git clone https://github.com/juj/emsdk.git
cd emsdk
./emsdk update
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
echo ${EMSCRIPTEN}
检查是否安装好查看emcc em++版本
emcc --version
em++ --version
2. opencv.js编译
source版的opencv里,需要进行opencv的js编译,来生成opencv.js opencv_js.js。
git clone https://github.com/opencv/opencv.git
cd opencv
python ./platforms/js/build_js.py build_js
python ./platforms/js/build_js.py build_wasm --build_wasm
python ./platforms/js/build_js.py --emscripten_dir ../emsdk/up/emscripten build_wasm --build_wasm
第一次build_wasm后可能会报错,index重定义导致冲突。
strings.h和bind.h不可修改,modules/js/bindings.cpp是生成的,可以修改:
emscripten::value_array<cv::Scalar_<double>> ("Scalar")
.element(index<0>())
.element(index<1>())
.element(index<2>())
.element(index<3>());
改成
emscripten::value_array<cv::Scalar_<double>> ("Scalar")
.element(emscripten::index<0>())
.element(emscripten::index<1>())
.element(emscripten::index<2>())
.element(emscripten::index<3>());
进去build_wasm文件下, 终端下 make,会显示编译成功。
在bin目录下有编译成功的opencv.js文件哈,大小7.4M左右。
3. Dockerfile
有简单的方法,Dockerfile搞定第1-2步,配置好环境。Dockerfile如下:
FROM trzeci/emscripten
ARG OPENCV_VERSION=4.1.0
RUN mkdir -p /third-party \
&& wget \
-c https://github.com/opencv/opencv/archive/$OPENCV_VERSION.tar.gz \
-O /third-party/opencv-$OPENCV_VERSION.tar.gz \
&& tar \
-C /third-party/ \
-xvf /third-party/opencv-$OPENCV_VERSION.tar.gz \
&& mv /third-party/opencv-$OPENCV_VERSION /third-party/opencv \
&& rm /third-party/opencv-$OPENCV_VERSION.tar.gz
WORKDIR /third-party/opencv
RUN python ./platforms/js/build_js.py build_wasm --build_wasm
还是docker配置更方便!
docker build -t images_test:0.1 .
docker run -it --name=xxxx images_test:0.1
4. 编写CMakeList.txt
和C/C++的不太一样哦,主要是emscripten的配置。
cmake_minimum_required(VERSION 2.8)
project(ImageTest)
set( CMAKE_CXX_STANDARD 11 )
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif(NOT CMAKE_BUILD_TYPE)
include_directories( /third-party/opencv/include )
include_directories( /third-party/opencv/build_wasm )
file( GLOB opencv_include_modules "/third-party/opencv/modules/*/include" )
include_directories( ${opencv_include_modules} )
add_executable( ImageTest src/*.cpp src/*.hpp )
file( GLOB opencv_js "/third-party/opencv/build_wasm/lib/*.a" )
target_link_libraries( ImageTest ${opencv_js} )
set(COMPILE_FLAGS "-Wno-missing-prototypes")
set_target_properties( ImageTest PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS})
set(EMSCRIPTEN_LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS} -s WASM=1")
set(EMSCRIPTEN_LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS} -std=c++1z -O3 --llvm-lto 1")
set(EMSCRIPTEN_LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS} -s ASSERTIONS=2")
set(EMSCRIPTEN_LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS} --memory-init-file 0 -s TOTAL_MEMORY=134217728 -s ALLOW_MEMORY_GROWTH=1")
set(EMSCRIPTEN_LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS} --bind")
set(EMSCRIPTEN_LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS} -s ENVIRONMENT=web")
set(EMSCRIPTEN_LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS} -s MODULARIZE")
set(EMSCRIPTEN_LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS} -s EXPORT_ES6")
set_target_properties( ImageCheck PROPERTIES LINK_FLAGS "${EMSCRIPTEN_LINK_FLAGS} --bind" )
5. cpp和hpp的编写配置
#include "ImageCheck.hpp"
#include <emscripten.h>
#include <emscripten/bind.h>
EMSCRIPTEN_BINDINGS(my_module)
{
emscripten::value_object<imgTest>("imgTest")
.field("xx1", &imgTest::xx1)
.field("xx2", &imgTest::xx2)
.field("xx3", &imgTest::xx3);
emscripten::function("ImageTest", &ImageTest);
//emscripten::function("ImageTest", select_overload<imgTest(const cv::Mat&)>(&ImageTest));
}
主要是增加EMSCRIPTEN_BINDINGS的声明。到js的声明是对象,不管类型。这里声明了 结构体 和 函数 。
cpp中输入接口的图像输入:
#include "ImageCheck.hpp"
#include <emscripten.h>
#include <emscripten/bind.h>
imgTest ImageTest(size_t img_data, int width, int height){
auto data_array = reinterpret_cast<void *>(img_data);
cv::Mat rgbaImage(height, width, CV_8UC4, data_array); // RGBA
cv::Mat bgrImage;
cv::cvtColor(rgbaImage, bgrImage, cv::COLOR_RGBA2BGR);
C++的结构体是:
struct imgTest {
int xx1;
int xx2;
int xx3;
};
6. 编译生成js 和wasm
进入工程目录:
mkdir build
cd build
emcmake cmake ..
emmake make
build里会生成js 和 wasm,之后可以用来前端集成了。
7. js如何传递图像数据给C++
在index.ts里编辑:
const buffer = my_module._malloc(cvMat.width * cvMat.height * 4);
my_module.HEAPU8.set(cvMat.data, buffer);
const result = my_module.ImageTest(buffer, cvMat.width, cvMat.height);
my_module._free(buffer);