(10)——添加图片的人脸检测程序

最后更新于:2022-04-01 20:14:29

  如今我们的MFC框架已经初具规模,能够读取并显示文件夹下的图片,在这篇博文中我们将向其中添加人脸检测的程序。   一、人脸检测算法   这里我们使用OpenCv封装的Adaboost方法来进行人脸检测,参见:[C++开发人脸性别识别教程(4)——OpenCv的人脸检测函数](http://blog.csdn.net/u013088062/article/details/50439630)   二、初始化   1、添加初始化按钮   在进行人脸检测之前需要初始化一些相关变量,例如开辟内存,加载检测器等等。首先,我们为MFC框架添加一个初始化按钮,并将ID更改为IDC_BUTTON_INITIAL: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-25_56ce6388ef242.png)   双击这个按钮,添加事件响应函数: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-25_56ce63890b684.png)   2、初始化变量   从之前的博客中可知,OpenCv在进行人脸检测时需要用到两个静态变量:static CvMemStorage* storage和static CvHaarClassifierCascade* cascade,这里我们将其作为成员变量添加到CGenderRecognitionMFCDlg类中,这里由于static CvMemStorage*和static CvHaarClassifierCascade*这两个类型名在MFC类向导中是无法被识别的,因此需要手动添加至CGenderRecognitionMFCDlg类的构造函数中: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-25_56ce63891b1b7.png)   接下里对这两个惊天变量进行初始化。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类中: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-25_56ce63892ea3b.png)    在类视图中找到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); } ~~~   注意这里相对于之前的程序,添加了一项直方图均衡化的操作,以提高人脸检测的成功率: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-25_56ce63893f93a.png)    四、调用人脸检测函数   理论上在显示图像之前应该自动调用人脸检测操作,因此在GetNextBigImg()函数中调用人脸检测函数: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-25_56ce638951805.png)   由于在detect_and_draw()函数中已经封装了picture显示的程序,所以可以将GetNextBigImg()函数中原有的picture控件显示程序去掉。   大功告成,顺利完成人脸检测: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-25_56ce6389675a2.png)   三、总结   这里我们初步完成了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)  
';