MFC与OpenCV联合打开摄像头
opencv在图像处理和计算机视觉算法方面无疑是非常强大的,但是其GUI界面却是差强人意。当然原因很简单,这不是OpenCV开发人员关注的重点,他们只需要关注算法的实现和优化就行了。GUI方面自有其他的库来代替。
我在之前也曾去学习过MFC和Qt,但都是浅尝辄止,主要原因是:一、它们的学习周期没那么短,不能很快地和OpenCV结合编出程序;二、自己的时间不由自己支配,总是有很多与此无关的任务。所幸,最近的任务需要用到MFC,于是终于有了略显深入的学习。
然而学习了MFC之后,第一个任务仍然是当初学习opencv时的第一个任务——打开一张图片。好吧,虽然听起来很简单,但是真正实现起来确实费了很大一番劲。具体的难处还是随着叙述慢慢展现吧。由于这是《OpenCV实践之路》系列的博客,所以对于其中的MFC的细节可能就不做太过深入的讨论了。仍然是以OpenCV的内容为主。
一、打开文件夹
单纯用opencv都是直接打开一张图片,如果要打开另一张图片还要更改路径或者文件名,比较麻烦。有了界面的好处就是可以直接打开文件夹选择一张图片打开。MFC里打开文件夹获取文件名的程序如下:
CString strFilter = _T("所有文件(*.*)|*.*|");
CFileDialog dlg(TRUE, NULL, NULL, NULL, strFilter, this);
if (!dlg.DoModal() == IDOK)
return;
CString strFileName = dlg.GetPathName();
CFileStatus status;
if (!CFile::GetStatus(strFileName, status))
{
MessageBox(strFileName + "不存在", L"信息提示", MB_OK);
return;
}
这里偷了个懒,直接把文件过滤器写成了所有文件,主要是嫌把各种图片格式列出来太麻烦。而且这里这样也够了,自己打开的肯定是图片文件夹。最后的错误提示,在打开了对话框但是没有选择图片又把对话框关掉的时候会弹出。
二、Mat和CImage相互转换
对怎么在MFC中用OpenCV,各路大神各显神通,有用cvvimage.h和cvvimage.cpp的,也有其他的。我试用了很多种很多次,最后发现这个最好用。可以直接调用函数实现CImage和Mat的相互转换。感谢这位大神提供的方法。实现方式链接在这里:Mat和CImage相互转换
我把这个类命名为MatCImage。Mat和CImage的转换可以用下面来那个函数实现,真的超简单实用:
void MatCImage::MatToCImage(Mat& mat, CImage& cimage)
void MatCImage::CImageToMat(CImage& cimage, Mat& mat)
三、图片适应控件大小
这里用cv::resize()函数来改变图片大小。当然也可以让控件适应图片大小。看自己选择。这个例子是图片适应控件。很简单,直接上代码:
//加载图片到CImage
CImage image;
image.Load(strFileName);
//把CImage转化为Mat
MatCImage MatCImage;
MatCImage.CImageToMat(image, img);
//获取图片宽高比
aspect_ratio = img.rows / img.cols;
if (!img.empty())
{
get_image = true;
}
//获取图片控件的大小
GetDlgItem(IDC_STATIC)->GetClientRect(&pic_rect);
width = pic_rect.right;
height = pic_rect.bottom;
//改变图片大小适应picture控件
resize(img, scaledImage, Size(width, height));
//Mat转换为CImage
if (!scaledImage.empty())
{
MatCImage.MatToCImage(scaledImage, img1);
}
四、图片的显示
以上代码都是按钮的处理函数里面的。而图片的显示是在OnPaint()函数里面。代码如下:
else
{
if (!img1.IsNull())
{
//显示CImage图片
img_show = img1;
CWnd *pWnd = GetDlgItem(IDC_STATIC);//获得pictrue控件窗口的句柄
CDC *pDC = pWnd->GetDC();//获得pictrue控件的DC
if (!img_show.IsNull())
{
img_show.Draw(pDC->m_hDC, pic_rect); //将图片画到Picture控件表示的矩形区域
}
ReleaseDC(pDC);//释放picture控件的DC
}
//不注释掉下面这一句会出现残留等很多问题
//CDialogEx::OnPaint();
}
强调,虽然已经写在注释里面了。最后一行一定要注释掉。不然问题很多,比如改变大小之后原来图片还在那里,会有叠加等各种问题。
五、控件大小适应对话框
也就是处理WM_SIZE消息:
void CMFCImageDlg::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
GetClientRect(&dlg_rect);
GetDlgItem(IDC_BUTTON1)->GetClientRect(&btn_rect);
GetDlgItem(IDC_STATIC)->GetClientRect(&pic_rect);
//设置按钮的位置
btn_rect.MoveToX(dlg_rect.right - 100);
btn_rect.MoveToY(dlg_rect.top + 100);
//设置图片控价的大小
if (get_image == true)
{
pic_rect.right = 3 * dlg_rect.Width() / 4;
pic_rect.bottom = pic_rect.right * aspect_ratio;
}
else
{
pic_rect.right = 3 * dlg_rect.Width() / 4;
pic_rect.bottom = dlg_rect.Height();
}
GetDlgItem(IDC_STATIC)->MoveWindow(pic_rect);
GetDlgItem(IDC_BUTTON1)->MoveWindow(btn_rect);
}
五、效果
界面很简陋,凑合着看,以后一定越来越好。
最近开通了微信公众号,感兴趣的同学可以扫码在微信上交流。