dlibとOpenCVのそれぞれで顔検出した結果比較


はじめに

google-image-downloadで取得した画像ファイルから、学習用の顔画像をファイルに保存するために、OpenCVのHaarCascadeとdlibのface Detetor(HOG)を試して、性能を比較してみました。

開発環境

Ubuntu 18.04.4 LTS
Python 3.6.9
opencv 4.5.1
dlib 19.21.1

比較結果

入力画像ファイル数 正しく検出した顔の数 誤検出した数 処理時間
OpenCV HaarCascade 468 278 8 real 0m28.000s
dlib Face Detector(HOG) 468 841 9 real 9m43.149s

入力画像に含まれている顔の数は様々です。
誤検出したどうかは、切り出し後の画像を目視して確認しました。

  • こちらが検出前のダウンロード画像の一部

  • こちらが検出後に保存した画像の一部

ソースコード


#!/usr/bin/env python3

import cv2
import dlib
import sys
import os
import argparse
import logging

logger = logging.getLogger()

SAVED_IMAGE_SIZE = (200, 200)

class faceDetector:
    pass

class HaarDetector(faceDetector):
    def __init__(self):
        self.__detector = cv2.CascadeClassifier('./cascades/haarcascade_frontalface_default.xml')
    def run(self, img):
        return self.__detector.detectMultiScale(img, 1.08, 5, minSize=SAVED_IMAGE_SIZE)
    def getVertexes(self, face):
        (x, y, w, h) = face
        return (x, y, w, h)

class HogDetector(faceDetector):
    def __init__(self):
        self.__detector = dlib.get_frontal_face_detector()
    def run(self, img):
        return self.__detector(img, 1)
    def getVertexes(self, face):
        x0 = max(0, face.left())
        y0 = max(0, face.top())
        x1 = max(0, face.right())
        y1 = max(0, face.bottom())
        return (x0, y0, x1-x0+1, y1-y0+1)

OpenCV HaarCascadeとglib face detectorのインタフェース差異を吸収するためにfaceDetectorクラスを作成しました。
以下の関数で、入力画像ファイルから顔認識を行い、認識した領域をファイルに保存します。


def detectAndSaveFace(input_dir, output_dir, useHaar):
    logging.basicConfig(level=logging.INFO, format='%(message)s')

    if useHaar:
        detector = HaarDetector()
    else:
        detector = HogDetector()

    files_processed = 0
    faces_detected = 0

    for filename in os.listdir(input_dir):
        orig_img = cv2.imread(os.path.join(input_dir, filename), cv2.IMREAD_GRAYSCALE)  
        if orig_img is None:
            logger.warning('ignoring {0}'.format(filename))
            continue

        faces = detector.run(orig_img)
        logger.debug('Detected({0}) in {1}'.format(len(faces), filename))
        files_processed += 1

        # Save the detected bounding boxes
        for face in faces:
            (x, y, w, h) = detector.getVertexes(face)
            face_img = cv2.resize(orig_img[y:y+h, x:x+w], SAVED_IMAGE_SIZE)
            face_filename = '{0}/{1}.pgm'.format(output_dir, faces_detected)
            cv2.imwrite(face_filename, face_img)
            faces_detected += 1

    logger.info('files processed = {0}, faces detected = {1}'.format(files_processed, faces_detected))

余談

正面を向いた顔画像を保存したかったので、dlibのCNNは試しませんでした。
dlibで検出した画像範囲の座標がマイナスになる場合がありました。

最後に

dlibのHOGによる検出精度が良いのは想定通りでしたが、私のPCでは予想以上に処理時間がかかりました。