Python + OpenCVでの画像処理を調べたつつつドキュメントを斜め読みしていたら、顔認識に関するチュートリアルがあった。最近、人の顔を覚えられなくなってきているワタクシを助けてくれそうなテクノロジーである。
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 ... 笑顔認識
とりあえず、これをコピーしておいた。で、識別器として使えるようにしておく。
face_cascade = cv2.CascadeClassifier(‘haarcascades/haarcascade_frontalface_default.xml’)
次に画像ファイルを読み込む。
cv2.imread()は、第二引数に0を指定するとグレースケールモードで読み込んでくれるんだけれど、その機能は使わないで、imgとしてカラーで読み込んだ後で、cv2.cvtColor()を使ってグレースケールに変換したコピーを持つ。これは、グレースケール画像を使って顔認識をして、カラー画像を表示するため。
img = cv2.imread(file)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
画像の中で顔を探して、その顔の位置の座標を戻す。もう訓練済みの識別データに基づいて識別器を使うだけなので、たったの一行。
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
顔を探すのはグレースケールに変換した画像(gray)の中で行なうんだけれど、その顔座標の位置に基づいて元のカラー画像(img)に、顔を囲む矩形を描画する。
for (x, y, w, h) in faces:
cv2.rectangle(img, (x,y), (x+w, y+h), (255,0,0), 2)
x,yが矩形の左上のそれぞれx,y座標を示していて、w,hはそれぞれ幅と高さを示しているんだけれど、OpenCVで画像(img)中に長方形を描画する時には、左上のx,y座標と右下のx,y座標がそれぞれ必要なので、(x,y), (x+w,y+h)というように指定している。
plt.imshow( cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.show()
最後に矩形を書きこんだ画像を表示するんだけれど、OpenCVのimread()で読み込んだカラー画像は何故かBGRの順にデータが保持されているが、今回、画像表示のために使うmatplotlibのpyplotではRGBの順にデータが保持されているという前提になっているので、plt.imshow()にデータを渡す際に、cv2.cvtColor(img, cv2.COLOR_BGR2RGB)で変換している。
ちなみに、顔は複数あってもOKなように、for (x,y,w,h) in faces: のように受けているんだけれど、ちょうどいい感じの著作権とか肖像権がウヤムヤな集合写真みたいなのが手元に無いので、試せないな。
import os
import cv2
import numpy as np
from matplotlib import pyplot as plt
%matplotlib inline
def facedetect(file):
face_cascade = cv2.CascadeClassifier('haarcascades/haarcascade_frontalface_default.xml')
img = cv2.imread(file)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
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)
plt.imshow( cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.show()
if __name__ == '__main__':
lenna = "4.2.04.tiff"
if os.path.exists(lenna):
facedetect(lenna)
眼認識もできる
チュートリアルには、顔認識の後で眼認識をするサンプルも載っている。
顔があったら眼があるよね、顔じゃないところには眼は無いよね、ということで、顔認識した矩形の中を対象に眼認識を走らせるように工夫されている。
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識別器をちゃんと作るようにならないといけないのだけれど、それはまた別の話だな。