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はこちらを参考にさせていただきました。ありがとうございました。
結論というか感想は動画のほうを是非御覧ください。