まとめ
OpenCV.jsを「DYNAMIC_EXECUTION=0」を指定してビルドすることで、Manifest V3でも利用できるOpenCV.jsを手に入れることができる。
解説
OpenCV.jsを手っ取り早く利用する方法
公式サイトによるとOpenCV.jsを利用する方法として公式サイトに用意されているファイルをダウンロードして利用するというのが案内されている。
Chrome拡張(Manifest V3)ではevalやnew Functionが禁止されている
しかしダウンロードしたファイルをChrome拡張で利用しようとするとエラーとなる。これはManifest V3でevalやnew Functionが禁止されているために発生する。
対応方法としてサンドボックス化されたiframeを用意するというのが案内されている。その中であればeval等も禁止されていない。
この方法で利用する方法はすでに記事になっている。
Emscriptenのオプションでevalとnew Functionを避けられる
Emscriptenのオプションを調べていくと「DYNAMIC_EXECUTION=0 」evalとnew Functionを利用しないようにするというそのものズバリなものが存在する。
一部の連携向けの機能やオプションが利用できなくなるというのもあるが、OpenCV.jsをちょこっと使う程度のことであれば恐らく問題ない。
OpenCV.jsのビルド環境を整える
OpenCV.jsのビルドはdockerでも提供されておりかなりお手軽に実行することができる。
おまけとして platforms/js/opencv_js.config.py を編集して必要なモジュールだけをビルドするようにすることができるので色々と削るとファイルサイズの削減につながる。
opencv_js.config.py
の編集を済ませたらあとはコマンド一発でビルドできる。以下は私がビルドした際のコマンドなので参考までに。
docker run --rm --workdir /src -v "$(get-location):/src" "emscripten/emsdk:2.0.10" emcmake python3 ./platforms/js/build_js.py build_js --disable_single_file --build_flags="-s DYNAMIC_EXECUTION=0 -s ENVIRONMENT=web -s EXPORT_ES6=1 -s MODULARIZE=1 -s FILESYSTEM=0 -s INCOMING_MODULE_JS_API=[] -s INVOKE_RUN=0"
OpenCV.jsを利用する
私の場合はChrome拡張をTypeScriptで実装しており、また動作オプションによってはOpenCV.jsを利用しない場合もあったので動的に読み込む必要があった。以下に参考となるコードを書いておく。
let cv: any;
export const initOpenCV = async (): Promise<void> => {
if (cv) return Promise.resolve();
const url = chrome.runtime.getURL("opencv_js.min.js");
const module = await import(/* @vite-ignore */ url);
cv = await module.default();
return Promise.resolve();
};
// 初期化
frame = new cv.Mat(canvas.height, canvas.width, cv.CV_8UC4);
fgmask = new cv.Mat(canvas.height, canvas.width, cv.CV_8UC1);
fgbg = new cv.BackgroundSubtractorMOG2(500, 16, true);
...
// 検出処理
const ctx = canvas.getContext("2d", { alpha: false, willReadFrequently: true })!;
const uint8ClampedArray = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
frame.data.set(uint8ClampedArray);
fgbg.apply(frame, fgmask);
cv.imshow(output, fgmask);
TypeScriptでの型情報をOpenCV.js関係のパッケージから拝借してくる手もあるがそこまで大規模な処理にならないなら割り切ってしまってもイイと個人的には思う。