【MFC】把一张500万像素的图片,拷贝进内存DC里,哪种方法速度比较快?

VergilYe 2017-08-19 12:04:41
加精
我目前要实现的功能:从一个500万的相机采集图像,并显示在窗口上。

我的方法:
1.目前我是用的CImage类,将500万的图片放进CImage的对象中
2.在OPaint函数中,使用CImage.Darw函数拷贝到内存DC里,DestRect和SourceRect的宽高都是图片Size:2592*1944。
3.将内存DC拷贝到窗口DC上。

问题:
可是我发现光是第二步CImage.Darw函数都要花了将近60ms时间。(低配工业电脑)
相当于每秒才16帧的速度。
本来,我以为是电脑配置太低造成的,无法解决这个问题。
然而,在同一台电脑上,我用相机厂商自带的软件进行相机连接,帧率居然可以达到30帧每秒以上。
那就说明,这是我个人技术不足导致的问题,并非电脑低配的问题。

请问,各位大神,能不能告诉我,还有没有其他更快的方法?
另外,我的方法,在我的电脑上测试,CImage.Darw函数只需要11ms。

也就是说,在我的电脑上,必须将速度提高至6ms左右完成,才可以实现在低配电脑上达到30fps。

附上我的电脑配置:




...全文
4903 74 打赏 收藏 转发到动态 举报
写回复
用AI写文章
74 条回复
切换为时间正序
请发表友善的回复…
发表回复
VergilYe 2017-09-21
  • 打赏
  • 举报
回复
引用 73 楼 weizehua 的回复:
看了一下大家的描述。发表一下我个人的看法。 我只略微涉猎一点图像处理相关的东西,说的不对请见谅。 我个人觉得,如果Image.Draw耗时60ms,你就能达到16fps的帧率,那么使用同样的技术你的代码已经没有进一步优化的可能性了。 因为耗时主要消耗在从DIB拷贝到DDB的过程中,也就是在Image.Draw这个函数中。Image.Draw使用的是GDI绘图,只吃CPU。系统底层也做了非常详尽的性能优化,可以认为没有其他手断做代码层的优化了。 如果想进一步提升绘制的效率,可以考虑: 1、使用多线程绘图(前提是你的CPU是多核的): 我猜得没错的话,Image.Draw使用的应该是单线程绘图,你可以把代码嵌进Paraller.For,实现多线程绘图。 这里要注意的一点是,如果你直接用Image.Draw把图像绘到显示器,因为每个线程完成的时间不同步,图像可能会出问题。我建议你先把图像绘制到一个设备有关DC(DDC,或者说MemoryCompatibleDC)里,再从DDC拷贝到显示器。 语言层面上面可能还要注意的就是,C#的图像对象在不同线程里同时访问,会不会出错~~这个我就不懂了,得靠你自己解决了。 2、使用显卡绘图: 内存中存储的图像一般是RGB格式,而显示器显示的图像使用的一般不是这种格式。在绘图之前必须转换格式。绘图之中最耗时的部分就是这一部分。Image.Draw使用的是GDI绘图,不会使用硬件加速,完全用CPU来实现转换,效率比较低。你换成硬件绘图就好了。具体代码我不懂,网上搜索一下吧。
感谢你在结贴后,还花费时间回答我的问题。 学习了!感谢你的回答!
Happy0403 2017-09-14
  • 打赏
  • 举报
回复
看了一下大家的描述。发表一下我个人的看法。 我只略微涉猎一点图像处理相关的东西,说的不对请见谅。 我个人觉得,如果Image.Draw耗时60ms,你就能达到16fps的帧率,那么使用同样的技术你的代码已经没有进一步优化的可能性了。 因为耗时主要消耗在从DIB拷贝到DDB的过程中,也就是在Image.Draw这个函数中。Image.Draw使用的是GDI绘图,只吃CPU。系统底层也做了非常详尽的性能优化,可以认为没有其他手断做代码层的优化了。 如果想进一步提升绘制的效率,可以考虑: 1、使用多线程绘图(前提是你的CPU是多核的): 我猜得没错的话,Image.Draw使用的应该是单线程绘图,你可以把代码嵌进Paraller.For,实现多线程绘图。 这里要注意的一点是,如果你直接用Image.Draw把图像绘到显示器,因为每个线程完成的时间不同步,图像可能会出问题。我建议你先把图像绘制到一个设备有关DC(DDC,或者说MemoryCompatibleDC)里,再从DDC拷贝到显示器。 语言层面上面可能还要注意的就是,C#的图像对象在不同线程里同时访问,会不会出错~~这个我就不懂了,得靠你自己解决了。 2、使用显卡绘图: 内存中存储的图像一般是RGB格式,而显示器显示的图像使用的一般不是这种格式。在绘图之前必须转换格式。绘图之中最耗时的部分就是这一部分。Image.Draw使用的是GDI绘图,不会使用硬件加速,完全用CPU来实现转换,效率比较低。你换成硬件绘图就好了。具体代码我不懂,网上搜索一下吧。
qq_40196638 2017-09-11
  • 打赏
  • 举报
回复
我来学习的 嘿嘿
weixin_38016399 2017-09-06
  • 打赏
  • 举报
回复
learning
VergilYe 2017-09-06
  • 打赏
  • 举报
回复
结贴了结贴了!!! 目前,优化差不多已经完成了。 我先总结一下问题点: 1.由于厂商给的软件输出的是8bit图像,而我这边的需求是,输出的是24bit(因为要绘制有颜色的图形),所以比较慢是正常的。 2.影像拷贝到内存DC里的动作可以通过消息响应,在OnPaint外面触发,这样的话,OnPaint就只剩两个动作: 1)内存DC里绘制图形 2)内存DC拷贝到窗口DC 消息响应可以放到相机采集的动作里。 3.这是最关键的一个点:相机的采集模式,从单帧采集改为连续采集。 这个是公司前辈改的,我也不是很清楚这部分。 按照我大概的理解,相机的采集动作有如下几步: 1)启动相机 2)采集图片 3)关闭相机 单帧采集的动作每次都需要重复1-3步。 连续采集的动作只需要在采集时执行第二步即可,不需要频繁启动相机。 经过上面几点的优化后,目前在我的电脑上OnPaint绘制24bit图像大概在30ms左右。 结合相机采集动作,整个流程从200-240ms优化至100ms-120ms左右,也就是说目前FPS在8-10左右。 而厂商的软件输出8bit图像,FPS为14左右。 最终效果在肉眼看来,至少不会有太大的延迟,这次的优化暂时可以结束了,剩下估计就只剩下细节上的提升了。 虽然最终由点偏题了,但是还是感谢各位的回答! 分数比较少,就平分给 我认为对我有帮助的前四名。 谢谢!
笨笨D幸福 2017-08-31
  • 打赏
  • 举报
回复
如果想要高FPS的摄像头数据,只能使用DirectShow,捕获摄像头流,只要硬件足够,非常好的。
gz_qmc 2017-08-31
  • 打赏
  • 举报
回复
引用 62 楼 VergilYe 的回复:
[quote=引用 53 楼 gz_qmc 的回复:] 1 通常情况,DC装载和内存拷贝速度差不多的, 区别在于,如果用DC装载,你每次更新都要装载,你是浪费了显示的时间去做装载数据的过程, 非常不合理,其结果是不该卡的也卡了,因为显示需要等待装载. 如果不用DC装载,那么数据装载准备的过程根本和显示没有关系.再发生卡已经是硬件的限制了,和你无关了 你看看这两个区别 for(int=0;i<10000;i++) { 创建DC 装载解压数据到DC; //本来应该是其他线程或者事先完成的,在这里来执行,你觉得有多大几会会卡呢? DC拷贝到目标DC } for(int=0;i<10000;i++) { 数据拷贝到目标DC } 2 关于BMP和PCX等很普通的图片,网络有很多成熟而简单的源代码 至于JPG,PNG等稍微复杂的,可以用一些现成的库 CxImage是一个公开源代码的库,功能超级强,可以直接用也可以借鉴.
这位老师你好,根据你的建议。 今天我突发奇想,想用CImage中的DC(HDC)来代替MemDC(CDC),这样的话,就省去了把CImage拷贝到MemDC中的时间。 速度是快了将近一倍,然而却遇到如下一些状况: 1.通过GDI+绘制出来的图形是灰色的。 2.程式关闭后,出现报错。 如下代码: OPaint中:

//CImage 中的HDC转换为CDC
CDC *pImageDC = CDC::FromHandle(m_DibImage.GetDC());

//使用GID+绘制图形到pImageDC中。
//......

//拷贝到窗口DC
	dc.SetStretchBltMode(STRETCH_DELETESCANS);
	dc.StretchBlt(
		0,			//选择在目标DC什么位置(X坐标)开始显示
		0,			//选择在目标DC什么位置(Y坐标)开始显示
		m_rcClient.Width(),		//选择在目标DC显示的宽度
		m_rcClient.Height(),	//选择在目标DC显示的高度
		pImageDC,	//源DC
		0,						//截取源DC的区域,起始X坐标
		0,						//截取源DC的区域,起始Y坐标
		nPicWidth,		//截取源DC的宽度
		nPicHeight,		//截取源DC的高度
		SRCCOPY);				//拷贝的模式
是我的做法不对吗? 还是这种做法是不允许的? 报错感觉像是DC中的内存越界? [/quote] 这充分证明, CImage类的Draw涵数本身带了相当一部分处理数据功能 也就是说,你一开始装载的图像并没有彻底处理好 也就是说要等画的时候在处理, 也就是说那一部分处理的代码耽搁了一倍的时间 所以,你直接使用DC是一定有问题的 即使用对了,因为没有经过Draw,结果也只是不完整的 所以不建议用CImage处理这种大图片 用CxImage,网上下一个,CSDN也有,免费的,顺便把使用说明也下来看看就会用了 现炒现卖,很适合你的习惯
VergilYe 2017-08-31
  • 打赏
  • 举报
回复
引用 67 楼 xiaoyilong19 的回复:
上面的笨笨D幸福也说过了,这是可行的,只是要楼主下点力气先学习了
好吧,看来这是唯一的退路了 其实我不是不想去用DirectShow,主要我现在是野路子出家,项目也比较赶,这只是其中一小块功能,其他功能部分也是需要我来做,只怕系统的学习是赶不上啊。。。
xiaoyilong19 2017-08-31
  • 打赏
  • 举报
回复
上面的笨笨D幸福也说过了,这是可行的,只是要楼主下点力气先学习了
xiaoyilong19 2017-08-31
  • 打赏
  • 举报
回复
楼主的疑惑是我好几年前的了,后来系统学习了dshow,然后用它改写了代码,就没有这个问题了,而且兼容性广
xiaoyilong19 2017-08-31
  • 打赏
  • 举报
回复
我看了一下,楼主的目的不就是实时显示一个摄像头的数据么,directshow绝对快,而且简单方便,里面自动优化过了,我尝试过1980*1020的分辨率采集,刷新很快
qq_20836163 2017-08-31
  • 打赏
  • 举报
回复
hhhhhhhhhhhhhhhhhhhhhhh好好
qq_40037082 2017-08-31
  • 打赏
  • 举报
回复
kkkkkkkkkkkkkkkkk
VergilYe 2017-08-31
  • 打赏
  • 举报
回复
引用 53 楼 gz_qmc 的回复:
1 通常情况,DC装载和内存拷贝速度差不多的,
区别在于,如果用DC装载,你每次更新都要装载,你是浪费了显示的时间去做装载数据的过程,
非常不合理,其结果是不该卡的也卡了,因为显示需要等待装载.
如果不用DC装载,那么数据装载准备的过程根本和显示没有关系.再发生卡已经是硬件的限制了,和你无关了

你看看这两个区别
for(int=0;i<10000;i++)
{
创建DC
装载解压数据到DC; //本来应该是其他线程或者事先完成的,在这里来执行,你觉得有多大几会会卡呢?
DC拷贝到目标DC
}
for(int=0;i<10000;i++)
{
数据拷贝到目标DC
}

2 关于BMP和PCX等很普通的图片,网络有很多成熟而简单的源代码
至于JPG,PNG等稍微复杂的,可以用一些现成的库
CxImage是一个公开源代码的库,功能超级强,可以直接用也可以借鉴.


这位老师你好,根据你的建议。
今天我突发奇想,想用CImage中的DC(HDC)来代替MemDC(CDC),这样的话,就省去了把CImage拷贝到MemDC中的时间。
速度是快了将近一倍,然而却遇到如下一些状况:
1.通过GDI+绘制出来的图形是灰色的。
2.程式关闭后,出现报错。


如下代码:
OPaint中:

//CImage 中的HDC转换为CDC
CDC *pImageDC = CDC::FromHandle(m_DibImage.GetDC());

//使用GID+绘制图形到pImageDC中。
//......

//拷贝到窗口DC
dc.SetStretchBltMode(STRETCH_DELETESCANS);
dc.StretchBlt(
0, //选择在目标DC什么位置(X坐标)开始显示
0, //选择在目标DC什么位置(Y坐标)开始显示
m_rcClient.Width(), //选择在目标DC显示的宽度
m_rcClient.Height(), //选择在目标DC显示的高度
pImageDC, //源DC
0, //截取源DC的区域,起始X坐标
0, //截取源DC的区域,起始Y坐标
nPicWidth, //截取源DC的宽度
nPicHeight, //截取源DC的高度
SRCCOPY); //拷贝的模式



是我的做法不对吗?
还是这种做法是不允许的?
报错感觉像是DC中的内存越界?


VergilYe 2017-08-30
  • 打赏
  • 举报
回复
引用 57 楼 jsxyhelu2015 的回复:
“相机厂商”是否有提供支持? 我认为500w的实时显示应该不成问题的,一般来说我们还要加图像处理的。 有无尝试过 gomfctemp2? http://www.cnblogs.com/jsxyhelu/p/GOMFCTemplate2.html
引用 58 楼 cvbtvbwu 的回复:
是JPG图像数据么?是的话不同的转换方式不同速度,我是用GlobalAlloc CreateStreamOnHGlobal之类生成Image的 另外我是用GDI+显示图片的,它有个SetInterpolationMode设置显示质量的,低的话速度快。 还有显示速度比接收图片速度慢,你就要考虑改用线程去做
抱歉,两位!最近在忙这个项目的其他部分,这个问题先放下,待其他部分优化完,再进行回顾。 你们指导的方式,我已经记下来了,感谢! 我打算暂时先不结贴,大家有新的建议可以继续留言,我也会持续关注这个帖子。 我会尽快结束我其他的工作,投入到这块优化当中。 再次感谢大家!
nettman 2017-08-29
  • 打赏
  • 举报
回复
叶恭介叶恭介 2017-08-29
  • 打赏
  • 举报
回复
是JPG图像数据么?是的话不同的转换方式不同速度,我是用GlobalAlloc CreateStreamOnHGlobal之类生成Image的 另外我是用GDI+显示图片的,它有个SetInterpolationMode设置显示质量的,低的话速度快。 还有显示速度比接收图片速度慢,你就要考虑改用线程去做
weixin_38786036 2017-08-28
  • 打赏
  • 举报
回复
学习了,赞赞赞!
jsxyhelu2015 2017-08-28
  • 打赏
  • 举报
回复
“相机厂商”是否有提供支持? 我认为500w的实时显示应该不成问题的,一般来说我们还要加图像处理的。 有无尝试过 gomfctemp2? http://www.cnblogs.com/jsxyhelu/p/GOMFCTemplate2.html
weixin_40003585 2017-08-28
  • 打赏
  • 举报
回复
高手区的高手!
加载更多回复(53)

19,468

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 图形处理/算法
社区管理员
  • 图形处理/算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧