最先看到有人臉偵測的設備是什麼呢?當然是非「數位相機」莫屬啦~只要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)是什麼呢?其實就只是一連串的檢查動作,通過第一關才能進入第二關,通過第二關才能進入地三關……直到通過最後一關,才會將此特徵辨識為人臉。

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

搜尋了網路的這篇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。
結論: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),使用HaarDetectObjects、cvHaarDetectObjects、Viola-Jones object detection framework、Face Detection (人臉偵測)。
推薦書籍:
- 博客來 – OpenCV with Microsoft Visual Studio影像辨識處理
- 博客來 – OpenCV圖像處理編程實例
- 博客來 – OpenCV3編程入門
- 博客來 – OpenCV程式設計參考手冊


Comments on: "[OpenCV] 人臉偵測 (Face Detection)" (70)
可以請問haar是對「pixel」去找嗎?
可是要怎麼確定要找的目標會佔幾個pixel呢?
目標的大小如果改變,訓練出的分類器不就沒有用了嗎?
還是我哪邊沒有看懂呢?
謝謝
讚Liked by 1 person
我也有這個問題QQ
讚Liked by 1 person
請問一下 “錯誤 7 error C2065: ‘TempFrame’ : 未宣告的識別項" 在程式是中
我都沒有看到對TempFrame的宣告 這要怎麼解決?
讚讚
[…] 一直覺得臉書的功能不斷地在進化,特別讓我驚訝的是關於相片,它除了做到人臉偵測外,還能辨識這個人是誰,方便我們幫友人貼標籤! […]
讚讚
請問如果要做動物的五官變識
有什麼方法
讚讚
跟人臉辨識一樣,若有現成的動物(如狗)臉部表情資料庫,就可以直接拿來套,反之就得自己去建立它喔~
讚讚
你好,看上面的範例圖中偵測出人臉的紅色框框好像都是正方形,程式中沒有看到調整這個框框比例的部分。想請教是否能調整成長方形去做偵測呢?
讚讚
嗨~
仔細看一下程式碼,min_face_height和min_face_width,就是分別設定框框的高和寬喔!
讚讚
你好:
我想請問一下
CvHaarClassifierCascade* classifier=(CvHaarClassifierCascade*)cvLoad(cascade_name.c_str(), 0, 0, 0);
在此段中,我一直無法選擇任何的分類器載入
classifier只要一載入,程式就會中斷,使用版本是OpenCV246,XML檔路徑也確定正確
請問一下我是少設定了甚麼…..
讚讚
你好:
我想請問一下我這一段:
CvHaarClassifierCascade* classifier=(CvHaarClassifierCascade*)cvLoad(cascade_name.c_str(), 0, 0, 0);
classifier無法載入任何XML檔(已經確認路徑是正確的)
請問是少了甚麼東西沒有設定嗎?
讚讚
不好意思 ,我是個初學者
想請教一個很基礎的問題
這個程式碼能用VC C++下去寫嗎
需如何改寫
非常感謝 !
讚讚
當然可以囉~
你只要安裝好VC C++,
相關設定完畢之後
將程式碼貼過去,
這些設定可以在我部落格上找到教學喔!
祝你新年快樂 😀
讚讚
不好意思再請教一下
我將程式碼貼上之後
跑出
LNK2019: 無法解析的外部符號 _cvReleaseHaarClassifierCascade 在函式 _main 中被參考
LNK2019: 無法解析的外部符號 _cvHaarDetectObjects 在函式 _main 中被參考
這兩個問題….一直找不到能解決的方法><
想請教一下是哪裡沒設定到嬤?
感謝~~
祝您順心如意^^
讚讚
看來像是某.lib檔沒有連結到,找一下跟人臉偵測相關的.lib檔加進去,再試試看吧! 😉
讚讚
感謝!!!!成功了~~~
THANKS!!!!
讚讚
恭喜! 😀
讚讚
[…] 回顧之前使用OpenCV所做的人臉偵測 (Face Detection),當時只是覺得這門技術好厲害。如今我感興趣的平台是行動裝置,趁此拿來把玩一下,說不一定可以想出有趣的應用! […]
讚讚
你好: 我想問 如果是用MATLAB 的語言寫的話! 要怎麼改?!
讚讚
如果可以找到現成的code來玩玩看是比較好的做法,如果你瞭解人臉偵測的理論,那麼你就可以試著去實做。若你有看到人臉偵測的source code,程式能力好的話就可以對照改成MATLAB的版本~
讚讚