BETA

4種類の顔検出を動かしてみた [ Haar+Cascade/ HOG+SVM/ CNN/ MTCNN ]

投稿日:2020-04-26
最終更新:2020-05-03

概要

・幾つかの顔検出方法がありますが、今回は4種類の顔検出をトライ。
(2020/05/03)MTCNNを追加

  1. Haar 特徴量+Cascade 識別器
  2. Dlib(HOG特徴量+SVM識別器)
  3. CNN(mmod_human_face_detector.dat.bz2
  4. MTCNN(Multi-task Cascaded Convolutional Neural Networks for Face Detection

・ソースコードはカメラからキャプチャしたイメージにしています。自画像を載せたくないためフリー画像データの読み込みで。(画像読み出しの箇所はコメント文にして残しています)
・画像は6人の顔が写っているものを使用し、顔検出した一人ずつの顔をcv2.imwriteでファイル保存(このイメージ画像は掲載しません)します。
・各アルゴリズムで検出した顔サイズが異なる事に注意してください。最後あたりにサイズが違うイメージを入れています。

環境条件

(2020/05/03) Pythonを3.7.6、OpenCVを4.1.2にバージョンアップ

・MacBook Pro
・CPU:2.4 GHz クアッドコアIntel Core i5
・メモリ:8 GB 2133 MHz LPDDR3
・MacOS Catalina(10.15.4)
・Python 3.7.6
・Opencv 4.1.2
・VS code1.43.1
・Dlib 19.19
・Mtcnn 0.1.0

Haar 特徴量+Cascade 識別器 による顔検出

・顔検出時間:約0.01sec
・フレームレート:約12fps

顔検出イメージ(Haar 特徴量+Cascade 識別器)

ソースコード(Haar 特徴量+Cascade 識別器)

#!/usr/bin/env python  
# -*- coding: utf-8 -*-  
import cv2  
import time  
import copy  

# OpenCV  
cascade_fn = "./haarcascade_frontalface_alt.xml"  
cascade = cv2.CascadeClassifier(cascade_fn)  
#カメラデバイスオープン  
cap = cv2.VideoCapture(0)  

# カメラフレームサイズをVGAに変更する  
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) # カメラ画像の横幅を640に設定  
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # カメラ画像の縦幅を480に設定  

# フレームサイズ取得  
frame_width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)  
frame_height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)  

while(cap.isOpened() == True):  

    #FPS算出のため、事前に時間を取得    
    tick = cv2.getTickCount()   
    # カメラキャプチャ  
    ret, frame = cap.read()  
#    frame = cv2.imread("./face_image.png")  
#    frame = cv2.resize(frame,dsize=(int(frame_width),int(frame_height)))  
    # 画像をコピー  
    face_frame = copy.deepcopy(frame)  

    # 画像のグレースケールと平滑化  
    gray_image = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  
    gray_image = cv2.equalizeHist(gray_image)  

    t1 = time.time()   
    # OpenCV顔検出  
    dets = cascade.detectMultiScale(gray_image, scaleFactor=1.3, minNeighbors=3, minSize=(30, 30), flags=cv2.CASCADE_SCALE_IMAGE)  
    t2 = time.time()    

    face_cnt = 0  
    # OpenCVの顔検出箇所の矩型描画ループ  
    for(x,y,w,h) in dets:  
        # 顔のトリミング  
        face_image = face_frame[y:y+h, x:x+w]          
        cv2.putText(frame, "Haar",  (x,y-4),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1, cv2.LINE_AA)  
        # 顔箇所を四角で描画  
        cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0), 2)  
        # 顔だけをファイルに保存  
        cv2.imwrite('haar_face_image'+ str(face_cnt)+'.png',face_image)   
        face_cnt = face_cnt + 1        

    # FPS算出と表示用テキスト作成    
    fps = cv2.getTickFrequency() / (cv2.getTickCount() - tick)  
    # 検出時間を算出    
    detect_time = t2 - t1    
    # FPS表示    
    cv2.putText(frame, "FPS:{}".format(int(fps)),(10,450), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)    
    # 顔検出時間表示  
    cv2.putText(frame, "DetectTime:{:.2f}".format(detect_time),(10,465), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)    
    # フレームサイズ表示  
    cv2.putText(frame, str(int(frame_width))+"*"+str(int(frame_height)),(10,20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)     

    # ウィンドウに表示  
    cv2.namedWindow("opencv")    
    cv2.moveWindow("opencv",200,100) # Window表示位置指定     
    cv2.imshow('opencv', frame)  

    cv2.imwrite("./haar_facedetect_image.png",frame)  

    # ESCキーで終了    
    k = cv2.waitKey(10) & 0xff     
    if k == 27:    
        break    

# Do a bit of cleanup    
print("\n Exit Program")   
cap.release()  
cv2.destroyAllWindows()  

Dlib(HOG特徴量+SVM識別器)による顔検出

・顔検出時間:約0.16sec
・フレームレート:約4fps

顔検出イメージ(Dlib)

ソースコード(Dlib)

#!/usr/bin/env python  
# -*- coding: utf-8 -*-  
import dlib  
import cv2  
import time  
import copy  

# Dlib  
detector = dlib.get_frontal_face_detector()  
#カメラデバイスオープン  
cap = cv2.VideoCapture(0)  

# カメラフレームサイズをVGAに変更する  
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) # カメラ画像の横幅を640に設定  
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # カメラ画像の縦幅を480に設定  

# フレームサイズ取得  
frame_width= cap.get(cv2.CAP_PROP_FRAME_WIDTH)  
frame_height= cap.get(cv2.CAP_PROP_FRAME_HEIGHT)  

while(cap.isOpened() == True):  
    #FPS算出のため、事前に時間を取得    
    tick = cv2.getTickCount()       
    # カメラキャプチャ  
    ret, frame = cap.read()  
#    frame = cv2.imread("./face_image.png")  
#    frame = cv2.resize(frame,dsize=(int(frame_width),int(frame_height)))      
    # 画像をコピー  
    face_frame = copy.deepcopy(frame)  

    t1 = time.time()   
    # Dlibの顔検出  
    dets = detector(frame, 1)  
    t2 = time.time()        

    face_cnt = 0  
    # Dlibの顔検出箇所の矩型描画ループ  
    for k, d in enumerate(dets):  
        # 顔のトリミング  
        face_image = face_frame[d.top():d.bottom(), d.left():d.right()]   

        # Dlib名を書き込み  
        cv2.putText(frame, "Dlib", (int(d.left()), int(d.top())-4), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0),1, cv2.LINE_AA)          
        # 顔箇所を四角で描画  
        cv2.rectangle(frame, (int(d.left()), int(d.top())),(int(d.right()), int(d.bottom())), (0, 255, 0), 2)  
        # 顔だけをファイルに保存  
        cv2.imwrite('dlib_face_image'+ str(face_cnt)+'.png',face_image)   
        face_cnt = face_cnt + 1    

    # FPS算出と表示用テキスト作成    
    fps = cv2.getTickFrequency() / (cv2.getTickCount() - tick)  
    # 検出時間を算出    
    detect_time = t2 - t1        
    # FPS表示    
    cv2.putText(frame, "FPS:{}".format(int(fps)),(10,450), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)    
    # 顔検出時間表示  
    cv2.putText(frame, "DetectTime:{:.2f}".format(detect_time),(10,465), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)    
    # フレームサイズ表示  
    cv2.putText(frame, str(int(frame_width))+"*"+str(int(frame_height)),(10,20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)     

    # ウィンドウに表示  
    cv2.namedWindow("DLIB")  
    cv2.moveWindow("DLIB",200,100) # Window表示位置指定   
    cv2.imshow('DLIB', frame)  

    cv2.imwrite("./dlib_facedetect_image.png",frame)  

    # ESCキーで終了    
    k = cv2.waitKey(10) & 0xff     
    if k == 27:    
        break    

# Do a bit of cleanup    
print("\n Exit Program")  
cap.release()  
cv2.destroyAllWindows()  

CNN(mmod_human_face_detector.dat.bz2)による顔検出

・顔検出時間:約2.43sec
・フレームレート:約0fps

顔検出イメージ(CNN)

ソースコード(CNN)

#!/usr/bin/env python  
# -*- coding: utf-8 -*-  
import dlib  
import cv2  
import time  
import copy  

# CNN  
cnn_fn= "./mmod_human_face_detector.dat"  
cnn_face_detector = dlib.cnn_face_detection_model_v1(cnn_fn)  

#カメラデバイスオープン  
cap = cv2.VideoCapture(0)  

# カメラフレームサイズをVGAに変更する  
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) # カメラ画像の横幅を640に設定  
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # カメラ画像の縦幅を480に設定  

# フレームサイズ取得  
frame_width= cap.get(cv2.CAP_PROP_FRAME_WIDTH)  
frame_height= cap.get(cv2.CAP_PROP_FRAME_HEIGHT)  

while(cap.isOpened() == True):  
    #FPS算出のため、事前に時間を取得    
    tick = cv2.getTickCount()        
    # カメラキャプチャ  
    ret, frame = cap.read()  
#    frame = cv2.imread("./face_image.png")  
#    frame = cv2.resize(frame,dsize=(int(frame_width),int(frame_height)))      
    # 画像をコピー  
    face_frame = copy.deepcopy(frame)  

    t1 = time.time()   
    # CNNの顔検出  
    cnn_dets = cnn_face_detector(frame, 1)  
    t2 = time.time()       

    face_cnt = 0  
    # CNNの顔検出箇所の矩型描画ループ  
    for face in cnn_dets:  
        # 顔のトリミング  
        face_image = face_frame[face.rect.top():face.rect.bottom(), face.rect.left():face.rect.right()]           
        # 顔箇所にCNN文字を表示  
        cv2.putText(frame, "CNN", (int(face.rect.left()), int(face.rect.top())-4), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0),1, cv2.LINE_AA)            
        # 顔箇所を四角で描画  
        cv2.rectangle(frame, (face.rect.left(),face.rect.top()), (face.rect.right(),face.rect.bottom()), (0,255,0), 2)  
        # 顔だけをファイルに保存  
        cv2.imwrite('cnn_face_image'+ str(face_cnt)+'.png',face_image)   
        face_cnt = face_cnt + 1  

   # FPS算出と表示用テキスト作成    
    fps = cv2.getTickFrequency() / (cv2.getTickCount() - tick)  
    # 検出時間を算出    
    detect_time = t2 - t1        
    # FPS表示    
    cv2.putText(frame, "FPS:{}".format(int(fps)),(10,450), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)    
    # 顔検出時間表示  
    cv2.putText(frame, "DetectTime:{:.2f}".format(detect_time),(10,465), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)    
    # フレームサイズ表示  
    cv2.putText(frame, str(int(frame_width))+"*"+str(int(frame_height)),(10,20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)     

    # ウィンドウに表示  
    cv2.namedWindow("CNN")  
    cv2.moveWindow("CNN",200,100) # Window表示位置指定   
    cv2.imshow('CNN', frame)  

    cv2.imwrite("./cnn_facedetect_image.png",frame)  

    # ESCキーで終了    
    k = cv2.waitKey(10) & 0xff     
    if k == 27:    
        break    

# Do a bit of cleanup    
print("\n Exit Program")  
cap.release()  
cv2.destroyAllWindows()  

MTCNN(Multi-task Cascaded Convolutional Neural Networks for Face Detection)による顔検出(2020/05/03)MTCNNを追加

・顔検出時間:約0.14sec
・フレームレート:約4fps

顔検出イメージ(MTCNN) (2020/05/03)MTCNNを追加


ついでに...顔のランドマーク(目、鼻、口)も検出箇所に赤色のサークルを表示

ソースコード(MTCNN)(2020/05/03)MTCNNを追加

import cv2  
from mtcnn import MTCNN  
import copy  
import time  

detector = MTCNN()  

#カメラデバイスオープン  
cap = cv2.VideoCapture(0)  

# カメラフレームサイズをVGAに変更する  
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) # カメラ画像の横幅を640に設定  
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # カメラ画像の縦幅を480に設定  

# フレームサイズ取得  
frame_width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)  
frame_height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)  

while(cap.isOpened() == True):  

    #FPS算出のため、事前に時間を取得    
    tick = cv2.getTickCount()   
    # カメラキャプチャ  
    ret, frame = cap.read()  
#    frame = cv2.imread("./face_image.png")  
#    frame = cv2.resize(frame,dsize=(int(frame_width),int(frame_height)))  
    # 画像をコピー  
    face_frame = copy.deepcopy(frame)  
    # BGRからRGBに変換  
    image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)  

    t1 = time.time()   
    # MTCNN顔検出  
    mtcnn_dets = detector.detect_faces(image)      
    t2 = time.time()    

    face_cnt = 0  
    # MTCNNの顔検出箇所の矩型描画ループ  
    for face in mtcnn_dets:  
        # 座標と高幅を取得  
        box_x, box_y, box_w, box_h = face['box']  
        # 顔のトリミング  
        face_image = face_frame[box_y:box_y+box_h, box_x:box_x+box_w]    
        # 顔画像の保存  
        cv2.imwrite('face image'+ str(face_cnt)+'.png',face_image)   
        face_cnt = face_cnt + 1  

        # 顔箇所にMTCNN文字を表示  
        cv2.putText(face_frame, "MTCNN",  (box_x,box_y-4),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1, cv2.LINE_AA)  
        # 顔箇所を四角で描画  
        cv2.rectangle(face_frame, (box_x,box_y), (box_x+box_w,box_y+box_h), (0,255,0), 2)  

        for key, value in face['keypoints'].items():  
            # 顔のランドマーク箇所に小さい赤色サークルを描画(目、鼻、口)              
            cv2.circle(face_frame,value, 1, (0,0,255), -1)  

   # FPS算出と表示用テキスト作成    
    fps = cv2.getTickFrequency() / (cv2.getTickCount() - tick)  
    # 検出時間を算出    
    detect_time = t2 - t1        
    # FPS表示    
    cv2.putText(face_frame, "FPS:{}".format(int(fps)),(10,450), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)    
    # 顔検出時間表示  
    cv2.putText(face_frame, "DetectTime:{:.2f}".format(detect_time),(10,465), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)    
    # フレームサイズ表示  
    cv2.putText(face_frame, str(int(frame_width))+"*"+str(int(frame_height)),(10,20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)     

    # ウィンドウに表示  
    cv2.namedWindow("MTCNN")  
    cv2.moveWindow("MTCNN",200,100) # Window表示位置指定   
    cv2.imshow('MTCNN', face_frame)  

    cv2.imwrite("./mtcnn_facedetect_image.png",face_frame)  

    # ESCキーで終了    
    k = cv2.waitKey(10) & 0xff     
    if k == 27:    
        break    

# Do a bit of cleanup    
print("\n Exit Program")  
cap.release()  
cv2.destroyAllWindows()  

顔検出時間

(2020/05/03)MTCNNを追加
・検出精度は考えずに処理時間を早くしたい場合は、Haar+Cascade。Haar+Cascadeは顔の角度がつくと検出できないため、画像をRotateしながら顔検出が必要になります。(今後検証します)
・CNNは遅い。ハイスペックのCPUではないと使えない感じです。
・DlibはCNNに比べ160msで検出は速いですが、カメラの性能を考えると遅いです。動体検知で顔検出と顔認証する場合は、10fps程度は必要でありDlibのコンパイルオプションで高速化が可能っぽい。

アルゴリズム 顔検出時間 全体フレームレート
Haar+Cascade 0.01sec 12fps
Dlib 0.16sec 4fps
CNN 2.43sec 0fps
MTCNN 0.14sec 4fps

顔検出イメージ(Haar/Dlib/CNN/MTCNNを一緒に検出)

(2020/05/03)MTCNNを追加
青-CNN/赤-Harr/緑-DLib/黄-MTCNN。アルゴリズムによって顔検出のサイズが異なるなることがわかります。なんか大きさが様々。

ソースコード(Haar/Dlib/CNN/MTCNNを一緒に検出)

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

# 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()  

#カメラデバイスオープン  
cap = cv2.VideoCapture(0)  

# カメラフレームサイズをVGAに変更する  
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) # カメラ画像の横幅を640に設定  
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # カメラ画像の縦幅を480に設定  

# フレームサイズ取得  
frame_width= cap.get(cv2.CAP_PROP_FRAME_WIDTH)  
frame_height= cap.get(cv2.CAP_PROP_FRAME_HEIGHT)  

while(cap.isOpened() == True):  
    #FPS算出のため、事前に時間を取得    
    tick = cv2.getTickCount()        
    # カメラキャプチャ  
    ret, frame = cap.read()  
    frame = cv2.imread("./face_image.png")  
    frame = cv2.resize(frame,dsize=(int(frame_width),int(frame_height)))      
    # 画像をコピー  
    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)  

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

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

    # Dlibの顔検出箇所の矩型描画ループ  
    for k, d in enumerate(dlib_dets):  
        cv2.putText(frame, "Dlib", (int(d.left()), int(d.top())-4), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0),1, cv2.LINE_AA)          
        # 顔箇所を四角で描画  
        cv2.rectangle(frame, (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, "Haar",  (x,y-4),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1, cv2.LINE_AA)  
        # 顔箇所を四角で描画  
        cv2.rectangle(frame, (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, "MTCNN",  (box_x,box_y-4),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 1, cv2.LINE_AA)  
        # 顔箇所を四角で描画  
        cv2.rectangle(frame, (box_x,box_y), (box_x+box_w,box_y+box_h), (0,255,255), 2)  

    # ウィンドウに表示  
    cv2.namedWindow("FaceDetect")  
    cv2.moveWindow("FaceDetect",200,100) # Window表示位置指定   
    cv2.imshow('FaceDetect', frame)  

    cv2.imwrite("./all_facedetect_image.png",frame)  

    # ESCキーで終了    
    k = cv2.waitKey(10) & 0xff     
    if k == 27:    
        break    

# Do a bit of cleanup    
print("\n Exit Program")  
cap.release()  
cv2.destroyAllWindows()  

Atom'sサイト関連情報

OpenCVで顔認証やってみた(Part1:学習編)
OpenCVで顔認証やってみた(Part2:認証編)

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@主にプログラム技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう