因为一些乱七八糟的原因。。。要把mfc和opencv结合起来使用。。于是有了这篇博客。。
本文亮点在于,无论是IplImage还是Mat格式的图像,咱都能显示。。。。
默认vs安装好,opencv+vs配置好,新建mfc基于对话框程序。。。
第一件事加两个文件CvvImage.h,CvvImage.cpp,因为opencv2.2之后这两个被抛弃了。。。所以咱得手动加回来
如图:
然后在项目头文件下,本文是Opencv_mfcDlg.h中添加头文件:
#include "CvvImage.h" #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp>
然后同样在这个头文件下 COpencv_mfcDlg 对话框类下即:class COpencv_mfcDlg : public CDialogEx{}下添加显示mat的函数声明:
int ShowMat(cv::Mat img, HWND hWndDisplay);//显示Mat
然后在Opencv_mfcDlg.cpp下实现这个函数:
int COpencv_mfcDlg::ShowMat(cv::Mat img, HWND hWndDisplay) { if (img.channels()<3 ) { return -1; } //构造将要显示的Mat版本图片 RECT rect; ::GetClientRect(hWndDisplay, &rect); cv::Mat imgShow( abs(rect.top - rect.bottom), abs(rect.right - rect.left), CV_8UC3 ); resize( img, imgShow, imgShow.size() ); //在控件上显示要用到的CImage类图片 ATL::CImage CI; int w=imgShow.cols;//宽 int h=imgShow.rows;//高 int channels=imgShow.channels();//通道数 CI.Create( w, h, 8*channels); //CI像素的复制 uchar *pS; uchar *pImg=(uchar *)CI.GetBits();//得到CImage数据区地址 int step=CI.GetPitch(); for(int i=0;i<h;i++) { pS=imgShow.ptr<uchar>(i); for(int j=0;j<w;j++) { for(int k=0;k<3;k++) *(pImg+i*step+j*3+k)=pS[j*3+k]; //注意到这里的step不用乘以3 } } //在控件显示图片 HDC dc ; dc = ::GetDC(hWndDisplay); CI.Draw( dc, 0, 0 ); ::ReleaseDC( hWndDisplay, dc); CI.Destroy(); return 0; }
然后搞个控件函数打开图像文件并调用上面的函数显示图像:
void COpencv_mfcDlg::OnBnClickedButton1() { // TODO: 在此添加控件通知处理程序代码 CString FilePath; CFileDialog FileDlg(TRUE); if (IDOK == FileDlg.DoModal()) { //获取FileOpen对话框返回的路径名 FilePath = FileDlg.GetPathName(); //GetPathName返回的是CString类型,要经过转换为string类型才能使用imread打开图片 std::string pathName(FilePath.GetBuffer()); //读取图片 cv::Mat orgImg = cv::imread(pathName); //显示图片 ShowMat(orgImg, GetDlgItem(IDC_STATIC)->GetSafeHwnd()); } }
上面步骤主要针对Mat图像的显示。。
效果如图:
那么问题来了,IplImage怎么办?有些情况下还是要用的。。例如,上任遗留代码。。何况,用mfc有几个不是因为上任对吧。。。。
其实之前添加的两个文件CvvImage.h,CvvImage.cpp就是为了这个作用。。。嗯,同样步骤,在头文件中添加显示函数声明
void DrawPicToHDC(IplImage *img, UINT ID);//显示IplImage
然后在实现文件添加实现:
void COpencv_mfcDlg::DrawPicToHDC(IplImage *img, UINT ID) { CDC *pDC = GetDlgItem(ID)->GetDC(); HDC hDC= pDC->GetSafeHdc(); CRect rect; GetDlgItem(ID)->GetClientRect(&rect); CvvImage cimg; cimg.CopyOf( img ); // 复制图片 cimg.DrawToHDC( hDC, &rect ); // 将图片绘制到显示控件的指定区域内 ReleaseDC( pDC ); }
然后我咱改改OpenImg按键的代码。。
void COpencv_mfcDlg::OnBnClickedButton1() { // TODO: 在此添加控件通知处理程序代码 CString FilePath; CFileDialog FileDlg(TRUE); if (IDOK == FileDlg.DoModal()) { //获取FileOpen对话框返回的路径名 FilePath = FileDlg.GetPathName(); //GetPathName返回的是CString类型,要经过转换为string类型才能使用imread打开图片 std::string pathName(FilePath.GetBuffer()); //读取图片 cv::Mat orgImg = cv::imread(pathName); //显示图片 IplImage * img = &IplImage(orgImg); DrawPicToHDC(img,IDC_STATIC); //ShowMat(orgImg, GetDlgItem(IDC_STATIC)->GetSafeHwnd()); }
其实就是将Mat转为IplImage,然后调用显示Iplmage的函数。。。