如今我們的MFC框架已經初具規模,能夠讀取并顯示文件夾下的圖片,在這篇博文中我們將向其中添加人臉檢測的程序。
一、人臉檢測算法
這里我們使用OpenCv封裝的Adaboost方法來進行人臉檢測,參見:[C++開發人臉性別識別教程(4)——OpenCv的人臉檢測函數](http://blog.csdn.net/u013088062/article/details/50439630)
二、初始化
1、添加初始化按鈕
在進行人臉檢測之前需要初始化一些相關變量,例如開辟內存,加載檢測器等等。首先,我們為MFC框架添加一個初始化按鈕,并將ID更改為IDC_BUTTON_INITIAL:

雙擊這個按鈕,添加事件響應函數:

2、初始化變量
從之前的博客中可知,OpenCv在進行人臉檢測時需要用到兩個靜態變量:static CvMemStorage* storage和static CvHaarClassifierCascade* cascade,這里我們將其作為成員變量添加到CGenderRecognitionMFCDlg類中,這里由于static CvMemStorage*和static CvHaarClassifierCascade*這兩個類型名在MFC類向導中是無法被識別的,因此需要手動添加至CGenderRecognitionMFCDlg類的構造函數中:

接下里對這兩個驚天變量進行初始化。C++明確規定靜態成員變量要在類外進行初始化,而不能在類內聲明時或者構造函數內進行初始化,原因就是靜態變量時屬于類本身的,而非類對象的屬性,和全局變量類似,因此我們將這兩個靜態變量的初始化操作放在GenderRecognitionMFCDlg.cpp文件(從解決方案資源管理器窗口中查找cpp文件)的開頭位置:
~~~
// 用于應用程序“關于”菜單項的 CAboutDlg 對話框
CvMemStorage* CGenderRecognitionMFCDlg::storage = NULL;
CvHaarClassifierCascade* CGenderRecognitionMFCDlg::cascade = NULL;
~~~
然后在“初始化”按鈕的響應函數OnBnClickedButtonInitial()中加載對應的人臉檢測器:
~~~
void CGenderRecognitionMFCDlg::OnBnClickedButtonInitial()
{
cascade = cvLoadHaarClassifierCascade("D:\\opencv\\sources\\data\\haarcascades
\\haarcascade_frontalface_alt_tree.xml",cvSize(30,30));
storage = cvCreateMemStorage(0);
// TODO: 在此添加控件通知處理程序代碼
}
~~~
初始化完成。
三、編寫人臉檢測函數
這里將人臉檢測的操作封裝成一個函數detect_and_draw(),作為成員函數添加到CGenderRecognitionMFCDlg類中:

? 在類視圖中找到detect_and_draw()函數,完善其人臉檢測代碼,由于之前已經詳細介紹過人臉檢測的相關操作,這里直接給出代碼:
~~~
void CGenderRecognitionMFCDlg::detect_and_draw(IplImage* img)
{
/**********初始化**********/
double scale = 1.2;
IplImage* gray = cvCreateImage(cvSize(img->width,img->height),8,1);
/**********灰度化**********/
if (img->nChannels = 3)
{
cvCvtColor(img,gray, CV_BGR2GRAY);//將圖像灰度化存放在gray中
}
else
{
gray = img;
}
/**********直方圖均衡**********/
cvEqualizeHist(gray,gray);
/**********人臉檢測**********/
cvClearMemStorage(storage);
CvSeq* objects = cvHaarDetectObjects(gray,//待檢測圖像
cascade, //分類器標識
storage, //存儲檢測到的候選矩形
1.3, //相鄰兩次檢測中窗口擴大的比例
3, //認為是人臉的最小矩形數(閾值)
0, //CV_HAAR_DO_CANNY_PRUNING
cvSize(30,30)); //初始檢測窗口大小
/**********繪制檢測結果**********/
if(objects->total > 0) //如果人臉檢測成功
{
for (int i = 0; i < (objects ? objects->total : 0); i++)
{
CvRect* rect = (CvRect*)cvGetSeqElem(objects,i);
cvRectangle(img,cvPoint(rect->x,rect->y),
cvPoint(rect->x + rect->width,rect->y + rect->height),cvScalar(0.0,255));
}
}
/**********在圖像控件上顯示圖像**********/
CvvImage cvvImage;
cvvImage.CopyOf(img);
cvvImage.DrawToHDC(m_pPicCtlHdc,m_PicCtlRect);
cvReleaseImage(&gray);
}
~~~
注意這里相對于之前的程序,添加了一項直方圖均衡化的操作,以提高人臉檢測的成功率:

? 四、調用人臉檢測函數
理論上在顯示圖像之前應該自動調用人臉檢測操作,因此在GetNextBigImg()函數中調用人臉檢測函數:

由于在detect_and_draw()函數中已經封裝了picture顯示的程序,所以可以將GetNextBigImg()函數中原有的picture控件顯示程序去掉。
大功告成,順利完成人臉檢測:

三、總結
這里我們初步完成了MFC中的人臉檢測功能,但這里存在兩個嚴重的BUG,一是如果用戶未單擊“初始化”按鈕,直接打開圖片,程序會因缺少必要的初始化步驟而直接崩潰;二是如上圖所見,OpenCv在進行人臉檢測時可能會錯誤檢測出多個矩形,其中只有一個矩形包含人臉,其余的都是干擾,需要進行處理,我們將在下一篇博客中介紹如何解決這兩個BUG。
同時在此需要強調一下兩個問題:
1、靜態成員變量的初始化:[c++中可以對類中私有成員中的靜態變量初始化嗎?](http://www.cnblogs.com/carbs/archive/2012/04/04/2431992.html)
2、字符串的連接:[C++字符換行 .](http://www.cnblogs.com/zhoug2020/archive/2012/04/01/2428156.html)
?