如今我們的MFC框架已經初具規模,能夠讀取並顯示文件夾下的圖片,在這篇博文中我們將向其中添加人臉檢測的程序。
一、人臉檢測算法
這裡我們使用OpenCv封裝的Adaboost方法來進行人臉檢測,
二、初始化
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++中可以對類中私有成員中的靜態變量初始化嗎?
2、字符串的連接:C++字符換行