動機
インカメラで人物や顔写真入り証明書を撮影したエビデンス画像に対して、顔部分のマスクを行うツールを作成したかった。
言語選定
画像処理ライブラリ opencvの対応言語は C/C++
Java
Python
です。
今回の動機であるツール的に用いるならば、Pythonが適していると思います。サンプルコードもたくさんあります。しかし、自分がPythonに対する知識が少なく、時間がかかりそうで逡巡していました。
しかし、opencv を Node.js 環境から使えるライブラリopencv4nodejsを見つけたので、試してみることにしました。
落とし穴
opencv4nodejsはopencvの型やメソッドと全く同じ名称ではなく、Javasciptの言語仕様に合わせて引数なども異なります。→ Contribution Guide
従って、Pythonのコードをそのまま置き換えればOK!といった形では実装できません。
自分はここを安易に考えていて若干ハマりました…
環境構築
Windows10(64bit)
Nodist(0.9.1) Node.js 11.13.0 上に
opencv4nodejs(5.5.0)をインストール
公式の手順はこちら
事前注意
- スペースを含むパスにインストールしない → node-gyp でパスが読めない
- 日本語を含むパスにインストールしない → opencvでパスが読めない
cmakeインストール
opencv4nodejs のインストール時に要求され、これが無いとエラーで進みません。
- cmakeをインストールする
- 実行ファイルがあるフォルダにPATHを通す 例:
G:\Program Files\CMake\bin
git
git ロングファイルネーム許可の設定(opencv4nodejs 内でのC++コンパイル用)git config --system core.longpaths true
npm
node-gypのインストール
npm install --global node-gyp
windows-build-tools インストール
npm install --global windows-build-tools
※時間かかりますopencv4nodejs のインストール
npm install opencv4nodejs
※とても時間かかります
実装
TypeScriptで書いてます
顔検出
サンプルソースをそのまま使いました。opencv4nodejsではサンプルソースが充実していてます。
import*ascvfrom'opencv4nodejs';exportconstfeceMaskBlur=(imagePath:string)=>{// 対象画像読込constimage=cv.imread(imagePath);if(!image){thrownewError(`No file ${imagePath}`);}constclassifier=newcv.CascadeClassifier(cv.HAAR_FRONTALFACE_ALT2);// detect facesconst{objects,numDetections}=classifier.detectMultiScale(image.bgrToGray());if(!objects.length){thrownewError(`No faces detected! ${imagePath}`);}console.log('顔検出した領域:',objects);console.log('確度:',numDetections);// draw detectionletblurImage:cv.Mat;blurImage=image.copy();objects.forEach((rect,i)=>{// 顔検出した部分に対してマスクを実施blurImage=drawBlurRect(blurImage,rect,numDetections[i]);});// file 保存cv.imwrite(imagePath,blurImage);}
検出した領域は、以下のような座標と大きさで取得できます
顔検出した領域: [ Rect { height: 525, width: 525, y: 1188, x: 1923 },
Rect { height: 262, width: 262, y: 3214, x: 2298 },
Rect { height: 584, width: 584, y: 878, x: 2714 } ]
確度: [ 8, 4, 11 ]
画像の一部上書き
上記で取得した領域に対して、マスクをかけます。
python だと、画像のデータは二次元配列として読み込まれるため、座標位置を指定して代入する形で記述できるようなのですが、opencv4nodejsだと形式が異なったため、ちょっと悩みました。
defmosaic_area(src,x,y,width,height,ratio=0.1):dst=src.copy()dst[y:y+height,x:x+width]=mosaic(dst[y:y+height,x:x+width],ratio)returndstdst_area=mosaic_area(src,100,50,100,150)cv2.imwrite('data/dst/opencv_mosaic_area.jpg',dst_area)
Python, OpenCVで画像にモザイク処理(全面、一部、顔など)より引用
issueを検索して見つけました。
// 対象のRectを塗りつぶしconstdrawBlurRect=(image:cv.Mat,rect:cv.Rect,numDetection:number):cv.Mat=>{// 領域の切り出しconstsrcRoi:cv.Mat=image.getRegion(rect);// 切り出した部分にマスクをかけるconstmasked=cv.blur(srcRoi,newcv.Size(rect.width,rect.height));// 切り出した部分を元画像に合成masked.copyTo(image.getRegion(rect));returnimage;}
実行
こうなります
画像取得元
まとめと気づき
画像から何かを識別する場合には識別器を用い、OpenCVにはこの識別に使う学習済みファイルが準備されています。
今回は、Haar Cascade識別器(分類器)のhaarcascade_frontalface_alt2.xml→ コード中では cv.HAAR_FRONTALFACE_ALT2
を使いました(サンプルそのまま)。
今回はインカメラを使った正面を向いた写真が基本なので顔検出がマッチしたのですが、横向きの顔は検出されにくい点については把握しておいた方がよさそうです。