在这篇博客需要解决之前遗留的两个BUG,一是用户在不初始化条件下运行程序,二是人脸检测的误差结果。
一、添加初始化警告
目前我们在“初始化”按钮对应的响应函数中封装了人脸分类器加载、开辟内存等操作:
![](https://box.kancloud.cn/2016-02-25_56ce6389859f0.png)
因此,如果用户在未单击“初始化”按钮的情况下进行图片读入,人脸检测,程序就会因为缺少人脸检测器而崩溃,因此我们向CGenderRecognitionMFCDlg类中添加一个布尔类型的标志位用于指示当前用户是否完成了初始化操作:
![](https://box.kancloud.cn/2016-02-25_56ce63899975d.png)
并在CGenderRecognitionMFCDlg类的构造函数中将其初始化为false(正常情况下VS会自动完整这个操作):
![](https://box.kancloud.cn/2016-02-25_56ce6389abc90.png)
接下来,在“初始化”按钮对应的响应函数中,将这个标志位置为true,代表初始化已经完成:
![](https://box.kancloud.cn/2016-02-25_56ce6389bb573.png)
接下来在“打开文件夹”按钮对应的响应函数的开头,添加标志位判断代码:
![](https://box.kancloud.cn/2016-02-25_56ce6389cb120.png)
此时,如果用户在未初始化时点击了“图像文件夹”按钮,程序会弹出对话框,提示用户先进行初始化操作:
![](https://box.kancloud.cn/2016-02-25_56ce6389dcc54.png)
二、人脸检测优化
在之前的程序中,人脸检测所返回的矩形不止一个,也就意味着存在检测误差。这里我们添加人脸检测的结果筛选代码,即根据检测结果矩形的面积进行筛选,只保留最大面积的矩形作为人脸检测的结果。这里需要对成员函数detect_and_draw()进行一些修改。
1、计算矩形面积
在人脸检测完成后,轮询检测结果序列,计算矩形面积,并保留面积最大的矩形标号:
~~~
/**********对检测出的人脸区域面积做比较,选取其中的最大矩形**********/
int maxface_label = 0; //最大面积人脸标签
Mat max_face = Mat::zeros(objects->elem_size,1,CV_32FC1); //候选矩形面积
for(int i = 0;i< objects->total;i++)
{
CvRect* r = (CvRect*)cvGetSeqElem(objects,i);
max_face.at<float>(i,0) = (float)(r->height * r->width);
if(i > 0&&max_face.at<float>(i,0) > max_face.at<float>(i - 1,0))
{
maxface_label = i;
}
}
~~~
然后更改结果显示部分的代码,只绘制最大面积的矩形:
~~~
/**********绘制检测结果**********/
if(objects->total > 0) //如果人脸检测成功
{
CvRect* rect = (CvRect*)cvGetSeqElem(objects,maxface_label);
cvRectangle(img,cvPoint(rect->x,rect->y),
cvPoint(rect->x + rect->width,rect->y + rect->height),cvScalar(0.0,255));
}
~~~
大功告成。这里由于对detect_and_draw()函数做了较大修改,因此在此给出修改后detect_and_draw()函数的完整代码:
~~~
void CGenderRecognitionMFCDlg::detect_and_draw(IplImage* img)
{
/**********初始化**********/
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)); //初始检测窗口大小
/**********对检测出的人脸区域面积做比较,选取其中的最大矩形**********/
int maxface_label = 0; //最大面积人脸标签
Mat max_face = Mat::zeros(objects->elem_size,1,CV_32FC1); //候选矩形面积
for(int i = 0;i< objects->total;i++)
{
CvRect* r = (CvRect*)cvGetSeqElem(objects,i);
max_face.at<float>(i,0) = (float)(r->height * r->width);
if(i > 0&&max_face.at<float>(i,0) > max_face.at<float>(i - 1,0))
{
maxface_label = i;
}
}
/**********绘制检测结果**********/
if(objects->total > 0) //如果人脸检测成功
{
CvRect* rect = (CvRect*)cvGetSeqElem(objects,maxface_label);
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);
}
~~~