[環境構築]React+TypeScriptでWebAssembly004。WindowsでC++開発->Ubuntuでwasmビルド。
<- React+TypeScriptでWebAssembly003
React+TypeScriptでWebAssembly005 ->
Abstract
開発の主体はVMを使わないWindowsで、WebAssemblyビルドの時にVMのUbuntuを使う開発環境を構築してみた。
ホントは、Windowsだけで完結したかったとけど、OpenCVのWebAssemblyビルドができんかった~。
Windows編
$ opencv_version
4.9.0-dev
$ cmake --version
cmake version 3.28.1
$ emcc
emccは未インストール
結論
今回の成果物はココ↓
前提
- gitインストール済。[環境構築]Windowsに、Gitインストール。
- pythonインストール済。Windows版Pythonのインストール
- CMakeインストール済。バージョン問わず。CMake 3.12.0 インストール手順
- VisualStudio2022インストール済。Windows での Visual Studio 2022 Community のインストール方法
- VSCodeインストール済。Windows への Visual Studio Code のインストール方法
VSCodeには、以下の拡張機能を入れておく。- C/C++
- CMake Tools
- 日本語化もついでに。
- Webカメラを準備。(内臓カメラを使うならそれでもOK)
Windows側のC++ビルド環境構築
Windowsで。
OpenCVインストール
1.OpenCVのConfigure実行
- opencvインストールフォルダ作成
$ mkdir d:\apps\opencv && cd d:\apps\opencv
- opencvとopencv_contribをダウンロード
$ curl -Lo opencv4.zip https://github.com/opencv/opencv/archive/4.x.zip
$ curl -Lo opencv_contrib4.zip https://github.com/opencv/opencv_contrib/archive/4.x.zip
- opencv4.zip,opencv_contrib4.zipを解凍。
$ dir D:\apps\opencv
~略~
2024/01/19 22:53 <DIR> opencv-4.x
2024/01/20 18:18 97,473,020 opencv4.zip
2024/01/20 05:13 <DIR> opencv_contrib-4.x
2024/01/20 18:19 62,105,890 opencv_contrib4.zip
~略~
- CMake GUI起動
(ココ見とくと幸せになれます。)
Where is the source code: D:\apps\opencv\opencv-4.x
Where to build the binaries: D:\apps\opencv\opencv-4.x\build
→ Configure押下
Optilnal platform for generator: Win32
Finish押下
↓
↓
この中から、"OPENCV_EXTRA_MODULE_PATH"を頑張って探して設定。
OPENCV_EXTRA_MODULE_PATH : D:\apps\opencv\opencv_contrib-4.x\modules
// "BUILD_opencv_world"を頑張って探して設定。
// BUILD_opencv_world: レ点
→ Configureをも一回押下
Configuring done
↓
Generate押下
↓
Generating done
↓
閉じる。
2. OpenCVをVisual Studio 2022でビルド
OpenCV.sln をダブルクリックで開く。
↓
OpenCVの中をデバッグするつもりはなので、ReleaseとWin32を選んで、ALL_BUILDを選ぶ。
↓
ビルド。15分ぐらいかな~。
ビルドエラーなし。OpenCVすばらしい~。
↓
INSTALL -> ビルドを選ぶ。
↓
ビルド。
ビルドエラーなし。さすがOpenCV。
↓
ビルド完了。閉じる。
3. 環境変数Pathに設定。
以下のパスを環境変数Pathに追加する。
- D:\apps\opencv\opencv-4.x\build\install
- D:\apps\opencv\opencv-4.x\build\install\include
- D:\apps\opencv\opencv-4.x\build\install\x86\vc17\bin
※D:\apps\opencv\opencv-4.xの部分は自身の環境に読み替えてね。
4. 再起動
環境変数をいじったのでシステムに反映させるために再起動する。
5. プロジェクトフォルダとソース一式を作成
- プロジェクトフォルダ作成
mkdir cppwasm_template && cd cppwasm_template
- cppフォルダ作成
mkdir cpp && cd cpp
- CMakeLists.txtを作成
cmake_minimum_required(VERSION 3.5)
project(OpenCVExample)
set(CMAKE_CXX_STANDARD 17)
# OpenCVのパッケージを見つける
find_package(OpenCV REQUIRED)
# #OpenCV関係のインクルードディレクトリのパスを設定
# include_directories( ${OpenCV_INCLUDE_DIRS} )
# ソースファイルを指定
set(SOURCES ../src/ifcpp.cpp ../src/MainProcess.cpp)
# 実行可能ファイルをビルド
add_executable(OpenCVExample ${SOURCES})
message('----------------s')
message(${SOURCES})
message(${OpenCV_LIBS})
message('----------------e')
# OpenCVをリンク
target_link_libraries(OpenCVExample ${OpenCV_LIBS})
- ifcpp.cppを作成
ifcpp.cppは、Windowsでのみ動作するコードを実装する。
ifcpp.cppから MainProcess.cpを呼び出す構成 |
#include <opencv2/opencv.hpp>
#include "../src/MainProcess.h"
int main(int argc, char *argv[]) {
/* 画像の読み込み */
cv::Mat inputImage = cv::imread("../../input.jpg");
/* 画像が正しく読み込まれたかを確認 */
if (inputImage.empty()) {
std::cerr << "画像を読み込めませんでした" << std::endl;
return -1;
}
/* 画像処理 */
cv::Mat grayscaleImage;
ConvertColor(inputImage, grayscaleImage);
/* 処理画像を表示 */
cv::imshow("Grayscale Image", grayscaleImage);
/* キー入力待ち */
cv::waitKey(0);
/* ウィンドウを閉じる */
cv::destroyAllWindows();
return 0;
}
cd ..
mkdir src && cd src
- MainProcess.hを作成
#include <opencv2/opencv.hpp>
void ConvertColor(const cv::Mat &inmat, cv::Mat &outmat);
- MainProcess.cppを作成
メイン処理をMainProcess.cppに書き、ifcpp.cpp/ifwasm.cppの両方から呼ばれる様に構成する。
MainProcess.cppはifcpp.cpp/ifwasm.cppの両方から呼ばれる。 メイン処理をMainProcess.cppに書く。 |
#include <opencv2/opencv.hpp>
void ConvertColor(const cv::Mat &inmat, cv::Mat &outmat) {
cv::cvtColor(inmat, outmat, cv::COLOR_BGR2GRAY);
return;
}
cppwasm_template
├ cpp
| ├ CMakeLists.txt # windowsC++開発用のCMakeLists.txt,VSCodeで開く
| ├ input.jpg # お試し画像
| └ ifcpp.cpp # windows側で動くコード。MainProcess.cppのコードを呼ぶ。
└ src
├ MainProcess.cpp # メイン処理。windows/weasmの両方から呼ばれる。
└ MainProcess.h # メイン処理のヘッダ。
6. VSCodeでプロジェクトフォルダを開く
VSCodeの拡張機能C/C++、CMake Toolsを入れてなければ入れておく。
↓
- C/C++: Edit Configurations(JSON)を選択
Ctrl + Shift + P -> "C/C++: Edi"まで入力と出てくる。
↓
c_cpp_properties.jsonが生成される。ついでに右下にビルド/デバッグ/実行ボタンも表示される。
7. CMakeの設定
Ctrl + Shift + P -> "CMake: Configure"を選択。
X86を選ぶ。(WebAssemblyは32bitなので。)
8. インクルードパスを追加
ifcpp.cppに戻ると、赤波線でエラーになっている。
↓
手順3でビルドしたパスを設定。
D:\apps\opencv\opencv-4.x\build\install\include
↓
赤波線が消えている。
9. 実行
ブレークポイントを設定して、カブト虫ボタンを押す。
↓
いろいろ動き出す。
↓
ちょっと待つと、ブレークポイントで止まる。
↓
再開。
出来た!!
Ubuntu編
windows側で開発したコードをwasmビルドして、ブラウザで動作確認する。
$ opencv_version
4.9.0
$ cmake --version
cmake version 3.28.20240119-g62ca955
$ emcc(=WebAssembly) --version
emcc ~略~ 3.1.52
前提
- VirtualBoxインストール済。 VirtualBoxインストール
- VirtualBoxにUbuntu22.04インストール済。VirtualBox上でUbuntu22.04を構築
- Ubuntuにgitインストール済。
sudo apt install -y git
- Ubuntuにcmakeインストール済。
$ git clone https://github.com/Kitware/CMake.git
$ cd CMake && ./bootstrap && make && sudo make install
Ubuntu側のwasmビルド
1.Emscripten のインストール
C++をEmscriptenにビルドするのにEmscriptenが必要。なのでインストールする。
参考 : Emscripten Download and install
cd ~
git clone https://github.com/emscripten-core/emsdk.git # gitから取得
cd emsdk # emsdk移動
git pull # 最新化
./emsdk install latest # インストール
./emsdk activate latest # make
# source ./emsdk_env.sh # 環境変数設定
echo 'source "/home/jun/emsdk/emsdk_env.sh"' >> $HOME/.bash_profile
# 環境変数設定の永続化
一旦、ログアウト -> ログイン
2.OpenCVビルド
$ mkdir ~/tmp-opencv && cd ~/tmp-opencv
$ mkdir build && cd build
$ git clone https://github.com/opencv/opencv.git --depth 1
### git clone https://github.com/opencv/opencv_contrib.git --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=~/tmp-opencv/build/opencv/platforms/js/opencv_js.config.py
$ cd build_wasm
$ emmake make -j
$ emmake sudo make install
# ↑↑↑ sudo付けんと file cannot create directory:~のエラーになるけん。
3.テンプレートのソースコード一式を取得
$ cd ~
$ git clone https://github.com/aaaa1597/cppwasm_template.git
$ cd cppwasm_template/wasm
- ifwasm.cppを作成
ifwasm.cppは、WebAssembly依存部分を実装する。
JavaScriptからカメラ画像を受け取って、MainProcess.cppに渡す役目とか。
ログをconsole.logに出力する機能とか。
WebAssemblyとしてビルドするのはこっちの構成になる。
ifwasm.cppから MainProcess.cpを呼び出す構成 |
$ cd ~/cppwasm_template/
$ mkdir wasm
$ cd ~/cppwasm_template/wasm
#include <stdlib.h>
#include <SDL/SDL.h>
#include <emscripten.h>
#include <emscripten/bind.h>
#include <opencv2/opencv.hpp>
#include <opencv2/opencv.hpp>
#include "../src/MainProcess.h"
namespace {
constexpr int WIDTH = 640;
constexpr int HEIGHT = 480;
SDL_Surface *screen = nullptr;
} // namespace
#define LOG_OUTPUT 0
#if LOG_OUTPUT
EM_JS(int, console_log, (const char *logstr), {
console.log('aaaaa ' + UTF8ToString(logstr));
return 0;
});
#else
#define console_log(logstr)
#endif
extern "C" int main(int argc, char **argv) {
console_log(__PRETTY_FUNCTION__);
SDL_Init(SDL_INIT_VIDEO);
screen = SDL_SetVideoMode(WIDTH, HEIGHT, 32, SDL_SWSURFACE);
return 0;
}
extern "C" {
size_t EMSCRIPTEN_KEEPALIVE creata_buffer(int size) {
console_log(__PRETTY_FUNCTION__);
return (size_t)malloc(size * sizeof(uint8_t));
}
void EMSCRIPTEN_KEEPALIVE destroy_buffer(size_t p) {
console_log(__PRETTY_FUNCTION__);
void *pbuf = (void*)p;
free(pbuf);
}
void EMSCRIPTEN_KEEPALIVE Convert(size_t addr, int width, int height, int cnt) {
console_log(__PRETTY_FUNCTION__);
auto data = reinterpret_cast<void *>(addr);
cv::Mat rgbaMat(height, width, CV_8UC4, data);
cv::Mat rgbMat;
cv::Mat rgbOutMat;
cv::Mat outMat;
ConvertColor(rgbaMat, rgbMat, cv::COLOR_RGBA2RGB);
rgbMat.convertTo(rgbOutMat, -1, 1.0, cnt - 100.0);
ConvertColor(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);
}
} /* extern "C" */
EMSCRIPTEN_BINDINGS(my_module) {
emscripten::function("convert", &Convert);
emscripten::function("creatabuffer", &creata_buffer);
emscripten::function("destroybuffer", &destroy_buffer);
}
4.ソースコードをビルド
$ cd ~/cppwasm_template/wasm
$ mkdir build && cd build
$ emcmake cmake ..
$ emmake make
$ mv cppmain.js ..
$ mv cppmain.wasm ..
$ ls -l ~/cppwasm_template/wasm
-rw-rw-r-- 1 jun jun 618 1月 20 14:29 CMakeLists.txt
drwxrwxr-x 3 jun jun 4096 1月 20 15:52 build
-rw-rw-r-- 1 jun jun 182218 1月 20 15:52 cppmain.js # ← これが出来た!!
-rwxrwxr-x 1 jun jun 587651 1月 20 15:52 cppmain.wasm # ← これが出来た!!
-rw-rw-r-- 1 jun jun 1936 1月 20 15:13 index.html
index.htmlと、出力されたcppmain.js、cppmain.wasmを、どこかのサーバに置けば動く!!
で、動かす。
5.Webカメラの設定(内蔵カメラならこの手順は不要)
- Webカメラ挿して。
- Windowsの設定 Windowsで設定
- Virtual Box設定 Virtual Box->デバイス->Webカメラ->HD Web Cameraにチェック
6.サーバ起動
$ cd ~/cppwasm_template/wasm
$ python3 -m http.server 8080
7.サーバにアクセス。
ブラウザから http://localhost:8080/ にアクセスする。
出来た!!
これで、Ubuntu側のwasmビルドも出来た!!
Windowsで開発して、Ubuntuでwasmビルドと動作確認の環境が出来た!!
ふぃ~。お疲れ様でした。
ハマったな~。
<- React+TypeScriptでWebAssembly003
React+TypeScriptでWebAssembly005 ->
Discussion