本文介紹最基本的用OpenCV實現人臉檢測的方法。
一.人臉檢測算法原理
Viola-Jones人臉檢測方法
參考文獻:Paul Viola, Michael J. Jones. Robust Real-Time Face Detection[J]. International Journal of Computer Vision,2004,57(2):137-154.
該算法的主要貢獻有三:
1.提出積分圖像(integral image),從而可以快速計算Haar-like特征。
2.利用Adaboost學習算法進行特征選擇和分類器訓練,把弱分類器組合成強分類器。
3.采用分類器級聯提高效率。
二.OpenCV檢測原理
OpenCV中有檢測人臉的函數(該函數還可以檢測一些其他物體), 甚至還包含一些預先訓練好的物體識別文件。
所以利用這些現成的東西就可以很快做出一個人臉檢測的程序。
主要步驟為:
1.加載分類器。
用cvLoad函數讀入xml格式的文件。文件在OpenCV安裝目錄下的“data/haarcascades/”路徑下。
http://blog.csdn.net/yang_xian521/article/details/6973667推薦使用haarcascade_frontalface_atl.xml和haarcascade_frontalface_atl2.xml
2.讀入待檢測圖像。讀入圖片或者視頻。
3.檢測人臉。
主要用的函數:
CvSeq* cvHaarDetectObjects( const CvArr* image, CvHaarClassifierCascade* cascade, CvMemStorage* storage, double scale_factor CV_DEFAULT(1.1), int min_neighbors CV_DEFAULT(3), int flags CV_DEFAULT(0), CvSize min_size CV_DEFAULT(cvSize(0,0)), CvSize max_size CV_DEFAULT(cvSize(0,0)) );
函數說明摘自《學習OpenCV》:CvArr* image是一個灰度圖像,如果設置了ROI,將只處理這個區域。CvHaarClassifierCascade* cascade是前面讀入的分類器特征級聯。CvMemStorage* storage 是這個算法的工作緩存。scale_factor :算法用不同尺寸的窗口進行掃描,scale_factor是每兩個不同大小的窗口之間的尺寸關系。min_neighbors 控制誤檢測,因為人臉會被不同位置大小的窗口重復檢測到,至少有這么多次檢測,我們才認為真的檢測到了人臉。flags有四個可用的數值,它們可以用位或操作結合使用。默認值是CV_HAAR_DO_CANNY_PRUNING,告訴分類器跳過平滑區域。min_size 指示尋找人臉的最小區域。 max_size 顯然應該是尋找人臉的最大區域了。。。
4.檢測結果表示。
可以畫個圈圈或者畫個方框表示。
三.代碼
#include "cv.h" #include "highgui.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <math.h> #include <float.h> #include <limits.h> #include <time.h> #include <ctype.h> #ifdef _EiC #define WIN32 #endif static CvMemStorage* storage = 0; static CvHaarClassifierCascade* cascade = 0; void detect_and_draw( IplImage* image ); const char* cascade_name = "haarcascade_frontalface_alt.xml"; /* "haarcascade_profileface.xml";*/ int main( int argc, char** argv ) { cascade_name = "haarcascade_frontalface_alt2.xml"; cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 ); if( !cascade ) { fprintf( stderr, "ERROR: Could not load classifier cascade\n" ); return -1; } storage = cvCreateMemStorage(0); cvNamedWindow( "result", 1 ); const char* filename = "Lena.jpg"; IplImage* image = cvLoadImage( filename, 1 ); if( image ) { detect_and_draw( image ); cvWaitKey(0); cvReleaseImage( &image ); } cvDestroyWindow("result"); return 0; } void detect_and_draw(IplImage* img ) { double scale=1.2; static CvScalar colors[] = { {{0,0,255}},{{0,128,255}},{{0,255,255}},{{0,255,0}}, {{255,128,0}},{{255,255,0}},{{255,0,0}},{{255,0,255}} };//Just some pretty colors to draw with //Image Preparation // IplImage* gray = cvCreateImage(cvSize(img->width,img->height),8,1); IplImage* small_img=cvCreateImage(cvSize(cvRound(img->width/scale),cvRound(img->height/scale)),8,1); cvCvtColor(img,gray, CV_BGR2GRAY); cvResize(gray, small_img, CV_INTER_LINEAR); cvEqualizeHist(small_img,small_img); //直方圖均衡 //Detect objects if any // cvClearMemStorage(storage); double t = (double)cvGetTickCount(); CvSeq* objects = cvHaarDetectObjects(small_img, cascade, storage, 1.1, 2, 0/*CV_HAAR_DO_CANNY_PRUNING*/, cvSize(30,30)); t = (double)cvGetTickCount() - t; printf( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) ); //Loop through found objects and draw boxes around them for(int i=0;i<(objects? objects->total:0);++i) { CvRect* r=(CvRect*)cvGetSeqElem(objects,i); cvRectangle(img, cvPoint(r->x*scale,r->y*scale), cvPoint((r->x+r->width)*scale,(r->y+r->height)*scale), colors[i%8]); } for( int i = 0; i < (objects? objects->total : 0); i++ ) { CvRect* r = (CvRect*)cvGetSeqElem( objects, i ); CvPoint center; int 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); cvCircle( img, center, radius, colors[i%8], 3, 8, 0 ); } cvShowImage( "result", img ); cvReleaseImage(&gray); cvReleaseImage(&small_img); }
四.結果及一些說明
運行結果如下圖:
需要說明的幾點:
1.圖像和.xml文件要放在該程序的bin目錄下(.sln所在的目錄)。
2.《學習OpenCV》里面就是用矩形表示,但是書里面的代碼不太對,原因是忽略了縮放因子,即void detect_and_draw(IplImage* img )里面的double scale=1.2;
這個縮放因子的作用是:拿到一個圖像,首先將它縮放(scale=1.2即變為一個小圖像),然后在縮放后的小圖像上檢測人臉,這樣會比較快。
最基本的就這么多吧。
注:本文所用OpenCV版本為2.3.1