Python + OpenCVで顔認識

OpenCVで画像処理を試してみたついでに、ドキュメントを斜め読みしていたら、顔認識に関するチュートリアル(OpenCV: Face Detection using Haar Cascades)があった。最近、人の顔を覚えられなくなってきているオレを助けてくれそ うなテクノロジーではある。

Haar Cascadesを用いた顔認識

この辺りの機械学習のことは詳しくは良く分からないんだけれど、簡単に言うと顔じゃない画像と顔画像を使ってHaar特徴量から顔を判別できるように訓練する。その結果として識別データができるんだけれど、識別データを効率的に使って識別する方法として、Cascade識別器というのが考案されていて、OpenCVの配布物の中に含まれているようだ。

MacのAnacondaの場合は「~/anaconda3/share/OpenCV/haarcascades」に、Windows版のAnacondaの場合には「Anaconda3\Library\etc\haarcascades」の中に入っている。

  • haarcascade_eye.xml ... 眼認識
  • haarcascade_frontalface_default.xml ... 顔認識
  • haarcascade_smile.xml ... 笑顔認識

とりあえず、これをコピーしておくか、コードの中でFullPathで明示的に指定する。今回はコピーしといた。

face_cascade = cv2.CascadeClassifier('haarcascades/haarcascade_frontalface_default.xml')

次に画像ファイルを読み込む。cv2.imread()は、第二引数に0を指定するとグレースケールモードで読み込んでくれるんだけれど、その機能は使わないで、imgとしてカラーで読み込んだ後で、cv2.cvtColor()を使ってグレースケールに変換したコピーを持つ。

グレースケール画像(gray)で顔認識をして座標を取得して、元のカラー画像(img)に加工するため、それぞれの形式の画像データが必要ということだ。

img = cv2.imread(file)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

画像の中で顔を探して、その顔の位置の座標を戻す。もう訓練済みの識別データに基づいて識別器を使うだけなので、たったの一行。

faces = face_cascade.detectMultiScale(gray, 1.3, 5)

顔を探すのはグレースケールに変換した画像(gray)の中で行なうんだけれど、その顔座標の位置に基づいて元のカラー画像(img)に、顔を囲む矩形を描画する。
x,yが矩形の左上のそれぞれx,y座標を示していて、w,hはそれぞれ幅と高さを示しているんだけれど、OpenCVで画像(img)中に長方形を描画する時には、左上のx,y座標と右下のx,y座標がそれぞれ必要なので、(x,y), (x+w,y+h)というように指定している。

for (x, y, w, h) in faces:
    cv2.rectangle(img, (x,y), (x+w, y+h), (255,0,0), 2)

最後に矩形を書きこんだ画像を表示するんだけれど、OpenCVのimread()で読み込んだカラー画像は何故かBGRの順にデータが保持されているが、今回、画像表示のために使うmatplotlibのpyplotではRGBの順にデータが保持されているという前提になっているので、plt.imshow()にデータを渡す際に、cv2.cvtColor(img, cv2.COLOR_BGR2RGB)で変換している。

plt.imshow( cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.show()

ちなみに、顔は複数あってもOKなように、for (x,y,w,h) in faces: のように受けているんだけれど、ちょうどいい感じの著作権とか肖像権がウヤムヤな集合写真みたいなのが無いので、試せないな。

眼認識もできる

さて、チュートリアルには、顔認識の後で眼認識をするサンプルも載っている。顔があったら眼があるよね、顔じゃないところには眼は無いよね、ということで、顔認識した矩形の中を対象に眼認識を走らせるように工夫されている。なるほどな。

faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
    cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
    roi_gray = gray[y:y+h, x:x+w]
    roi_color = img[y:y+h, x:x+w]
    eyes = eye_cascade.detectMultiScale(roi_gray)
    for (ex,ey,ew,eh) in eyes:
        cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)

問題は、「Haar特徴量に基づくCascade識別器」に関して、何も分からなくて、detectMultiScale()メソッドの引数の説明が全然理解できないことだったりする。

さらに問題なのは、自分でCascade識別器を作れるようになる気がしないことだな。