openCV是一个专业的开源图像处理和显示库,用起来非常方便,但界面设计功能一般。OpenCV与MFC结合进行图像显示是一个不错的选择。

OpenCV与MFC结合绘图的方法主要有:

1)将OpenCV窗口附着到MFC窗口

基本方法是,独立创建openCV显示窗口,并在MFC的窗口界面上设置一个与之对应的绘图控件,设置该控件为opwnCV显示窗口的父窗口,并使它们的位置、大小相同。代码如下:

namedWindow(“myview”, CV_WINDOW_NORMAL);//设置窗口名
	HWND m_wincv = (HWND)cvGetWindowHandle(“myview”);//获取opencv窗口句柄
	HWND hParent1 = ::GetParent(m_wincv);//取窗口的父窗口句柄
        m_disp = GetDlgItem(IDC_DISPLAY);  // IDC_DISPLAY为对应的MFC窗口绘图控件IDC值
	HWND hw = ::SetParent(m_wincv, m_disp->m_hWnd); // 设置新的父窗口句柄
	::ShowWindow(hParent1, SW_HIDE);//隐葳原openCV窗口的边框

当窗口大小需要改变时,要同时更新MFC和OpenCV的窗口大小,代码如下:

CRect rect;
	rect.left = 0;
	rect.top = 0;
	rect.right = 640;
	rect.bottom = 480;
	m_disp->MoveWindow(&rect);
	cv::resizeWindow(“myview”, rect.Width(), rect.Height());

这种方法的优点是步骤比较简单,图像可以直接用openCV显示,一般情况下推荐使用这种方式。缺点是,绘图是在openCV窗口进行的,窗口事件的响应是按openCV的机制实现的。这在某些需要处理窗口事件场合会不太方便。

2)利用CImage

这种方法的基本思路是,先把openCV的Mat转化为CImage,然后在MFC的图像控件上显示。Mat转化为CImage的代码如下:(特别说明,这段代码是从网上下载的,在此对原作者表示感谢。因时间有点久了,记不起链接,抱歉!)

int Mat2CImage(Mat *mat, CImage &img)
{
	if (!mat || mat->empty())
		return -1;
	int nBPP = mat->channels() * 8;
	img.Create(mat->cols, mat->rows, nBPP);
	if (nBPP == 8)
	{
		static RGBQUAD pRGB[256];
		for (int i = 0; i < 256; i++)
			pRGB[i].rgbBlue = pRGB[i].rgbGreen = pRGB[i].rgbRed = i;
		img.SetColorTable(0, 256, pRGB);
	}
	uchar* psrc = mat->data;
	uchar* pdst = (uchar*)img.GetBits();
	int imgPitch = img.GetPitch();
	for (int y = 0; y < mat->rows; y++)
	{
		memcpy(pdst, psrc, mat->cols*mat->channels());//mat->step is incorrect for those images created by roi (sub-images!)
		psrc += mat->step;
		pdst += imgPitch;
	}

	return 0;
}

在需要显示图像之处,调用如下代码显示:

CImage imgDst;
			Mat2CImage(&frame, imgDst);  // Mat转化为 CImage
			dlg->GetClientRect(&rect);  // dlg为MFC图像控件的指什
			imgDst.Draw(dlg->GetDC()->GetSafeHdc(), rect);

这种方法的缺点是,图像大小变化时,图像质量不是太好,可能与转换算法有关。

3)自行转化为GDI图像并在MFC窗口显示

基本思路是先转化为GID图像,再显示。代码如下:

void showMatImgToWnd(CWnd* pictureWnd, const cv::Mat& disimg)
{
	if (disimg.empty())
    	    return;
	static BITMAPINFO *bitMapinfo = NULL;
	static bool First = TRUE;
	if (First)
	{
		BYTE *bitBuffer = new BYTE[40 + 4 * 256];//开辟一个内存区域
		if (bitBuffer == NULL)
		{
			return;
		}
		First = FALSE;
		memset(bitBuffer, 0, 40 + 4 * 256);
		bitMapinfo = (BITMAPINFO *)bitBuffer;
		bitMapinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		bitMapinfo->bmiHeader.biPlanes = 1;
		for (int i = 0; i < 256; i++)
		{ //颜色的取值范围 (0-255)
			bitMapinfo->bmiColors[i].rgbBlue = bitMapinfo->bmiColors[i].rgbGreen = bitMapinfo->bmiColors[i].rgbRed = (BYTE)i;
		}
	}
	bitMapinfo->bmiHeader.biHeight = -disimg.rows;
	bitMapinfo->bmiHeader.biWidth = disimg.cols;
	bitMapinfo->bmiHeader.biBitCount = disimg.channels() * 8;

	CRect drect;
	pictureWnd->GetClientRect(drect);    //pWnd指向CWnd类的一个指针 
	CClientDC dc(pictureWnd);
	HDC hDC = dc.GetSafeHdc();                  //HDC是Windows的一种数据类型,是设备描述句柄;
	SetStretchBltMode(hDC, COLORONCOLOR);
	StretchDIBits(hDC,
		0,
		0,
		drect.right,  //显示窗口宽度
		drect.bottom,  //显示窗口高度
		0,
		0,
		disimg.cols,     //图像宽度
		disimg.rows,     //图像高度
		disimg.data,
		bitMapinfo,
		DIB_RGB_COLORS,
		SRCCOPY);
}

需要显示时直接调用以下代码即可:

win = GetDlgItem(IDC_DISPLAY);  // 取图像控件指针
showMatImgToWnd(win, frame); // 显示

这这种方式缺点是需要自行转换和显示。