non vorrei lavorare

ブログ名の通りです。javascript three.js mruby rust OCaml golang julialang blender

公式の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

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

関連記事