画像の中から人の顔が写っている場所を自動的に判定する 顔検出 ってやつをやってみようと思います。
そのために OpenCV という有名なライブラリを使用します。OpenCV 自体は様々な言語と組み合わせて使うことが出来るのですが、今回は自分が書き慣れている Python でいきます。
幸いに Python は OpenCV が公式でサポートしている言語(C++, Python, Java)の一つですしね。
評価器を用意する
顔検出をするためには「画像のこの部分は、人の顔である/人の顔ではない」という判定をする 評価器 という部品が必要です。
この評価器は通常、事前に用意した何千枚というテスト画像をプログラムに読ませ、学習させて(機械学習というやつですね)作らなければいけません。
この機械学習で評価器を用意するという工程にとてつもない労力が掛かります。
人の顔が写ったたくさんの画像を用意して、画像の中の顔の部分を指定するなどの下準備が必要になるからです。
ところが、ありがたいことに OpenCV には標準で多くの種類の評価器が付属しています。
今回は OpenCV 標準の評価器を GitHub から落としてきて使おうと思います。
顔以外の評価器もあるようですが、今回使うのは以下の7つです。
- haarcascade_frontalcatface.xml
- haarcascade_frontalcatface_extended.xml
- haarcascade_frontalface_alt.xml
- haarcascade_frontalface_alt_tree.xml
- haarcascade_frontalface_alt2.xml
- haarcascade_frontalface_default.xml
- haarcascade_profileface.xml
6つは正面顔(front face)用、1つは横顔(profile face)用です。
基本的な顔検出
シンプルに顔検出して顔の部分を枠で囲むだけならば、非常に少ないコード量で可能です。
import cv2 # 画像の読み込み image = cv2.imread("img/src/01.jpg") # 処理速度を高めるために画像をグレースケールに変換 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 評価器を読み込み cascade = cv2.CascadeClassifier("opencv/data/haarcascades/haarcascade_frontalcatface.xml") # 顔検出 facerect = cascade.detectMultiScale( gray, scaleFactor=1.11, minNeighbors=3, minSize=(30, 30) ) if 0 != len(facerect): BORDER_COLOR = (255, 255, 255) # 線色を白に for rect in facerect: # 顔検出した部分に枠を描画 cv2.rectangle( image, tuple(rect[0:2]), tuple(rect[0:2] + rect[2:4]), BORDER_COLOR, thickness=2 ) # 結果の画像を保存 cv2.imwrite("img/dest/detected.jpg", image)
import cv2
で読み込んでいるモジュールが OpenCV です。
簡単ですね。
精度を高める
顔検出が簡単に行えるのは分かりました。問題は精度です。
顔検出の間違いには2つのパターンがあります。
- 第一の過誤 - 顔ではない対象を顔であると誤って検出する
- 第二の過誤 - 顔である対象を顔であると正しく検出できない
今回は顔検出の際のパラメーターをいろいろに変えつつ、7つの評価器を比べてみます。
と言いつつ、比べてみるパラメーターは minNeighbors
だけです。
minNeighbors
は信頼度に関するパラメーターで、値を大きくすると第一の過誤のリスクを抑える代わりに第二の過誤のリスクが増します。つまり、何でもない部分を顔だと勘違いしづらくなる代わりに人の顔の部分も見落としやすくなるわけです。
今回は minNeighbors
を 3, 10, 20 と変えてそれぞれの評価器の精度を比べてみます。
評価器毎の精度を比較する
信頼度=3 のとき
というわけでこちらをご覧ください。
minNeighbors=3
で各評価器を使って顔検出を走らせた結果です。
ものによっては枠が30個以上描かれて大変なことになってる画像もありますね。これは人の顔ではない対象を誤検出してしまっている状態です。
各評価器の精度を数字で比べましょう。
まずは第一の過誤の割合から。
全部で12枚の画像があり、1枚あたり2.4個の顔が写っています。精度の悪い評価器では1枚あたり19.6カ所もの誤検出があるようです。
その中で frontalface_alt2 と profileface の精度が良いですね。frontalface_alt2 は1枚あたりの誤検出が0.2カ所、非常に優秀です。
第二の過誤についても比べてみましょう。
はい、人の顔を検出できなかった割合は、全て横並びで7%という結果になりました。
とは言っても、さきほどの結果の画像を並べたページを見てもらえれば分かるとおり評価器によっては手当たり次第に枠がついている状態なので、まさに下手な鉄砲も数打ちゃ当たるの結果ですね。
信頼度のパラメーターをいじってもう少し比べてみましょう。
信頼度=10のとき
minNeighbors=10
で試して見ます。誤検出は減り、検出できない顔が増えるはずです。
第一の過誤
さすがに誤検出は1枚あたり7カ所ほどに減りましたね。
そして先ほど優秀だった frontalface_alt2, profileface がイマイチになり、今度は frontalface_default が優秀です。はて。
第二の過誤
続いて見落とし率ですが、信頼度を上げ慎重になった代わりに見落としが増えています。
軒並み4,5割は見落としてますね。これはいただけない。
信頼度=20のとき
もう少し試してみましょう minNeighbors=20
です。
第一の過誤
再び frontalface_alt2 が優秀です。驚異の誤検出0%。
第二の過誤
5割以上の顔を見落とすようになってしまいました。これ以上 信頼度を上げていっても期待はできないですね。
まとめ
というわけで比較の結果、評価器は frontalface_alt2 を使い minNeighbors=3
の設定で顔検出するのが最も精度を高めることができました。
この設定では人の顔が映った個所を見落とす確率は7%、そして1枚あたり約0.2カ所の誤検出があります。
今回特に検出しづらかったのは、この方のお顔です。
横顔ですし天地も逆になってますからねぇ。このお方の顔を検出するには画像を回転させながら評価器にかけるなどの工夫が必要そうです。
今回の比較のためのコード一式は下記のリポジトリに置いています。
また、その他のパラメーターによって顔検出の制度がどのように変わるかについても興味深いところですが、それについてはMakoto Koikeさんが書かれた下記の記事でよく研究されていましたので、興味のある方はそちらを参考にしてみてください。
http://workpiles.com/2015/04/opencv-detectmultiscale-scalefactor/
http://workpiles.com/2015/04/opencv-detectmultiscale-minneighbors/
私からは以上です。