🌟
[環境構築]Dockerで、WebAssemblyのOpenCV,C++,CMakeの開発環境を構築してみた。
Abstract
WebAssemblyのOpenCVってなかなか情報がない!! そんな中、DockerでWebAssemblyのOpenCV,C++,CMakeの開発環境を構築してみた。
ここ(やっぱりWasm は C++!!!~Wasm/EmscriptenでOpenCVを使う~)がすごい参考になった。
前提
- VirtualBoxインストール。 VirtualBoxインストール
- VirtualBoxにLinux22.04をインストール。VirtualBox上でUbuntu22.04を構築
- Dockerインストール。[環境構築]Ubuntu22.04にDockerをインストール
1.作業フォルダ作成
Ubuntuで、適当にフォルダを新規作成。
$ cd ~ && mkdir wasm1st && cd wasm1st
2.先に必要ファイルを作成する。(CMakeLists.txt,Dockerfile,index.html,src/main.cpp)
2-1.CMakeLists.txtを作成する。
CMakeLists.txt
$ vi CMakeLists.txt
CMakeLists.txtの中身
cmake_minimum_required(VERSION 3.5)
project(cppmain LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s USE_ZLIB=1 -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0 --bind -O3 -s LLD_REPORT_UNDEFINED -s DEMANGLE_SUPPORT=1")
include_directories(/build/opencv/build_wasm/install/include/opencv4)
file(GLOB_RECURSE sources1 "src/main.cpp")
add_executable(${PROJECT_NAME} ${sources1})
file(GLOB opencv_core "/build/opencv/build_wasm/install/lib/*.a")
file(GLOB opencv_3rdparty "/build/opencv/build_wasm/install/lib/opencv4/3rdparty/*.a")
target_link_libraries(${PROJECT_NAME} ${opencv_core} ${opencv_3rdparty})
2-2.main.cppを作成する。
CMakeLists.txt
$ mkdir src && cd src
$ vi main.cpp
main.cppの中身
#include <SDL/SDL.h>
#include <emscripten.h>
#include <emscripten/bind.h>
#include <opencv2/opencv.hpp>
namespace {
constexpr int WIDTH = 640;
constexpr int HEIGHT = 480;
SDL_Surface *screen = nullptr;
} // namespace
extern "C" int main(int argc, char **argv) {
SDL_Init(SDL_INIT_VIDEO);
screen = SDL_SetVideoMode(WIDTH, HEIGHT, 32, SDL_SWSURFACE);
return 0;
}
void doOpenCvTask(size_t addr, int width, int height, int cnt) {
auto data = reinterpret_cast<void *>(addr);
cv::Mat rgbaMat(height, width, CV_8UC4, data);
cv::Mat rgbMat;
cv::Mat rgbOutMat;
cv::Mat outMat;
cv::cvtColor(rgbaMat, rgbMat, cv::COLOR_RGBA2RGB);
rgbMat.convertTo(rgbOutMat, -1, 1.0, cnt - 100.0);
cv::cvtColor(rgbOutMat, outMat, cv::COLOR_RGB2RGBA);
if (SDL_MUSTLOCK(screen))
SDL_LockSurface(screen);
cv::Mat dstRGBAImage(height, width, CV_8UC4, screen->pixels);
outMat.copyTo(dstRGBAImage);
if (SDL_MUSTLOCK(screen))
SDL_UnlockSurface(screen);
SDL_Flip(screen);
}
EMSCRIPTEN_BINDINGS(my_module) {
emscripten::function("doOpenCvTask", &doOpenCvTask);
}
2-3.index.htmlを作成する。
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<h1>By Wasm OpenCV</h1>
<video id="video"></video>
<canvas id="canvas" width="640" height="480"></canvas>
<script>
const init = async () => {
const video = document.getElementById("video");
video.width = 640;
video.height = 480;
const stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {
width: { ideal: video.width },
height: { ideal: video.height },
},
});
video.srcObject = stream;
video.play();
const videoCanvas = document.getElementById("canvas");
videoCanvas.width = video.width;
videoCanvas.height = video.height;
const context = videoCanvas.getContext("2d");
let cnt = 0;
const loopNum =200;
let sumTime = 0.0;
const updateCanvas = () => {
context.drawImage(video, 0, 0);
const data = context.getImageData(
0,
0,
videoCanvas.width,
videoCanvas.height
);
const start1 = performance.now();
const buffer = Module._malloc(data.data.length);
Module.HEAPU8.set(data.data, buffer);
Module.doOpenCvTask(buffer, data.width, data.height, cnt);
Module._free(buffer);
const end1 = performance.now();
cnt++;
sumTime += end1 - start1;
if(cnt == loopNum) {
console.log(`doOpenCvTask+Memory: ${sumTime / 100} msec (${loopNum} times avg)`);
sumTime = 0;
cnt = 0;
}
requestAnimationFrame(updateCanvas);
};
updateCanvas();
};
window.Module = {
canvas: document.getElementById("canvas"),
onRuntimeInitialized: init,
// print: function(text) { console.log(text); },
print: function(text){},
printErr: function(text) { console.log(text); }
};
</script>
<script src="dist/build/cppmain.js"></script>
</body>
</html>
2-4.Dockerfileを作成する。
Dockerfile
$ vi Dockerfile
Dockerfileの中身
FROM emscripten/emsdk:1.39.16
WORKDIR /build/app
COPY CMakeLists.txt ./
COPY src ./src
COPY index.html ./
SHELL ["/bin/bash", "-c"]
この段階のフォルダ構成
$ ls -l ~/wasm1st
-rw-rw-r-- 1 jun jun 670 1月 17 19:44 CMakeLists.txt
-rw-rw-r-- 1 jun jun 113 1月 17 20:11 Dockerfile
-rw-rw-r-- 1 jun jun 113 1月 17 20:11 index.html
drwxrwxr-x 2 jun jun 4096 1月 17 19:47 src
4.Dockerビルド
cd ~/wasm1stで移動して、
Dockerビルド
$ docker build -t aaa:000 .
$ docker images
aaa 000 d41496b35747 3 years ago 1.41GB
5.Docker起動
Docker起動
$ docker run --name bbb111 -it aaa:000 /bin/bash
6.OpenCVビルド
OpenCVビルド
$ cd /build
$ git clone https://github.com/opencv/opencv.git -b 4.5.0 --depth 1
$ git clone https://github.com/opencv/opencv_contrib.git -b 4.5.0 --depth 1
$ cd opencv
$ python3 ./platforms/js/build_js.py build_wasm --build_wasm --emscripten_dir=/emsdk/upstream/emscripten --config_only
### --cmake_option="-DOPENCV_EXTRA_MODULES_PATH=/build/opencv_contrib/"
$ export OPENCV_JS_WHITELIST=/build/opencv/platforms/js/opencv_js.config.py
$ cd build_wasm && emmake make -j && emmake make install
7.自作srcをビルド
自作srcをビルド
$ cd /build/app
$ mkdir dist && cd dist && mkdir build && cd build
$ emcmake cmake ../..
$ emmake make
出来た!!
/build/app配下のindex.htmlと、dist/build/cppmain.jsと、dist/build/cppmain.wasmを、どこかのサーバに置けば動くはず!!
Discussion