Pythonの学習がてらですが、顔検出をやっていなかったと思い、OpenCVとDlibをテストしてみました。
Pycharmいいですね!
venv環境でopencv-pythonはさくっと入ります。
問題はDlibで、公式ページ見ながら試行錯誤してなんとか入りました。
動画にしました。
ここからはコードの話です。
まずはOpenCVですが、カスケードは
face_cascade = cv2.CascadeClassifier('haar-like/data/haarcascade_frontalface_alt.xml')
leftEye_cascade = cv2.CascadeClassifier('haar-like/data/haarcascade_mcs_lefteye.xml')
rightEye_cascade = cv2.CascadeClassifier('haar-like/data/haarcascade_mcs_righteye.xml')
nose_cascade = cv2.CascadeClassifier('haar-like/data/haarcascade_mcs_nose.xml')
mouth_cascade = cv2.CascadeClassifier('haar-like/data/haarcascade_mcs_mouth.xml')
eye_cascade = cv2.CascadeClassifier('haar-like/data/haarcascade_eye.xml')
を使いました。
汚いソースですが、
def detector(faces):
facePoint = ""
for (x, y, w, h) in faces:
# 検知した顔を矩形で囲む
# 顔画像(グレースケール)
roi_gray = gray[y:y + h, x:x + w]
cv2.imshow('roi_gray',roi_gray)
cv2.moveWindow('roi_gray', 100, 200)
# 顔画像の左上(グレースケール)
roi_gray_leftUpper = gray[y+h//4:y + h//2, x:x + w//2]
cv2.imshow('roi_gray_leftUpper',roi_gray_leftUpper)
cv2.moveWindow('roi_gray_leftUpper', 340, 200)
# 顔画像の右上(グレースケール)
roi_gray_rightUpper = gray[y+h//4:y + h//2, x+w//2:x + w]
cv2.imshow('roi_gray_rightUpper',roi_gray_rightUpper)
cv2.moveWindow('roi_gray_rightUpper', 500, 200)
# 顔画像の下(グレースケール)
roi_gray_under = gray[y+ h//2:y + h, x:x + w]
cv2.imshow('roi_gray_under',roi_gray_under)
cv2.moveWindow('roi_gray_under', 360, 400)
# 顔画像の中央(グレースケール)
roi_gray_center = gray[y +h//4 :y + h - h//4, x+w//4:x + w - w//4]
cv2.imshow('roi_gray_center',roi_gray_center)
cv2.moveWindow('roi_gray_center', 400, 300)
# 顔画像(カラースケール)
roi_color = frame[y:y + h, x:x + w]
# 顔の中から左目を検知
lefteye = leftEye_cascade.detectMultiScale(roi_gray_leftUpper,minNeighbors=1)
if len(lefteye) > 0:
# 3番目(幅)が一番大きい要素のみ取得
ex, ey, ew, eh = lefteye[np.argmax(lefteye, axis=0)[2]]
# 検知した目を矩形で囲む
leftEyePoint = [x+ex, y+ey+h//4, x+ex + ew, y+ey + eh+h//4]
# 顔の中から右目を検知
righteye = rightEye_cascade.detectMultiScale(roi_gray_rightUpper,minNeighbors=1)
if len(righteye) > 0:
# 3番目(幅)が一番大きい要素のみ取得
ex, ey, ew, eh = righteye[np.argmax(righteye, axis=0)[2]]
# 検知した目を矩形で囲む
rightEyePoint = [x+ex+w//2, y+ey+h//4, x+ ex + ew+w//2,y+ ey + eh+h//4]
# 顔の中から鼻を検知
nose = nose_cascade.detectMultiScale(roi_gray_under,minNeighbors=1)
if len(nose) > 0:
# 3番目(幅)が一番大きい要素のみ取得
ex, ey, ew, eh = nose[np.argmax(nose, axis=0)[2]]
# 検知した鼻を矩形で囲む
nosePoint = [x+ex, y+ey+h//2,x+ ex + ew,y+ ey + eh+h//2]
# 顔の中から口を検知
mouth = mouth_cascade.detectMultiScale(roi_gray_under,minNeighbors=1)
if len(mouth) > 0:
# 3番目(幅)が一番大きい要素のみ取得
ex, ey, ew, eh = mouth[np.argmax(mouth, axis=0)[2]]
# 検知した口を矩形で囲む
mouthPoint = [x + ex,y+ ey+h//2,x+ ex + ew, y+ey + eh+h//2]
if len(lefteye) > 0 and len(righteye) > 0 and len(nose) > 0 and len(mouth) > 0:
facePoint = [[x, y, x + w, y + h],leftEyePoint,rightEyePoint,nosePoint,mouthPoint]
return facePoint
これを呼び出すと各顔のパーツが入ります。各パーツは必ず1つあるぞ、前提です。
次にDlibですが、
def face_shape_detector_dlib(frame,detector,predictor):
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# frontal_face_detectorクラスは矩形, スコア, サブ検出器の結果を返す
dets, scores, idx = detector.run(frame_rgb, 0)
if len(dets) > 0:
for i, rect in enumerate(dets):
shape = predictor(frame_rgb, rect)
shape = face_utils.shape_to_np(shape)
clone = frame.copy()
cv2.putText(clone, "Dlib", (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 2)
# landmarkを画像に書き込む
for (x, y) in shape[0:68]:
cv2.circle(clone, (x, y), 1, (0, 0, 255), 5)
# shapeで指定した個所の切り取り画像(ROI)を取得
(x, y, w, h) = cv2.boundingRect(np.array([shape[48:68]])) #口の部位のみ切り出し
roi = frame[y:y + h, x:x + w]
roi = cv2.resize(roi,(160,100))
(x, y, w, h) = cv2.boundingRect(np.array([shape[42:48]])) # 左目の部位のみ切り出し
leftEye = frame[y:y + h, x:x + w]
leftEye = cv2.resize(leftEye, (100, 50))
(x, y, w, h) = cv2.boundingRect(np.array([shape[36:42]])) # 左目の部位のみ切り出し
rightEye = frame[y:y + h, x:x + w]
rightEye = cv2.resize(rightEye, (100, 50))
return clone, roi
else :
return frame, None
Dlibはこちらを参考にさせていただきました。ありがとうございました。
結論というか感想は動画のほうを是非御覧ください。

