M1 MacにOpenCVのwasm開発環境を作成する

どうやらOpenCVから離れては生きられないようです。


OpenCVのC++実装をどこかの言語環境に持っていけないか調べたのですが、基本WASMでは他の言語環境を組み合わせてのビルドはまだ難しいようでした。
RustとC++を混合するWASMの記事もありましたが、Hello Worldすら動かすのも困難なようです。

先ず手始めに、M1 Macのlima環境にamd64ビルド環境を作成したのですがとても遅いことが解りました。
ちなみに環境は emsdk v2.0.10 + OpenCV v4.7.0で動かせました。
どうやらOpenCVは最新のemsdk(v3.1.9)、恐らくですが3系ではビルドできないようです。

M1 Macでなんとか動かないか調べたところ、emsdk v2.0.21 でOpenCVのビルドができました。


## M1 Mac emsdk + OpenCV環境構築

$HOME/src 直下に配置する例になります。
ヘッダーがないと何かと不便なので`$ brew install opencv`はしておきましょう。

```bash
$ mkdir ~/src && cd ~/src
$ git clone https://github.com/emscripten-core/emsdk.git
$ cd emsdk
$ git checkout refs/tags/2.0.21
$ ./emsdk install latest
$ ./emsdk activate latest
$ source $HOME/src/emsdk/emsdk_env.sh

$ cd ~/src
$ curl -LO https://github.com/opencv/opencv/archive/refs/tags/4.7.0.zip
$ unzip 4.7.0.zip
$ cd opencv-4.7.0
$ python3 ./platforms/js/build_js.py --emscripten_dir=${EMSDK}/upstream/emscripten --build_wasm build_wasm
```

結局組み合わせ問題でした。先人の足跡を見つけるのが遅くて苦労しました。

## VSCodeビルド設定

`Cmd + Shift + B`でビルドして実行

```cpp:test.cpp
#include <stdio.h>

int main() {
    printf("Hello World\n");
    return 0;
}
```

```js:tasks.json
{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "run all tasks",
            "type": "shell",
            "command": "echo complete",
            "dependsOn": [
                "emscripten make",
                "emscripten run"
            ],
            "dependsOrder": "sequence",
            "problemMatcher": [],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        {
            "label": "emscripten run",
            "type": "shell",
            "command": "${HOME}/src/emsdk/node/14.15.5_64bit/bin/node test.js"
        },
        {
            "label": "emscripten make",
            "type": "shell",
            "command": "${HOME}/src/emsdk/upstream/emscripten/em++",
            "args": [
                "-g",
                "${workspaceFolder}/test.cpp",
                "-o",
                "test.js",
                "-L${HOME}/src/opencv-4.7.0/build_wasm/lib",
                "-lopencv_core",
                "-lopencv_imgproc",
                "${workspaceFolder}/src/*.cc",
                "-I${workspaceFolder}/include",
                "`pkg-config",
                "--cflags",
                "opencv4`"
            ],
        }
    ]
}
```


```js:c_cpp_properties.json
{
    "configurations": [
        {
            "name": "Mac",
            "includePath": [
                "${workspaceFolder}**",
                "/opt/homebrew/opt/opencv/include/opencv4",
                "include",
                "${HOME}/src/emsdk/upstream/emscripten/cache/sysroot/include"
            ],
            "defines": [],
            "macFrameworkPath": [
                "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks"
            ],
            "compilerPath": "/usr/bin/g++",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "macos-clang-arm64"
        }
    ],
    "version": 4
}
```

## 参考

### M1 Mac OpenCV v2.0.21

https://github.com/emscripten-core/emsdk/issues/671

### WASMで他の言語環境を組み合わせてのビルド

https://github.com/WebAssembly/wabt/issues/1563

https://github.com/AssemblyScript/assemblyscript/issues/741

### Wasm Fast Step

https://qiita.com/soramimi_jp/items/1b7ed0ddcefb0f4a7172

https://developer.mozilla.org/ja/docs/WebAssembly/existing_C_to_wasm


### 画像処理

https://zenn.dev/kounoike/articles/20201209-opencv-js-wasm

上の記事のコード

https://github.com/kounoike/webassembly-night-sample/tree/master/run-by-full-wasm

## Memory格納と表示

https://observablehq.com/@esperanc/webassembly-emscripten-with-arrays

```
function cArray(size) {
    var offset = Module._malloc(size * 8);
    Module.HEAPF64.set(new Float64Array(size), offset / 8);
    return {
        data: Module.HEAPF64.subarray(offset / 8, offset / 8 + size),
        offset: offset,
        size:size
    }
}

a = Object {
  data: Float64Array(10) [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
  offset: 5246648
  size: 10
}
```