Node.jsでもOpenCVしてみる
少し前のエントリではPythonを使用してOpenCVで顔認識をしていたんですが、モノによってはnode.jsでも同様なことができるかなと思いますので 実験をやってみました。
node.jsでもopencvのモジュールがあります。ただ、OpenCVは2系であることに注意です。
以前のエントリでRaspberryPiにインストールしたのは2系ですので問題なく動作します。(version 2.4.9)
opencvモジュールのインストール
node.jsのインストールに関しては
こちらで取り上げましたのでRaspbianのオリジナルのNode.jsではないことに注意です。npm
でopencvをインストールすればOKです。
$ npm install opencv - > opencv@6.0.0 install /home/pi/node_opencv/node_modules/opencv > node-pre-gyp install --fallback-to-build make: Entering directory '/home/pi/node_opencv/node_modules/opencv/build' CXX(target) Release/obj.target/opencv/src/init.o CXX(target) Release/obj.target/opencv/src/Matrix.o CXX(target) Release/obj.target/opencv/src/OpenCV.o CXX(target) Release/obj.target/opencv/src/CascadeClassifierWrap.o CXX(target) Release/obj.target/opencv/src/Contours.o CXX(target) Release/obj.target/opencv/src/Point.o CXX(target) Release/obj.target/opencv/src/VideoCaptureWrap.o CXX(target) Release/obj.target/opencv/src/CamShift.o CXX(target) Release/obj.target/opencv/src/HighGUI.o CXX(target) Release/obj.target/opencv/src/FaceRecognizer.o CXX(target) Release/obj.target/opencv/src/Features2d.o CXX(target) Release/obj.target/opencv/src/BackgroundSubtractor.o CXX(target) Release/obj.target/opencv/src/Constants.o CXX(target) Release/obj.target/opencv/src/Calib3D.o CXX(target) Release/obj.target/opencv/src/ImgProc.o CXX(target) Release/obj.target/opencv/src/Stereo.o CXX(target) Release/obj.target/opencv/src/LDAWrap.o SOLINK_MODULE(target) Release/obj.target/opencv.node COPY Release/opencv.node COPY /home/pi/node_opencv/node_modules/opencv/build/opencv/v6.0.0/Release/node-v46-linux-arm/opencv.node TOUCH Release/obj.target/action_after_build.stamp CXX(target) Release/obj.target/test_nativemat/test/nativemat.o SOLINK_MODULE(target) Release/obj.target/test_nativemat.node COPY Release/test_nativemat.node make: Leaving directory '/home/pi/node_opencv/node_modules/opencv/build' opencv@6.0.0 node_modules/opencv tqq buffers@0.1.1 tqq nan@2.6.1 mqq istanbul@0.4.5 (abbrev@1.0.9, async@1.5.2, wordwrap@1.0.0, nopt@3.0.6, esprima@2.7.3, once@1.4.0, supports-color@3.2.3, which@1.2.14, mkdirp@0.5.1, resolve@1.1.7, glob@5.0.15, js-yaml@3.8.3, escodegen@1.8.1, handlebars@4.0.6)
比較的長い時間がかかります。(RaspberryPi2では10分ちょっとかかっています) これが終わるといよいよOpenCVの世界に入れます。
静止画像の顔認識
まずは静止画に含まれる顔認識を行ってみます。
認識をする画像はinput.jpg
解析に成功した場合には、
顔と思われる部分に赤色の四角形を描画し、output.jpg
として画像を出力します。
若干書きぶりが異なりますが、なんとなくは同じ様です。oencvパッケージに含まれるサンプルを参考にしているので分類機の読み込みが異なるようです。
CascadeClassifier
やdetectMultiScale
での呼び出しではなく、detectObjectで引数での呼び出しとなっています。
【OpenCVサンプルフォルダ】 github.com
【face-detect.js】
var cv = require('opencv'); var RED = [0, 0, 255]; var THICKNESS = 2; cv.readImage("./input.jpg", function(err, im){ if (err) throw err; if (im.width() < 1 || im.height() < 1) throw new Error('error:画像が不正の様です。'); im.detectObject("./haarcascade_frontalface_alt.xml", {}, function(err, faces){ for (var i=0;i<faces.length; i++){ var face = faces[i] im.rectangle([face.x, face.y], [face.width, face.height], RED, THICKNESS); } console.log('イメージを保存しました'); im.save('./output.jpg'); }); })
実行のためには、あらかじめプログラムと同じディレクトリにhaarcascade_frontalface_alt.xml
など分類機設定のファイルがないとエラーになりますので注意してください。
実行させると…
$ node.js face-detect.js
【input画像】 ライセンス: CC0 Public DomainCreative Commons — CC0 1.0 Universal
【output画像】
Webカメラの画像の取り込み
カメラ画像の取り込みに関しては基本的にpythonと違いはありません。 OpenCVでGUIを使えますが、node.js経由でも同様にGUIを使用することができます。
【camera.js】
var cv = require('opencv'); try { var camera = new cv.VideoCapture(0); var window = new cv.NamedWindow('Video', 0) setInterval(function() { camera.read(function(err, im) { if (err) throw err; console.log(im.size()) if (im.size()[0] > 0 && im.size()[1] > 0){ window.show(im); } window.blockingWaitKey(0, 50); }); }, 20); } catch (e){ console.log("Couldn't start camera:", e) }
実行させると…
$ node.js camera.js
【動作画像】
多少のアラームは発生するのですが問題なく動作しました。使用したWebカメラはMicrosoftのLifeCamになります。(以前の動作までに少し時間がかかる挙動に関係するのだと思います)あとpythonからの呼び出しとは異なってデフォルト解像度は320*240ぽいですね。
Webカメラで顔認識を行う
あとはこれら2つを組み合わせていきます。
【face-detect-cam.js】
var cv = require('opencv'); var RED = [0, 0, 255]; var THICKNESS = 2; try { var camera = new cv.VideoCapture(0); var window = new cv.NamedWindow('Video', 0); camera.setWidth(480); camera.setHeight(320); setInterval(function() { camera.read(function(err, im) { if (err) throw err; if (im.size()[0] > 0 && im.size()[1] > 0){ im.detectObject("./haarcascade_frontalface_alt.xml", {}, function(err, faces){ for (var i=0;i<faces.length; i++){ var face = faces[i] im.rectangle([face.x, face.y], [face.width, face.height], RED, THICKNESS); } window.show(im); }); window.blockingWaitKey(0, 50); } }); }, 500); } catch (e){ console.log("Couldn't start camera:", e) }
実行させると…
$ node.js face-detect-cam.js
【動作画像】
このコードでは動作に関してはかなり遅いと思います。遅延が3秒ほどありそうでした。
今回はsetInterval()
を使って周期処理を行っているのですが、直感的には
while
でループを回せばいいのかなと思ったんですが、うまくいきません。
javascriptにsleep()
とかwait()
が無いことに起因してるかなと思います。
あと、setInterval()
の周期のパラメータを小さくすると結構な確率でSegmentation fault
が発生するので500msec以上にしたほうがいいかなと思います。
終わりに
一応、node.jsでOpenCVを使ったプログラムを組んでみました。今後そういうこともあるかなとは思います。RaspberryPi2では少しパワー不足という印象がありますが、ホストが一般的なPCならもっといいパフォーマンスをだせるのかなと思います。
これが限界なのかなと色々おもっていたのですが、Xを使ってリモートで接続していることに気が付きました。
ダイレクトにモニタに接続するとsetInterval()
の周期のパラメータを250程度まで小さくしてもSegmentation fault
もほとんど発生せず、遅延も2秒程度まで追い込めました。
ちなみにファイルに一度保存したほうが早いのではないかと思って、以下のコードを書いてみましたが、全然早くはならず、逆に処理が追いつかずSegmentation fault
の確率が上がったように感じます。こちらはディスプレイの直接接続でもSegmentation fault
が発生するようでした。
var cv = require('opencv'); var RED = [0, 0, 255]; var THICKNESS = 2; try { var camera = new cv.VideoCapture(0); var window = new cv.NamedWindow('Video', 0); camera.setWidth(480); camera.setHeight(320); setInterval(function() { camera.read(function(err, im) { if (err) throw err; if (im.size()[0] > 0 && im.size()[1] > 0){ im.detectObject("./haarcascade_frontalface_alt.xml", {}, function(err, faces){ for (var i=0;i<faces.length; i++){ var face = faces[i] im.rectangle([face.x, face.y], [face.width, face.height], RED, THICKNESS); } im.save('./tmp.jpg'); }); cv.readImage("./tmp.jpg", function(err2, im2){ if (err2) throw err2; if (im2.size()[0] > 0 && im2.size()[1] > 0){ window.show(im2); } }); window.blockingWaitKey(0, 50); } }); }, 500); } catch (e){ console.log("Couldn't start camera:", e) }