Python + OpenCVで斜めの顔を顔認識をしてみた時の備忘録

何番煎じの話だって幹事だけど、とにかくしてみたくなったので、Python + OpenCVで人の顔認識をしてみた。
環境はいつものubuntu 15.10

深く考えずにPython用OpenCVをインストール

$ sudo apt-get install python-opencv

顔認識したい画像を探してくる

Yahoo!画像検索とかGoogle画像検索とか、好きなところからとってくるといいよ。
lenna姉さんでもいいんだけど、それじゃしおもしろくないから、「美少女」で一番上にきた橋本かんなさんの画像にした。
kanna
美人やねー。

深く考えずにスクリプトを書く

こんな感じでさらさらっとスクリプトを書く。
pythonでopencvするのは初めてだったけど、opencvSharpで少し触った経験があったので、その経験+引用文献で何となくかけた。
あと下の参考URLは本当に参考になるURLなのでぜひ読んでね。

# encoding:utf-8
import numpy as np
import cv2 

#画像ロード。3なら(0以上ならすべて)フルカラー。0ならグレースケール
img = cv2.imread('filename.jpg', 3)
#がぞうをグレースケール化
gray = cv2.cvtColor(img, cv2.cv.CV_BGR2GRAY)

#カスケードファイルロード。大抵はpythonのライブラリ群に入ってる。
#見つからなければここからDLしてくる。https://github.com/opencv/opencv/tree/master/data/haarcascades
cascade = cv2.CascadeClassifier('./haarcascades/haarcascade_frontalface_alt.xml')
#カスケードデータと類似する領域(今回は顔領域)探し
facerects = cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=1, minSize=(1, 1))
if len(facerects) > 0:
    for rect in facerects:
        cv2.rectangle(img, tuple(rect[0:2]), tuple(rect[0:2]+rect[2:4]), (0, 0, 255), thickness=2)
else:
    print "顔を認識できませんでした"

cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

顔認識出来た

実際に動かしてみるとこんな感じになる
screenshot-from-2016-09-20-15-49-15

haar分類器で簡単に顔認識が出来た。
これを応用すれば、例えば「社員証ようにアップロードされた社員の写真で、顔領域がW x Hピクセルなかったら撮り直しするよう指示する」とかできるアプリケーションとか機能が作れそう。

しかし問題もある

先ほどは割と顔がきれいに正面で写っているような画像を用いていたので、何の問題もなく顔認識はできていた。
じゃあ逆にちょっと意地悪な問題で、Perfect Humanのあっちゃんの画像を用いて顔認識させてみるとどうなるか?

I’m a Perfect Human.
ph

顔認識させてみた結果
screenshot-from-2016-09-20-16-17-08

ご覧のとおり顔を認識出来なかった。
これは、先ほど申し上げたhaar分類器の特性によるものらしい。
haar分類器で顔のデータとして与えられている基準となる画像は正面向きのものが多く、ななめになっていたりすると顔として認識し辛くなる。(要出典)
では、このPerfect Humanの画像で顔認識するのは諦めるしかないのだろうか?実は裏技がある。

画像を斜めにしてみる

顔が斜めに写っていて、顔認識ができない」というのであれば、「画像を傾けて、顔が正面に対してまっすぐになるようにすればよい」という発想の転換があるらしい。
実際にやってみた。

# encoding:utf-8
import numpy as np
import cv2

src_img = cv2.imread('ph.jpg', 3)

center = tuple(np.array([src_img.shape[1]*0.5, src_img.shape[0]*0.5]))
size = tuple(np.array([src_img.shape[1], src_img.shape[0]]))

cascade = cv2.CascadeClassifier('./haarcascades/haarcascade_frontalface_alt.xml')
for i in range(0, 360):
    #回転行列の定義
    rot_matrix = cv2.getRotationMatrix2D(center, i, 1.0)
    #アフィン変換を適応し、回転した画像を取得
    rot_img = cv2.warpAffine(src_img, rot_matrix, size)
    #回転した画像をグレースケール化
    gray = cv2.cvtColor(rot_img, cv2.cv.CV_BGR2GRAY)

    facerects = cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=1, minSize=(1, 1))
    if len(facerects) > 0:
        print("%d度の回転で顔を認識しました" % i)
        for rect in facerects:
            cv2.rectangle(rot_img, tuple(rect[0:2]), tuple(rect[0:2]+rect[2:4]), (0, 0, 255), thickness=2)
        cv2.imshow('image',rot_img)

        #ウィンドウでESCキーが押されたらプログラム自体を終了
        k = cv2.waitKey(0)
        if k == 27: #ESC key
            cv2.destroyAllWindows()
            exit()
        else:
            cv2.destroyAllWindows()

上記は画像を1度ずつ回転させてみて、回転した画像(rot_img)で顔認識を試み、成功したらwindowに表示するというものである。
これを実行してみるとこうなった。

実行結果:
screenshot-from-2016-09-20-16-16-18

ちょうど300度(-60度)のアファイン変換をかませて、目と目が平行になった付近の角度の画像で顔認識をとってみると、みごとあっちゃんの顔を認識することができた。
しかし、このサンプルを実行してみた人ならお分かりだろうが、少しずつ回転をかけていって顔を認識させようとすると、下の例のように顔でないところを顔として認識してしまうリスクが高くなる。
screenshot-from-2016-09-20-17-05-30

実際に回転させて顔認識をとろうと思ったときには、認識したRectangeがある程度の大きさ以上で、同じような位置を顔として認識していることが多い、と言ったような方法で顔として判断しないといけないらしい。

終わりに

OpenCVやってみたい病が発病して勢いでやったみたが、作ったら作ったで結構楽しかった。
これについて調べる上で出てきた「TensorFlowによるももクロメンバー顔認識」に結構興味を引かれたので、また別のグループとかでそういうのをやってみたいと思う。

参考URL

シェアする

  • このエントリーをはてなブックマークに追加

フォローする