Atom's tech blog

顔の傾き(Roll)で顔検出を検証してみた

概要

前回、顔の傾き無しの画像で4つの顔検出アルゴリズムで検出をしてみたが、10°刻みでどこまで検出可能かトライしてみた

コメント

  • 今回カメラは使用せず、画像ファイルを直接読み出して検証しています
  • ソースコード は一度に4つのアルゴリズムを動作させて、矩型の輪郭を描画をしています。正しい描画ができているかちょっと不安
  • 顔検出できていないライブラリは輪郭描画なし
  • 画像回転は「scipy.ndimage」モジュールのrotate()を利用しています

環境条件

結果

アルゴリズム 顔検出最大角度(Roll)
Haar+Cascade ±10°
Dlib ±50°
CNN ±90°
MTCNN ±90°

カメラを使用して顔検出することを考えると、Dlibの最大50°という結果であり、アルゴリズムとして普通に使えそう。Harr+Cascadeはちょっとした傾きで顔検出ができないため難しい。CNN、MTCNNは文句なしの結果。あとはCPUのパフォーマンス次第で判断になりそうです。

Picth(ピッチ:顔の左右の向き)とYaw(ヨー:顔の上下の向き)も検証したいとことですが、簡単な画像データをrotate(回転)するだけでは検証できないので、 今度は顔の角度を判定するようなプログラムをサンプルソースを元にトライしてみたい。

イメージ画像

  • 赤色線:Haar 特徴量+Cascade 識別器
  • 緑色線:Dlib(HOG特徴量+SVM識別器)
  • 水色線:CNN
  • 黄色線:MTCNN

f:id:iAtom:20201105212534p:plainf:id:iAtom:20201105212614p:plain
左からRoll:0°,10°

f:id:iAtom:20201105212736p:plainf:id:iAtom:20201105212904p:plain
左からRoll:20°,30°

f:id:iAtom:20201105213122p:plainf:id:iAtom:20201105213145p:plain
左からRoll:40°,50°

f:id:iAtom:20201105213209p:plainf:id:iAtom:20201105213234p:plain
左からRoll:60°,70°

f:id:iAtom:20201105213256p:plainf:id:iAtom:20201105213322p:plain
左からRoll:80°,90°

ソースコード

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import dlib
import cv2
import time
import copy
from mtcnn import MTCNN
from scipy import ndimage

# CNN
cnn_fn= "./mmod_human_face_detector.dat"
cnn_face_detector = dlib.cnn_face_detection_model_v1(cnn_fn)
# Dlib
detector = dlib.get_frontal_face_detector()
# HaarCascade
cascade_fn = "./haarcascade_frontalface_alt.xml"
cascade = cv2.CascadeClassifier(cascade_fn)
# MTCNN
mtcnn_detector = MTCNN()
     
# 顔検出するオリジナル画像を読み出し
frame = cv2.imread("./face_image.jpg")

# 画像をコピー
face_frame = copy.deepcopy(frame)
# 画像のグレースケールと平滑化
gray_image = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray_image = cv2.equalizeHist(gray_image)
# BGRからRGBに変換
rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

# 顔の角度を変えるためのループ
for cnt in range(0,10):
    # 角度を10度刻みにする
    angle = cnt*10
    # イメージ画像を回転する (CNN,Dlib用)
    frame_rotate = ndimage.rotate(frame,angle)
    # イメージ画像を回転する (HaarCascade顔検出用)
    gray_image_rotate = ndimage.rotate(gray_image,angle)
    # イメージ画像を回転する (MTCNN顔検出用)
    rgb_image_rotate = ndimage.rotate(rgb_image,angle)

    # CNN顔検出
    cnn_dets = cnn_face_detector(frame_rotate, 1)
    # Dlib顔検出
    dlib_dets = detector(frame_rotate, 1)
    # HaarCascade顔検出
    haar_dets = cascade.detectMultiScale(gray_image_rotate, scaleFactor=1.3, minNeighbors=3, minSize=(30, 30), flags=cv2.CASCADE_SCALE_IMAGE)   
    # MTCNN顔検出
    mtcnn_dets = mtcnn_detector.detect_faces(rgb_image_rotate)   

    # CNNの顔検出箇所の矩型描画ループ
    for cnn_face in cnn_dets:     
        cv2.putText(frame_rotate, "CNN", (int(cnn_face.rect.left()), int(cnn_face.rect.top())-4), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0),1, cv2.LINE_AA)          
        # 顔箇所を四角で描画
        cv2.rectangle(frame_rotate, (cnn_face.rect.left(),cnn_face.rect.top()), (cnn_face.rect.right(),cnn_face.rect.bottom()), (255,255,0), 2)

    # Dlibの顔検出箇所の矩型描画ループ
    for k, d in enumerate(dlib_dets):
        cv2.putText(frame_rotate, "Dlib", (int(d.left()), int(d.top())-4), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0),1, cv2.LINE_AA)        
        # 顔箇所を四角で描画
        cv2.rectangle(frame_rotate, (int(d.left()), int(d.top())),(int(d.right()), int(d.bottom())), (0, 255, 0), 2)

    # OpenCVの顔検出箇所の矩型描画ループ
    for(x,y,w,h) in haar_dets:     
        cv2.putText(frame_rotate, "Haar",  (x,y-4),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1, cv2.LINE_AA)
            # 顔箇所を四角で描画
        cv2.rectangle(frame_rotate, (x,y), (x+w,y+h), (0,0,255), 2)

    # MTCNNの顔検出箇所の矩型描画ループ
    for face in mtcnn_dets:
        # 座標と高幅を取得
        box_x, box_y, box_w, box_h = face['box']
        cv2.putText(frame_rotate, "MTCNN",  (box_x,box_y-4),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 1, cv2.LINE_AA)
        # 顔箇所を四角で描画
        cv2.rectangle(frame_rotate, (box_x,box_y), (box_x+box_w,box_y+box_h), (0,255,255), 2)

    # ファイルに保存
    cv2.imwrite("./facedetect_angle_" + str(angle)+".png",frame_rotate)
 
print("\n Exit Program")
cv2.destroyAllWindows()