OpenCV Mat对象与CImage对象间的数据传输实例

        在用MFC写图像处理程序时,使用OpenCV可以做到事半功倍。但是,如果使用OpenCV4.0或OpenCV4.0以后版本,要显示图像可能会遇到麻烦,因为OpenCV去掉了原有的cvGetWindowHandle()函数,没法再用cvGetWindowHandle()函数来获取OpenCV显示窗口的句柄,也就是说,没法用SetWindowPos()来将OpenCV显示窗口嵌入MFC窗口中。既然OpenCV的显示窗口嵌入MFC窗口比较麻烦,我们可以用MFC来显示图像。有一个ATL/MFC 共享类CImage,该类有多个可用于图像显示的成员函数。OpenCV的图像处理功能强大,可以用OpenCV处理数据,然后从内存中将OpenCV Mat对象的图像数据传送到CImage对象中,用CImage的成员函数来显示图像。如何将OpenCV Mat对象的图像数据传送到CImage对象中呢?下面写一个简单的图像处理程序,来做演示。

        在VS2022中新建一个对话框程序,对话框界面如下:

该对话框的每一个按钮,我都已经给它添加了事件处理函数,暂不去看该函数的代码,不看该程序的代码,先看看该程序的试运行的效果。试运行,结果如下:

点击打开图像,弹出打开对话框:

点击“打开”按钮,结果如下:

试一试,打开别的格式的图像。点击打开图像按钮,选中1.webp

点击对话框中的“打开”按钮,打开图像如下:

点击打开图像按钮,选中2.bmp,

点击对话框中的“打开”按钮,打开图像如下:

 点击打开图像按钮,选中a.jpeg,

点击对话框中的“打开”按钮,打开图像如下:

点击打开图像按钮,选中2.png,

点击对话框中的“打开”按钮,打开图像如下:

可见常见图像格式都可打开。 

 测试一下改变亮度 

为看得更清楚,打开一张更大的图片:

 Step 1  在改变亮度按钮旁的编辑框中输入-50,

点击“改变亮度”按钮,结果如下:

可见图像变暗了。

        Step 2  在改变亮度按钮旁的编辑框中输入30,

点击“改变亮度”按钮,结果如下:

图像明显变亮了,说明改变亮度是有效的。

        测试一下磨皮

        打开一张较粗糙的图片,如下:

这张图像有点小,看不太清楚,把它放大一倍。在“图像比例缩放”按钮旁的输入框中输入2:

再点击“图像比例缩放按钮”,结果如下:

这个脸麻点够多了,需要磨皮一下。点击“磨皮”按钮,结果如下:

再点击两次“磨皮”按钮,结果如下:

再点击4次“磨皮”按钮,结果如下:

 这样看起来好多了,但图像变得有些不清晰,在“增减对比度”按钮旁的按钮中输入1.1,然后再点击“增减对比度”按钮,结果如下:

再点击2次“磨皮”按钮,结果如下:

再点击一次“增减对比度”按钮,结果如下:

在“图像比例缩放”按钮旁的框中输入0.5,点击“图像比例缩放”按钮,结果如下:

        测试转成灰度图

        点击“转成灰度图”按钮,结果如下:

其它图像处理功能就不逐一测试了,要实现这些功能,单用MFC其代码量是很大的,这里由于是用了OpenCV,代码量大大减小,有的仅需几行代码即可实现。下面讨论一下程序代码。

        “打开图像”按钮事件处理函数,代码如下:

void CCImageMatTestDlg::OnBnClickedOpen()
{
	// TODO: 在此添加控件通知处理程序代码
	CFileDialog fdlg(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("All files(*.*)|*.*||"));
	if (fdlg.DoModal() == IDOK)
	{
		m_Path = fdlg.GetPathName();
		m_strEx = fdlg.GetFileExt();
		m_strName = fdlg.GetFileName();

		m_Path.ReleaseBuffer();
		m_strEx.ReleaseBuffer();
		m_strName.ReleaseBuffer();
	
		if(m_strEx == "BMP" || m_strEx == "bmp" || m_strEx == "dib" || m_strEx == "TIF" || m_strEx == "tif" || m_strEx == "tiff" || m_strEx == "PNG" || m_strEx == "png" 
			|| m_strEx == "jpg"|| m_strEx == "JPG"|| m_strEx == "jpe" || m_strEx == "jpeg" || m_strEx == "jp2"  || m_strEx == "webp" || m_strEx == "avif" || m_strEx == "pbm"
			|| m_strEx == "pgm"  || m_strEx == "ppm" || m_strEx == "pxm" || m_strEx == "pnm" || m_strEx == "pfm" || m_strEx == "sr" || m_strEx == "ras" || m_strEx == "exr"
			|| m_strEx == "hdr" || m_strEx == "pic")
		{
			m_str = CT2A(m_Path);
			src = imread(m_str);
			dst = src;
			if (src.empty())
				MessageBox(_T("打开图像文件失败!"));
			else
			{
				MatToCImage(dst, mImage);	//send Mat object data to CImage objiect
				DispalyImage(mImage);		//dispaly image
			}
		}
		else
		{
			MessageBox(_T("你要打开的文件不是本程序支持的图像文件!",MB_OK));
		}
	}
}

其代码量很小,用到了MFC的文件对话框,OpenCV的图像文件读入函数imread(),最后用到了自定义的Mat对象数据传送到CImage 对象函数MatToCImage(),及自定义的图像显示函数DispalyImage()。

        Mat对象数据传送到CImage 对象函数, 其代码如下:

void CCImageMatTestDlg::MatToCImage(Mat& src, CImage& dst) {
	// 确保Mat不为空且是可支持的类型
	if (src.empty() || (src.type() != CV_8UC3 && src.type() != CV_8UC1)) {
		return;
	}
	// 如果CImage对象有附加图像就分离并销毁图像
	if (!dst.IsNull())
		dst.Destroy();

	//创建CImage对象附加图像,需与源图像大小类型一致
	dst.Create(src.cols, src.rows, 8 * src.channels());

	if (src.channels() == 1)
	{
		//将源位图转成八位灰度图时,CImage对象需用到颜色表,需定义一个RGBQUAD数组,并填充该数组
		RGBQUAD* colorTable = new RGBQUAD[256];
		for (int i = 0; i < 256; i++)
		{
			colorTable[i].rgbRed = i;
			colorTable[i].rgbGreen = i;
			colorTable[i].rgbBlue = i;
		}

		//设置颜色表RGB分量值
		dst.SetColorTable(0, 255, colorTable);
	}
	
	int rows = src.rows;
	int cols = src.cols;
	uchar channels = src.channels();
	//内存中的数据传送,注意这里是逐行传送。
	for (int i = 0; i < rows; i++) 
	{
		memcpy(dst.GetPixelAddress(0, i), src.ptr<uchar>(i), cols * channels);
	}
}

该函数有几个重点,第一,要将Mat对象的图像数据传向 CImage对象,必须将CImage对象的原有解绑可以用Destroy()或Detach()函数。第二,同时需创建一张与Mat对象载入图像同样大小的图像用以接收传入数据。第三,创建设置颜色表,如果Mat对象载入的是8位位图(灰度图),可以不创建设置颜色表,如果将Mat对象载入的图像转成灰度图,没有颜色表就没法显示。第四,使用Memcpy()将Mat对象中的图像数据Copy到CImage对象的图像数据存储内存中。下面将MatToCImage(Mat& src, CImage& dst)函数中的颜色表部分注释掉,测试一下效果。注释后的代码如下:

void CCImageMatTestDlg::MatToCImage(Mat& src, CImage& dst) {
	// 确保Mat不为空且是可支持的类型
	if (src.empty() || (src.type() != CV_8UC3 && src.type() != CV_8UC1)) {
		return;
	}
	// 如果CImage对象有附加图像就分离并销毁图像
	if (!dst.IsNull())
		dst.Destroy();

	//创建CImage对象附加图像,需与源图像大小类型一致
	dst.Create(src.cols, src.rows, 8 * src.channels());

	/*
	if (src.channels() == 1)
	{
		//将源位图转成八位灰度图时,CImage对象需用到颜色表,需定义一个RGBQUAD数组,并填充该数组
		RGBQUAD* colorTable = new RGBQUAD[256];
		for (int i = 0; i < 256; i++)
		{
			colorTable[i].rgbRed = i;
			colorTable[i].rgbGreen = i;
			colorTable[i].rgbBlue = i;
		}

		//设置颜色表RGB分量值
		dst.SetColorTable(0, 255, colorTable);
	}
	*/

	int rows = src.rows;
	int cols = src.cols;
	uchar channels = src.channels();
	//内存中的数据传送,注意这里是逐行传送。
	for (int i = 0; i < rows; i++) 
	{
		memcpy(dst.GetPixelAddress(0, i), src.ptr<uchar>(i), cols * channels);
	}
}

试运行,打开图像,结果如下:

     可正常显示,点击“转成灰度图”按钮,结果如下:

没法正常显示图像。打开一张灰度图:

结果如下: 

点击“转成灰度图”按钮,结果如下:

同样没法显示。

        图像显示函数 其代码如下:

void CCImageMatTestDlg::DispalyImage(CImage mImage)
{
	// TODO: 在此处添加实现代码.
	if (mImage.IsNull())
		MessageBox(L"No Image to Display!",L"系统提示",MB_ICONWARNING | MB_OK);
	else
	{
		//
		Invalidate();
		OnPaint();

		CClientDC dc(this);
		mImage.BitBlt(dc.GetSafeHdc(), 0, 0, SRCCOPY);
	}
}

该函数使用了BitBlt()函数来显示图像,在前面调用了Invalidate()及OnPaint()函数,目的是刷新窗口,避免图像重叠。

        上面每一个图像处理的函数都调用了MatToCImage()函数与DispalyImage()函数,其它函数与本主题无关这里就不逐一解说了。该示例的源代码已上传到CSDN,如果要查看其源代码可以去下载。示例程序是基于VS2022及OpenCV4.8,在Win10 平台调试通过,OpenCV4.8放在D盘下。示例程序的源代码的下载链接为:https://download.csdn.net/download/billliu66/89248781

  • 31
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bill66

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值