non vorrei lavorare

昔はおもにプログラミングやガジェット系、今は?

公式のOpenCVでWASM版OpenCV.jsがビルドできたのでnode.jsで動かそうとした件

おはようございます。先週なんとなく、熱っぽいことがあった次男ですが、 諸事情により、実家に来てから、症状が悪化して、39度を超える発熱、想定外の事態です。 幸い、高熱が続くことは無く、翌朝は平熱に戻ったので、インフルエンザではなさそう@kjunichiです。

背景

CycleGANで息子たちの顔を入れ替えるやつを学習させようと、まずは子供たちの顔を切出そうと

abrakatabura.hatenablog.com

を読み直して、切り取ったものの、ずいぶん精度が悪かったし、おそかった。。

ここで利用している顔検出処理の実装はPureJSで、かつ、だいぶ今となっては古いので、 OpenCV3.0以降はEmscriptenでビルド出来るようになるというかすかな記憶があり、 ちょっと前はEmscripteのIncomming版でないとWASMできなかったのだが、 最近はlatestで対応されていることを知った。

そんな訳で、顔検出をWASM化したOpenCV.jsでやったら、精度や速度が改善されるのでは?と思い 始めたが、そもそも、OpenCV.jsをNode.jsから動かすのに躓いた。

OpenCVの公式にWASMでビルドする方法があった

ということで、git cloenしてEmscriptenで簡単にWASMでOpenCVがビルドできた

で、どうやってつかうの?

公式には、nodeコマンドでテストもできるよ!って書いてあり、 パッと見、

cv = require('./opencv.js');

とtest_mat.jsなどに記述があり、 楽勝!

と思いきや、テストはパスするも

cv = require('./opencv.js');

let mat = new cv.Mat();

なんて書いても、cv.Matにコンストラクターは未定義だ!のエラー しか出てこない。。

/Volumes/EXTHDD3/local/opencv/build_wasm/bin/opencv.js:21
var Module;if(!Module)Module=(typeof cv!=="undefined"?cv:null)||{};var moduleOverrides={};for(var key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;if(Module["ENVIRONMENT"]){if(Module["ENVIRONMENT"]==="WEB"){ENVIRONMENT_IS_WEB=true}else if(Module["ENVIRONMENT"]==="WORKER"){ENVIRONMENT_IS_WORKER=true}else if(Module["ENVIRONMENT"]==="NODE"){ENVIRONMENT_IS_NODE=true}else if(Module["ENVIRONMENT"]==="SHELL"){ENVIRONMENT_IS_SHELL=true}else{throw new Error("The provided Module['ENVIRONMENT'] value is not valid. It must be one of: WEB|WORKER|NODE|SHELL.")}}else{ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof require==="function"&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;ENVIRONMENT_IS_SHELL=!ENVIRONMEN

TypeError: cv.Mat is not a constructor
    at Object.<anonymous> (/Volumes/EXTHDD3/local/opencv/build_wasm/bin/dame.js:3:11)
    at Module._compile (module.js:641:30)
    at Object.Module._extensions..js (module.js:652:10)
    at Module.load (module.js:560:32)
    at tryModuleLoad (module.js:503:12)
    at Function.Module._load (module.js:495:3)
    at Function.Module.runMain (module.js:682:10)
    at startup (bootstrap_node.js:191:16)
    at bootstrap_node.js:613:3

ひょっとして非同期なのでは?

slコマンドをEmscripteで動かした経験もあるので、Emscriptenは コードの読み込みは非同期だったことを思い出す。

cv = require('./opencv.js');
setTimeout(()=>{
(()=>{
let mat = new cv.Mat();
})()

},8000);

やった!

今度は動いた。

しかし、全然スマートじゃない!

改善版

var cv = require("./opencv.js");
cv['onRuntimeInitialized']=()=>{
          console.log(`cv.CV_8UC3 = ${cv.CV_8UC3}`);
          let mat = new cv.Mat(10, 20, cv.CV_8UC3);
          console.log(mat);
};

まとめ

Emscriptenではmain関数が呼ばれる準備が完了したら、 ModuleのonRuntimeInitializedに登録したコールバックを読んでくれる

cause opencv_js.js need time to load

なんで、opencvのテストは成功したんだろと軽く再度テストコードのtests.jsを眺めたら、

let testrunner = require('node-qunit');
testrunner.options.maxBlockDuration = 20000; // cause opencv_js.js need time to load

と、なんらかのウェイトを入れてテストを実行している模様。

関連記事