kivantium活動日記

プログラムを使っていろいろやります

OpenCVとdlibを連携した顔検出プログラム

dlibという画像処理や機械学習などの機能を持つC++ライブラリがあります。dlibに付属している顔検出ライブラリはOpenCVのものより精度が良いということだったので試してみました。

インストール

いつものように環境はUbuntu 14.04です。既にOpenCVとCMakeがインストールされている必要があります。

  • Sourceforgeからソースコードをダウンロードして解凍します。
  • 適当な場所に次の内容でCMakeLists.txtを保存します。
cmake_minimum_required(VERSION 2.8.4)
PROJECT(face_detect)
#dlibの解凍先に合わせて適宜変更
include(dlib/cmake)
ADD_EXECUTABLE(face_detection face_detection.cpp)
TARGET_LINK_LIBRARIES(face_detection dlib)
  • CMakeLists.txtと同じディレクトリに次の内容のface_detection.cppを保存します。

dlib C++ Library - face_detection_ex.cppを改変)

#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/gui_widgets.h>
#include <dlib/image_io.h>
#include <iostream>

using namespace dlib;
using namespace std;

int main(int argc, char** argv){
    try{
        if (argc == 1){
            cout << "画像ファイルを指定してください" << endl;
            return 0;
        }

        //顔検出器の読み込み
        frontal_face_detector detector = get_frontal_face_detector();
        image_window win;

        // 全ての引数に対してループ
        for (int i = 1; i < argc; ++i){
            //画像の読み込み
            array2d<unsigned char> img;
            load_image(img, argv[i]);

            //80x80の顔を検出するため画像を拡大した方が検出率が上がるらしい
            pyramid_up(img);

            //顔部分の長方形を取得
            std::vector<rectangle> dets = detector(img);

            //画像と顔領域の描画
            win.clear_overlay();
            win.set_image(img);
            win.add_overlay(dets, rgb_pixel(255,0,0));

            cout << "次の画像に進むにはEnterを押してください" << endl;
            cin.get();
        }
    }
    catch (exception& e){
        cout << "例外が発生しました" << endl;
        cout << e.what() << endl;
    }
}
  • 次のようにしてコンパイルします。初回は時間がかかります。
mkdir build
cd build
cmake ..
cmake --build . --config Release
  • buildディレクトリに移動して実行します
./face_detection lena.jpg

結果
f:id:kivantium:20150405222422p:plain:w300

動画に対する顔検出

動画に対する顔検出も行いました。動画の読み込みにはOpenCVを使います。

  • CMakeLists.txtを次の内容に変更
cmake_minimum_required(VERSION 2.8.4)

PROJECT(face_detect)
#dlibの解凍先に合わせて適宜変更
include(dlib/cmake)

find_package(OpenCV)
if (OpenCV_FOUND)
   include_directories(${OpenCV_INCLUDE_DIRS})

   ADD_EXECUTABLE(face_detection face_detection.cpp)
   TARGET_LINK_LIBRARIES(face_detection dlib ${OpenCV_LIBS} )
endif()
  • face_detection.cppを次の内容に変更
#include <iostream>
#include <dlib/opencv.h>
#include <dlib/image_io.h>
#include <dlib/gui_widgets.h>
#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/image_processing/render_face_detections.h>
#include <opencv2/highgui/highgui.hpp>

using namespace dlib;
using namespace std;

int main(int argc, char** argv){  
    try{
        frontal_face_detector detector = get_frontal_face_detector();
        image_window win;
        cv::VideoCapture cap("video.mp4");

        while(!win.is_closed()){
            cv::Mat temp;
            cap >> temp;
            if(temp.empty()){
                break;
            }
            //OpenCVのMatからdlibのarray2dに変換
            cv_image<bgr_pixel> cimg(temp);
            array2d<unsigned char> img;
            assign_image(img, cimg);
            pyramid_up(img);

            //顔部分の長方形を取得
            std::vector<rectangle> dets = detector(img);

            //動画と顔領域の描画
            win.clear_overlay();
            win.set_image(img);
            win.add_overlay(dets, rgb_pixel(255,0,0));
        }
    }
    catch (exception& e){
        cout << "exception thrown!" << endl;
        cout << e.what() << endl;
    }
}

感想

frontal_faceのみの検出ということで、OpenCVと比べて目に分かるほどの差はなかったように感じます。

dlibには顔の向きの推定SVMなどの機能もあるようなので機会があったら使ってみようと思います。

追記
動画作成用プログラム

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import cv2
import dlib

if len(sys.argv) != 3:
    sys.exit("argument error: please select input output file")

#元動画の読み込み
cap = cv2.VideoCapture(sys.argv[1])
# 出力動画の設定
fourcc = cv2.cv.CV_FOURCC(*'DIVX')
out = cv2.VideoWriter(sys.argv[2], fourcc,
    int(cap.get(cv2.cv.CV_CAP_PROP_FPS)),
    (int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH)),
     int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT))))

face_detector = dlib.get_frontal_face_detector()
while(cap.isOpened()):
    ret, frame = cap.read()
    if ret == True:
        dlib_img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        rects = face_detector(dlib_img, 1)

        for index, d in enumerate(rects):
            width = d.right() - d.left()
            height = d.bottom() - d.top()
            cv2.rectangle(frame, (d.left(), d.top()), (d.right(), d.bottom()), (255, 255, 255), 2)
        cv2.imshow("result", frame)
        cv2.waitKey(1)
        out.write(frame)
    else:
        break
cap.release()
out.release()
cv2.destroyAllWindows()