カスケードファイルをOpenCVの機械学習ツールで作る。
opencv_traincascade.exeをコマンドプロンプトで使う。
適当に試してみても使えるカスケードファイルが出来ないので、ソースコードをデバッガで分析して機械学習の仕組みを学ぶ。
OpenCV 4.xではopencv_traincascadeのサポートが終了しているため、OpenCV 3.4のソースコードをダウンロードする必要がある。
Windows10でopencv_traincascadeをビルドする方法
特徴評価器
特徴評価器は、すべての絶対座標(x,y)に対して、幅dx、高さdyを1ピクセルずつ振って生成される。以下は特徴評価器生成の1例。
// haar_x2
if ( (x+dx*2 <= winSize.width) && (y+dy <= winSize.height) )
{
features.push_back( Feature( offset, false,
x, y, dx*2, dy, -1,
x+dx, y, dx , dy, +2 ) );
}
例えば、画像サイズ24x24、x=0, y=0, dx=5, dy=5、で生成される上記haar_x2の特徴評価器は、以下のようなイメージ。マイナスの領域の重みは小さく、プラスの領域の重みは大きく計算される。黒塗りの+1のエリアが-1のエリアに比べてより濃い画像ほど、この特徴評価器における重みづけが高くなる。
このような特徴評価器を、x, y, dx, dyそれぞれ網羅的に生成する。画像サイズ24x24でも16万2336個の特徴評価器が生成される。
正方形の画像の一辺の長さと、消費するメモリサイズは以下のとおりで、100x100程度のサイズで3GBくらいを消費する。搭載メモリを超える画像サイズ設定で実行すると、この初期化段階でPCが固まってしまう。
アルファベット画像サンプル
アルファベット画像で試してみる。
opencv_traincascade.exe -vec .\h.vec -data .\model -bg .\bgh.txt -w 24 -h 24 -numPos 20 -numNeg 25 -numStages 1
正解画像データ
不正解画像データ
結果は、以下の特徴評価器No.999が1つだけ適用された。
この特徴評価器による各サンプル画像の評価値を出力させたのが以下のプロット。-0.2213を境界とすれば、正解画像と不正解画像を100%識別できる。確かに、アルファベットの中で上中下の真ん中が一番濃い文字はHしかない。この特徴評価器1つだけで、用意したサンプル画像は識別できてしまう。
しかし、実際にこのカスケードファイルを使うとこうなる。
実際の画像認識では、映っているターゲットのサイズは可変という前提で探すので、真ん中が濃いというだけの条件では、無数の正解が検出されてしまう。
そこで、以下2枚の画像を不正解画像に追加してみた。
ところが、結果は変化せず。
調べると、追加した2枚は「正解画像」として誤判定されているが、計47枚のサンプル画像のうち2枚の誤判定率は7.4%で、最大False Alarm Rate(FA)のデフォルト値50%を余裕でクリアしてしまっている。いわゆる弱い特徴評価器を多数組み合わせることで、強い特徴評価器を作るという手法のため、個々の評価器の精度は要求されない様子。
+----+---------+---------+
| N | HR | FA |
+----+---------+---------+
| 1| 1|0.0740741|
+----+---------+---------+
ここでは実験のため、maxFalseAlarmRateオプションを5%に設定することで、誤判定を許さないようにする。
opencv_traincascaded.exe -vec .\h.vec -data .\model -bg .\bgh.txt -w 24 -h 24 -numPos 20 -numNeg 27 -numStages 1 -maxFalseAlarmRate 0.05
結果は、特徴評価器No.999に加えて、No.71343が追加された。
しかし、実際にこのカスケードファイルを使うとこうなる。
多少は改善したが、まだまだ誤判定だらけの状態。
さらに以下の画像を不正解画像に追加してみた。
結果は、特徴評価器No.999に加えて、No.11088、No.84378が追加された。No.84378は重みづけが逆で、黒い領域が濃い画像の場合は不正解と判定する。
しかし、実際にこのカスケードファイルを使うとこうなる。
むしろ悪化した。
小手先でサンプル画像を追加しながら改善するのは無理なことが分かったので、いろいろ工夫して不正解画像を増やしてみたが、リアルタイム検出での使用に耐えるカスケードファイルが全然出来ない。
いろいろ試す中で、既に不正解画像を100%識別できた後に不要なループに入ることが多かったので、関連するソースコードを確認した。
ステージが進めば進むほど、不正解画像が正解だと誤判定される可能性は下がり、どんどん不正解画像として使える率が低くなるため、実際に必要な不正解画像の数は、numNegとは比較にならないほど膨大となることが分かった。その数は数千万枚と、1枚ずつ用意できる数ではなく、そのため、opencv_traincscadeでは不正解画像がw,hよりも大きい画像であることを前提として、総当たりで切り取って使うようになっている。
これまでは、不正解画像もわざわざw,hサイズに合わせて作っていたため、使える不正解画像はnumNegより少し多い程度で、ステージごとに識別できる不正解画像を増やしていくという基本的なフローが出来ていなかったことになる。
不正解画像として、我が家のまとめ写真フォルダ(平均サイズ800x600)を丸ごと対象として、ほぼデフォルト設定のまま、
opencv_traincascaded.exe -vec .\h.vec -data .\model -bg .\bgh2.txt -w 24 -h 24 -numPos 20 -numNeg 590
で実行した結果。
できた。
結局、不正解画像のサイズは、設定サイズ(正解画像サイズ)と同じサイズではダメで、設定サイズよりも十分に大きい必要があった。気を使ってサイズを合わせた不正解画像をわざわざ作ったのが間違いだった。
作成されたcascade.xmlのDecision Treeは以下の通り。
STAGE1,2,4の2つ目の評価器あたりは感覚的に理解できないが、おそらく正解画像のHが1種類しかないので、形状(フォント)が少し違うだけで検出しないようになってしまっている。
TOP 顔認識 機械学習 Eigenfaces