人脸检测之基于Opencv和MFC创作的换脸小程序
1. 背景
Opencv已经接触了有段日子了,无论是Python还是C++,都在控制台内运行,使用Opencv的imshow观察结果。最近在学习MFC,突发奇想地希望把人脸检测和MFC结合起来,制作一个简单地换脸小程序。
2. 设计
采用简单的按钮-事件机制,每个按钮实现不同的功能。程序面板设计如下图:
3. 解决的问题
1. 显示图片的问题
Opencv1的时候,可以支持直接在MFC的Pic control控件内绘图。但是在Opencv2以后,就不再支持了,为了方便,我从网上找到了这段代码直接导入工程使用。
2. 打开图片的问题
我希望程序能够友好一些,所以摒弃了原来在代码内手工编辑图片路径的方式,使用MFC中的CfileDialog类来新建文件选择对话框,从而友好地获取文件路径,方便了操作。
3. 字符串格式转换问题
Cstring转char*,在Unicode编码方式地工程里,只能采用WideCharToMultiByte()函数进行转换。
CStringfileinfo=filepathname;
intlen=WideCharToMultiByte(CP_ACP,0,fileinfo,-1,NULL,0,NULL,NULL);
char*path=newchar[len+1];
WideCharToMultiByte(CP_ACP,0,fileinfo,-1,path,len,NULL,NULL);
4. IplImage和Mat的使用问题
IplImage是指针,而Mat是对象,所以这两种类型的赋值要注意,给IplImage类型赋值实际上仍是原来的Image,而新定义一个Mat再给其赋值实际上就是进行了对象之间的拷贝操作。
5. sprintf_s函数的使用问题
sprintf_s(result,"Havefound %d faces",faces.size());
如果含有中文,那么MessageBox时会出现乱码,这跟编码有关,再Unicode的工程内,尽量使用英文以避免乱码。
6. Vector<Rect>类型的顶点坐标问题
.tl()是时左上角坐标,类型Point
.br()是右下角坐标,类型也是Point
注意这是个函数调用,不加()的话编译报错
如error“.x”的左边必须有类/结构/联合”
7. image的坐标系问题
8. CascadeClassifier分类器问题
不知道是不是跟版本有关,使用cvCascadeClassifier下的cvHarrDetect程序崩溃,而使用CascadeClassifier下的detectMultiScale则正常运行。
4. 运行效果
5. 代码
1. CvvImage.cpp和CvvImage.h见上一篇博文
2. 各个按钮响应事件
voidCChangeFaceDlg::OnBnClickedButton1() { // TODO: 在此添加控件通知处理程序代码 CString filter; filter="(文件类型)(*.*)|*.*||"; CFileDialogdlg(TRUE,NULL,NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,filter,NULL); if(dlg.DoModal()==IDOK) { filename = dlg.GetFileName(); fileext = dlg.GetFileExt();//文件扩展名 filepathname =dlg.GetPathName(); //MessageBox(str); } CString fileinfo = filepathname; int len=WideCharToMultiByte(CP_ACP,0,fileinfo,-1,NULL,0,NULL,NULL); char *path =new char[len +1]; WideCharToMultiByte(CP_ACP,0,fileinfo,-1,path,len,NULL,NULL); //MessageBox(fileinfo); if(image) cvReleaseImage(&image); image = cvLoadImage(path,1); //显示图片 gray_img = cvLoadImage(path,1); delete[] path; DrawPicToHDC(gray_img, IDC_PIC1); } voidCChangeFaceDlg::OnBnClickedButton2() { DrawPicToHDC(gray_img, IDC_PIC2); } voidCChangeFaceDlg::Facedetect(IplImage *img) { //加载分类器 CascadeClassifier face_cascade; face_cascade.load("haarcascade_frontalface_alt.xml"); Mat gray = Mat (img); face_cascade.detectMultiScale(gray,faces, 1.1, 2, 0|CV_HAAR_SCALE_IMAGE, Size(30, 30) ); char result[200]={0}; sprintf_s(result,"Have found %dfaces",faces.size()); CString str(result); MessageBox(str); //存放两个脸的中心点,仅限两张脸 for(unsigned int i=0;i <faces.size();i++) { rectangle(gray,faces[i],RGB(0,255,0),2); } } voidCChangeFaceDlg::OnBnClickedButton3() { // TODO: 在此添加控件通知处理程序代码 gray = Mat(gray_img); draw = Mat(image); cvtColor(gray,gray,CV_BGR2GRAY); //imshow("gray",gray); //waitKey(0); *gray_img = IplImage(gray); Facedetect(gray_img); } voidCChangeFaceDlg::OnBnClickedButton4() { // TODO: 在此添加控件通知处理程序代码 Point centers[2]; if(faces.size()==2) { int h[2]={0,0};int w[2]={0,0}; Mat tmp_img[2]; for(unsigned int i=0;i <faces.size();i++) { /*rectangle(draw,faces[i],RGB(0,255,0),2);*/ centers[i] =(Point((faces[i].tl().x+faces[i].br().x)/2,(faces[i].tl().y+faces[i].br().y)/2)); h[i] =-faces[i].tl().y+faces[i].br().y; w[i] =-faces[i].tl().x+faces[i].br().x; tmp_img[i] =Mat::zeros(h[i],w[i],draw.type()); } double x1[2],y1[2]; x1[0] = centers[1].x-w[0]/2.0; y1[0] = centers[1].y-h[0]/2.0; x1[1] = centers[0].x-w[1]/2.0; y1[1] = centers[0].y-h[1]/2.0; for(int i=0;i<2;i++) { for(intk=0,n=y1[i];k<tmp_img[i].rows;k++,n++) { for(intl=0,m=x1[i];l<tmp_img[i].cols;l++,m++) { for(intc=0;c<3;c++) { tmp_img[i].at<Vec3b>(k,l)[c]= draw.at<Vec3b>(n,m)[c]; } } } } for(int i=0,j=1;i<2;i++,j--) { for(intk=0,n=y1[i];k<tmp_img[j].rows;k++,n++) { for(intl=0,m=x1[i];l<tmp_img[j].cols;l++,m++) { for(intc=0;c<3;c++) { draw.at<Vec3b>(n,m)[c]= tmp_img[j].at<Vec3b>(k,l)[c]; } } } } /*imshow("one",tmp_img[0]); imshow("two",tmp_img[1]); waitKey(0);*/ *image = IplImage(draw); DrawPicToHDC(image, IDC_PIC2); }else { MessageBox(L"请保证检测到了两张脸"); } }
6. 全部代码、工程已上传待审核