Just My Life & My Work

最先看到有人臉偵測的設備是什麼呢?當然是非「數位相機」莫屬啦~只要real-time找到人臉,接著加以追蹤,直到按下快門前,焦點都會在這個人臉上頭,如此一來就不太會發生失焦的狀況。

多媒體設計課程老師介紹一篇paper「Robust Real-Time Face Detection」,這是一篇2004年在IJCV上發表的論文,裡頭談到Haar-Features、Detection Framework、Integral Image、Feature Selection、Learning the Classifier、AdaBoost、Detection Procedure、Cascade Method

廣告

Face Detection基本作法就是先做Feature Extraction,接著做Cascade Detection特徵擷取(Feature Extraction)沒有問題,那麼瀑布偵測(Cascade Detection)是什麼呢?其實就只是一連串的檢查動作,通過第一關才能進入第二關,通過第二關才能進入地三關……直到通過最後一關,才會將此特徵辨識為人臉。

Cascade Architecture
Cascade Architecture
廣告

paper所使用的特徵型有四種,在偵測階段時,因為大部分的window沒有包含人臉,於是提早拒絕負面的window,來加快偵測速度。因為我們人臉不一定都是「正」的,有時候會坐著、躺著、手會支撐著臉、情人依偎等等,臉可能會有高達45度角的狀況,此時檢測視窗(如下圖的A、B、C、D特徵型態)就會改變角度來掃描搜索視窗(如下圖的正方形)。

Feature Types
Feature types used by Viola and Jones
廣告

搜尋了網路的這篇paper,竟然有2146次被引用的紀錄!我想偵測人臉是個非常實用的技術,在照相、攝影、監視器等各種追蹤人的設備上都相當需要,能想到這idea的作者真是厲害呢!

註:2012/11/20查詢google,該篇引用次數已達6260次

首先來看一下程式碼:

廣告
/**
	Theme: Face Detection
	Compiler: Dev C++ 4.9.9.2
	Date: 100/04/26
	Author: ShengWen
	Blog: https://cg2010studio.wordpress.com/
*/
#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
#include <iostream>
using namespace std;
// the minimum object size
int min_face_height = 50;
int min_face_width = 50;
int main( int argc , char ** argv ){
	string image_name="../image/XD2.jpg";
	// Load image
	IplImage* image_detect=cvLoadImage(image_name.c_str(), 1);
	string cascade_name="../haarcascades/haarcascade_frontalface_default.xml";
	// Load cascade
	CvHaarClassifierCascade* classifier=(CvHaarClassifierCascade*)cvLoad(cascade_name.c_str(), 0, 0, 0);
	if(!classifier){
        cerr<<"ERROR: Could not load classifier cascade."<<endl;
        return -1;
    }
	CvMemStorage* facesMemStorage=cvCreateMemStorage(0);
	IplImage* tempFrame=cvCreateImage(cvSize(image_detect->width, image_detect->height), IPL_DEPTH_8U, image_detect->nChannels);
	if(image_detect->origin==IPL_ORIGIN_TL){
        cvCopy(image_detect, tempFrame, 0);    }
    else{
        cvFlip(image_detect, tempFrame, 0);    }
	cvClearMemStorage(facesMemStorage);
CvSeq* faces=cvHaarDetectObjects(tempFrame, classifier, facesMemStorage, 1.1, 3
, CV_HAAR_DO_CANNY_PRUNING, cvSize(min_face_width, min_face_height));
	if(faces){
		for(int i=0; i<faces->total; ++i){
			// Setup two points that define the extremes of the rectangle,
			// then draw it to the image
			CvPoint point1, point2;
			CvRect* rectangle = (CvRect*)cvGetSeqElem(faces, i);
			point1.x = rectangle->x;
			point2.x = rectangle->x + rectangle->width;
			point1.y = rectangle->y;
			point2.y = rectangle->y + rectangle->height;
			cvRectangle(tempFrame, point1, point2, CV_RGB(255,0,0), 3, 8, 0);
		}
	}
	// Save the image to a file
	cvSaveImage("../result/result.jpg", tempFrame);
	// Show the result in the window
	cvNamedWindow("Face Detection Result", 1);
	cvShowImage("Face Detection Result", tempFrame);
	cvWaitKey(0);
	cvDestroyWindow("Face Detection Result");
	// Clean up allocated OpenCV objects
	cvReleaseMemStorage(&facesMemStorage);
	cvReleaseImage(&tempFrame);
	cvReleaseHaarClassifierCascade(&classifier);
	cvReleaseImage(&image_detect);
	return EXIT_SUCCESS;
}

cvHaarDetectObjects(const CvArr* image, CvHaarClassifierCascade* cascade,
CvMemStorage* storage, double scale_factor=1.1,
int min_neighbors=3, int flags=0,
CvSize min_size=cvSize(0,0) );
說明:

廣告
  • image: 要偵測的圖片。
  • cascade: 要使用的分類器。
  • storage: 偵測到的物件所儲存的記憶體區塊。
  • scale_factor: 搜索視窗成長比率。
  • min_neighbors: 最少鄰近偵測視窗。一個臉可能重複偵測好幾次,但我們只要取一次,如果設0的話,所有偵測的視窗都會畫出來。
  • flag: 演算法模式。
  • min_size: 檢測視窗的最小尺寸。因為AdaBoost的演算法,分成搜索視窗檢測視窗兩個部分,搜索視窗在整個影像中移動,檢測視窗搜索視窗中移動並計算特徵值。當檢測視窗越小,則計算特徵值的單位就越小,需要的運算量就越高,但是結果不一定會更為精確。

在此需要特別注意的地方是路徑,接著就是classifier的選擇,我所使用的是OpenCV 2.0,訓練好的分類器放在C:\OpenCV2.0\data\haarcascades,這次人臉偵測實驗使用haarcascade_frontalface_default.xml,效果比較好的則是haarcascade_frontalface_alt.xml。其它的還有可以偵測眼睛、嘴巴、鼻子、上半身、下半身等等,但並不是所有分類器都能運作的相當好。這次實做相當好玩,有興趣的人趕快來玩玩看吧~

廣告

註:對於初學者來說,可以試著先安裝環境來跑程式,接著瞭解程式碼的意義喔!環境建置可以參考我這篇:Dev-C++4.9.9.2 安裝 OpenCV 2.0

Face Detection
我和我弟四人一起到芝山貴族世家吃牛排的兩張相片實驗結果。(因為作業要求一定要有自己的臉在相片中,所以只好出賣自己XD~)

結論:Haar Features的效果非常好,然而在影像長寬不一樣(如上)時,會有不一樣的偵測結果,我想這跟我設定的偵測框架大小有關係,像素多的影像可以擷取較細節的特徵,而像素少的則相反,看來還是得根據需要來調整影像大小偵測框架大小。因為影像有縮小,但依然可以看到,左邊也就是像素大的影像紅框比較細,而右邊則比較粗。

廣告

我將參數調整恰到好處:

  • int min_face_height = 50;
  • int min_face_width = 50;
  • CvSeq* faces=cvHaarDetectObjects(tempFrame, classifier, facesMemStorage, 1.1, 3, CV_HAAR_DO_CANNY_PRUNING, cvSize(min_face_width, min_face_height));
Face Detection
相當完美的結果:)~
廣告

參考:簡單的人臉偵測(face detection),使用HaarDetectObjectscvHaarDetectObjectsViola-Jones object detection frameworkFace Detection (人臉偵測)

推薦書籍:

廣告

Comments on: "[OpenCV] 人臉偵測 (Face Detection)" (70)

  1. […] 另外人臉佔整張相片太小,也不容易被偵測出來,看來跟它的演算法參數設定有關係。這部分可參考我2011年寫的文章:[OpenCV] 人臉偵測 (Face Detection)。 […]

  2. 現在寫App,也要來整合人臉偵測功能囉!🤡

  3. 容我叫您一聲大神!~
    小弟我正好在仔細閱讀這篇論文,加上您的解說讓我閱讀的速度提升不少!在此感謝您!
    對了,2018/06/02 引用次數已經來到:16025 次

    Liked by 2 people

  4. 您好!
    我有問題想問您一下
    我偵測道人臉之後,想要在偵測眼睛
    for( size_t i = 0; i < faces.size(); i++ )
    {
    Point center( faces[i].x + faces[i].width/2, faces[i].y + faces[i].height/2 );
    ellipse( frame, center, Size( faces[i].width/2, faces[i].height/2), 0, 0, 360, Scalar( 255, 0, 255 ), 4, 8, 0 );

        Mat faceROI = frame_gray( faces[i] );
        std::vector<Rect> eyes;
    
        //-- In each face, detect eyes
        eyes_cascade.detectMultiScale( faceROI, eyes, 1.1, 2, 0 |CASCADE_SCALE_IMAGE, Size(30, 30) );
    
        for( size_t j = 0; j < eyes.size(); j++ ) // <- 這個迴圈沒辦法進去
        {
            Point eye_center( faces[i].x + eyes[j].x + eyes[j].width/2, faces[i].y + eyes[j].y + eyes[j].height/2 );
            int radius = cvRound( (eyes[j].width + eyes[j].height)*0.25 );
            circle( frame, eye_center, radius, Scalar( 255, 0, 0 ), 4, 8, 0 );
        }
    }
    

    請問有什麼辦法可以解決嘛?

  5. 請問 #include &amp;lt;iostream&amp;gt; 這行什麼意思呢
    我執行這行會編譯錯誤 該如何解決呢 謝謝

  6. HappyMan你好:
    我想請問你,我有問題!?我是電機研究所研究生,我從網路有[人臉偵測]範例!
    我用C++試試看[人臉偵測]範例,但…安裝OpenCV2.6,有debug。
    [fatal error LNK1181: 無法開啟輸入檔 ‘opencv_calib3d246.lib’],我的opencv
    程式庫目錄是[build\x86\vc10/vc9]程式庫目錄都有。[攝影機]不行播放!
    [p.s:我用攝影機是微軟Kinect]

    Liked by 1 person

  7. 您好 我想請問一下,所謂min_neighbors以及四個演算法是甚麼意思呀,字面上看不太懂,我們要使用這個演算法來做實驗數據,改參數來測驗,可以使用哪幾個參數來做實驗數據比較好??

    • min_neighbors: 最少鄰近偵測視窗。一個臉可能重複偵測好幾次,但我們只要取一次,如果設0的話,所有偵測的視窗都會畫出來。

      你是說特徵型有四種吧?!文章有列出四種Feature types。

      建議只要有可調整的參數,都可以拿來做實驗,屆時你將會知道怎樣的參數組合,適用於你的目標影像喔~

  8. 不好意思~我想請問一下
    因為影像中的人臉有大有小,有不同角度
    例如獨自的大頭照、團體的合照
    那這樣子以Haar features來訓練的話,是否也要不同大小、角度的Haar features圖塊
    且Haar features的圖塊由小到大在要測試的圖片上一個區塊一個區塊的掃描?
    又如果今天前置條件為人臉區塊設定在window某個區塊範圍內
    如此的話Haar features訓練的樣本是不是就可以減少很多

    • 訓練這部分我尚未深入研究,我就以個人觀點推論⋯⋯

      訓練的話需要考慮所有可能的影像,所以需要各種解析度的圖,而圖的內容也要包含多樣的人臉,男女、角度、年齡、大小、膚色等等。

      以Haar features演算法確實要陸續掃描圖塊。

      訓練樣本跟測試的圖沒有直接關聯,樣本依然要考慮所有可能的影像。你的意思應該是在測試圖上圈一塊區域,然後拿這塊區域來執行掃描,當然會比掃整張圖快很多囉~

  9. 請問cvqueryframe 在3.0好像是不同的用法

    Liked by 2 people

回覆給Seven Chang 取消回覆

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料

標籤雲