1. ホーム
  2. OpenCV

OpenCV演習 - 顔検出と顔画像抽出

2022-02-25 03:20:58

    タクシー運転手の顔と免許証の写真を比較したり、入退室管理システムの入室者の顔と顔ライブラリ内の顔を比較したりと、顔比較は最近では比較的よく使われる機能である。顔比較を実装するためには、まず、カメラで撮影した写真の中から顔の正しい位置を検出し、顔を抽出する顔検出を行います。OpenCVはフリーでオープンソースであることを考えると、これを実現するのに適しており、LinuxでのOpenCVCのインストールは、前回のブログで紹介したように ubuntu 16.04 OpenCV3.2.0をフルコンパイルしてインストールしました。

(i) 顔検出の実装。

    以下のコードは、OpenCVCのサンプルを改良したもので、顔検出と人の目検出を可能にし、検出された結果を丸で囲んでいます。コードは以下の通りです。

/*============================================================================= 
# FileName: facecheck.cpp
# Desc: detect faces and eyes by opencv ,and then cut the face      
# Author: Licaibiao 
# Version:  
# LastChange: 2017-10-31 
# History: 
=============================================================================*/ 
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <stdio.h>

using namespace std;
using namespace cv;
 
#define CASCADENAME ". /modle/haarcascade_frontalface_alt2.xml"
#define NESTEDCASCADENAME ". /modle/haarcascade_eye_tree_eyeglasses.xml"
#define FACEPHOTO_FACENAME ". /image/result.jpg"
#define DETECT_IMAGE ". /image/001.jpg"

 
void detectAndDraw( Mat& img, CascadeClassifier& cascade,
	CascadeClassifier& nestedCascade, double scale, bool tryflip )
{
    double t = 0;
    vector<Rect> faces, faces2;
	/* Define seven colors for face markers */
    const static Scalar colors[] =
    {
        Scalar(255,0,0),
        Scalar(255,128,0),
        Scalar(255,255,0),
        Scalar(0,255,0),
        Scalar(0,128,255),
        Scalar(0,255,255),
        Scalar(0,0,255),
        Scalar(255,0,255)
    };
	
    Mat gray, smallImg;
	
	/* Because the haar-like features are used, they are all based on grayscale images, here they are converted to grayscale images */
    cvtColor( img, gray, COLOR_BGR2GRAY );
	
	/* Scale the image down to speed up detection */
    double fx = 1 / scale;
	/* shrink the size to 1/scale, using linear interpolation */
    resize( gray, smallImg, Size(), fx, fx, INTER_LINEAR );
	/* histogram equalization */
    equalizeHist( smallImg, smallImg );
 
	/* Used to calculate the algorithm execution time */
    t = (double)getTickCount();
	
	/* face detection
		smallImg: the original image of the input
		faces: the target sequence of detected faces
		1.1 : the ratio of each image size reduction is 1.1
		2: Each target must be detected at least 3 times to be considered a real target
		CV_HAAR_SCALE_IMAGE: means not scaling classifier to detect, but scaling image
		Size(30, 30) The maximum and minimum size of the target
	*/
    cascade.detectMultiScale( smallImg, faces, 1.1, 2, CASCADE_SCALE_IMAGE, Size(30, 30) );
    if( tryflip )
    {
        flip(smallImg, smallImg, 1);
        cascade.detectMultiScale( smallImg, faces2,1.1, 2, 0|CASCADE_SCALE_IMAGE,Size(30, 30) );
        for( vector<Rect>::const_iterator r = faces2.begin(); r ! = faces2.end(); ++r )
        {
            faces.push_back(Rect(smallImg.cols - r->x - r->width, r->y, r->width, r->height));
        }
    }
	
	/* Subtract as the time to execute the algorithm */
    t = (double)getTickCount() - t;
    printf( "detection time = %g ms\n", t*1000/getTickFrequency());
    
	for ( size_t i = 0; i < faces.size(); i++ )
    {
        Rect r = faces[i];
        Mat smallImgROI;
        vector<Rect> nestedObjects;
        Point center;
        Scalar color = colors[i%8];
        int radius;
		
		/* face aspect ratio, draw circle between 0.75-1.3, other range draw rectangle */
        double aspect_ratio = (double)r.width/r.height;
        if( 0.75 < aspect_ratio && aspect_ratio < 1.3 )
        {
			/* Restore the original size Calculate the circle center and circle radius */
            center.x = cvRound((r.x + r.width*0.5)*scale);
            center.y = cvRound((r.y + r.height*0.5)*scale);
            radius = cvRound((r.width + r.height)*0.25*scale);
			/* Draw the face detection area Draw a circle */
            circle( img, center, radius, color, 3, 8, 0 );
        }
        else
		{
			/* draw the detection area, draw a rectangle */
			rectangle( img, cvPoint(cvRound(r.x*scale), cvRound(r.y*scale)),
				cvPoint(cvRound((r.x + r.width-1)*scale), cvRound((r.y + r.height-1)*scale)), color, 3, 8, 0);
		}
		
		/* detect human eye, draw human eye on human face */
        if( nestedCascade.empty())
		{
			continue;
		}
            
        smallImgROI = smallImg( r );
		
		/* Human eye detection */
        nestedCascade.detectMultiScale( smallImgROI, nestedObjects, 1.1, 2, CASCADE_SCALE_IMAGE, Size(30, 30) );	
        for ( size_t j = 0; j < nestedObjects.size(); j++ )
        {
            Rect nr = nestedObjects[j];
			/* Restore the original size Calculate the center and radius of the circle */
            center.x = cvRound((r.x + nr.x + nr.width*0.5)*scale);
            center.y = cvRound((r.y + nr.y + nr.height*0.5)*scale);
            radius = cvRound((nr.width + nr.height)*0.25*scale);
            /* Draw the human eye detection area Draw a circle */
			circle( img, center, radius, color, 3, 8, 0 );
        }
    }
	/* Show the image img */
    imshow( "result", img );
}
 
 
int main( int argc, const char** argv )
{
    Mat frame, image;
    bool tryflip;
    CascadeClassifier cascade, nestedCascade;
    double scale = 1.3;
 
	/* Load the classifier */
    if ( !nestedCascade.load( NESTEDCASCADENAME ) )
	{
		cerr << "WARNING: Could not load classifier cascade for nested objects" << endl;
	}   
    if( !cascade.load( CASCADENAME ) )
    {
        cerr << "ERROR: Could not load classifier cascade" << endl;
        return -1;
    }
	
	/* Load the image */
	image = imread(DETECT_IMAGE, 1 );
    if(image.empty()) 
	{
		cout << "Couldn't read iamge" << DETECT_IMAGE << endl;
		
	}
	
    cout << "Detecting face(s) in " << DETECT_IMAGE << endl;
    
	/* Detect the face and eyes and draw the detected area */
	if( !image.empty() )
    {
        detectAndDraw( image, cascade, nestedCascade, scale, tryflip );
        waitKey(0);
    }
    return 0;
}



上記のコードをコンパイルして実行するには、いくつか注意する点があります。
(1) haarcascade_frontalface_alt2.xml と haarcascade_eye_tree_eyeglasses.xml は opencv で提供されるファイルです。
(2) opencvのソースコードは、インストール時にいくつかのファイルをダウンロードする必要があるため、インターネットの速度が遅いところでは、インストールに失敗しやすくなっています。sudo apt-get install libopencv-dev python-opencv コマンドで直接インストールする場合は、 `pkg-config --cflags --libs opencv` パラメータを指定してコンパイルする必要があります。

コンパイルと実行結果

licaibiao@ubuntu:~/OpenCV/FaceTest$ make DetectDraw 
g++ FaceDetect_Draw.cpp -o TestDetectDraw `pkg-config --cflags --libs opencv` 
licaibiao@ubuntu:~/OpenCV/FaceTest$ ls
FaceCheck.cpp FaceCheck_Draw.cpp FaceDetect.cpp FaceDetect_Draw.cpp image Makefile modle out TestDetectDraw
licaibiao@ubuntu:~/OpenCV/FaceTest$ . /TestDetectDraw 
Detecting face(s) in . /image/001.jpg
detection time = 423.166 ms


実際の検出結果は以下の通りです。

上記は比較的普通の画像で、顔がはっきりしていて、画像の背景も比較的きれいなので、顔や人物の目の検出は正しくできています。もっと複雑な画像で試してみると、あまりうまくいきません。同じコード、同じ学習ファイルで、以下のような結果になりました。

    上の図から、顔は検出できることがわかりますが、すべての顔が検出されているわけではなく、誤判定しているものもいくつかあります。このことから、顔検出にオリジナルのOpencv学習ファイルを用いると、顔は検出できるが、その検出力はあまり高くないことが分かります。

    実際には、顔検出にOpenCVCを使う場合、認識率を上げるために、顔が検出された後に目があるかどうかを判断するとよいでしょう。組み込み機器では、顔検出や顔キーにOpenCVCを使うことはあまりお勧めできません。1つは、OpenCVはより多くのファイルに依存し、ライブラリファイルや学習ファイルを置くために、より多くのストレージスペースを必要とすることです。20は、検出があまり得意でないことです。

    次に、CとC++で書かれたオープンソースの顔認識コードで、コードサイズが小さく、検出結果が良好なMTCNNを紹介します。

    今回のテストコードは、こちらからダウンロードできます。 OpenCVテストコード

    Hessianの開発ボードで実行できるテストコードと、PCで実行できるテストコードのセットがあり、かなりごちゃごちゃしていて整理されていないので、必要であれば参照してください。また、コードを使う前に、PCに正しくOpenCVをインストールする必要があります。

licaibiao@ubuntu:~/OpenCV$ tree -L 2
.
├── arm_test
│ ├── 001.jpg
│ ├── example.cpp
│ ├── haarcascade_eye_tree_eyeglasses.xml
│ ├── haarcascade_frontalface_alt2.xml
│ ├── include
│ ├── lib
│ ├── Makefile
│ ├── OpencvFaceAPI.cpp
│ ├── OpencvFaceAPI.h
│ └── OpencvTestMain.cpp
├─ face_test
│ ├── detect.cpp
│ ├── example_1.cpp
│ ├── example.cpp
│ ├── example.cpp_ok
│ ├── face_detect1.cpp
│ ├── face_detect.cpp
│ ├── facedetect.cpp
│ ├── haarcascade_eye_tree_eyeglasses.xml
│ ├── haarcascade_eye.xml
│ ├── haarcascade_frontalface_alt2.xml
│ ├── haarcascade_frontalface_default.xml
│ ├── Makefile
│ ├── run.sh
│ └── test
└─ FaceTest
    ├─ FaceCheck.cpp
    ├── FaceCheck_Draw.cpp
    ├─ FaceDetect.cpp
    ├─ FaceDetect_Draw.cpp
    ├─ image
    ├─ Makefile
    ├─ modle
    ├─ out
    └─ TestDetectDraw

8 directories, 28 files
licaibiao@ubuntu:~/OpenCV$ 

<イグ