随笔 - 62  文章 - 73  评论 - 592 

      开发平台:VS 2008,Windows xp sp2

      效果截图:

 


图1 软件界面图
 

图2 差异结果图

      图片说明:第一幅图展示的是我所实现的这个软件的最终效果图,通过菜单栏的按键可以选择两幅有差异的图片(要求是相同大小,24位或以上颜色,目的是模仿系统屏幕截图),然后对图片进行比较后,就会截取出差异部分,并将该图片保存到E盘根目录,也就是上面的差异结果图。

 

      最近项目中要实现屏幕传输和控制功能,其中主要的就是屏幕传输功能。“不就是屏幕传输嘛,简单呀~~建立个线程,隔一定时间截取一下屏幕的图片,然后发送给对方不就成了。”这是我最初的想法,我也按照这个办法做了,当看到屏幕从一台电脑成功传到了另一台电脑时(局域网内),我有点高兴,心里暗想“原来远程控制也不过如此”。当然,事情的发展并不如预期中那样完美,事实上,我所实现的远程控制几乎没有任何价值,因为延迟太大,一副屏幕的截图从截取到发送到再显示出来,整个过程至少要耗费2秒多的时间。这是什么概念呢?我们可以比较一下,标准的卡通片是24fps,也就是每秒24帧,这样才会流畅;如果你玩过极品飞车,会发现极品飞车一般的帧数会保持在40以上(之所以说一般,是因为帧数和具体的显卡有关)。而现在我所实现的这个帧数是0.5,极品飞车的帧数是我的80倍,当你玩极品飞车的时候,画面每2秒才刷新一次,你就会知道我的实现有多烂了。不过这些帧数都是对流畅性要求极高的环境来说的,对于远程控制这种每秒画面变化不大的情况,就没必要这么高要求了,只要能保持在6帧每秒就几乎可以达到流畅的感受了。

     屏幕传输主要分为“截图->压缩->传输->解压->显示”这几个步骤,传输、压缩、解压及显示几乎很难有明显的性能提升,因此主要能改进的就是“截图”这个过程。单纯的全屏截图,我尝试过最快每秒也就7、8帧,无法实现更高的速度。(我截屏利用的GDI+方式,而并非是用DirectX来做,DirectX更能发挥显卡的性能,但是我没有尝试过。)7、8帧的速度是不是可以了呢?这是不够的,因为这只是单纯的截图,并没有加上网络传输、压缩解压等,因此必须要有更快的速度。而且,此种方式,每秒需要传输的数据量是相当惊人的。拿我的电脑1280*800的分辨率来说,按24位颜色质量存储的话,一张图需要2.9M,按每秒7帧来算需要20.3M。每秒需要传输20.3M,这对于现有的网络带宽来说是承受不了的。

      翻阅了很多网上达人的文章,发现通常用于屏幕传输的方法主要是传输前后两幅图片的不同之处。而查找不同之处的算法又主要有两类:隔行查找和分块比较。翻阅了很多,主要是以Delphi下的实现为主,尤其以DG老大为代表的实现方式。由于本人从未用过Delphi,对C++也有多年未用了,因此在研究DG老大的代码(DGScreenSpy_0.2c)上着实花了不少时间,现在也只能算是懂了个基本。在我自己的实现方式,并非只是简单的对DG老大代码的翻译,根据我自己的理解,我的算法思路如下:

      首先对两张图片的数据按照从上到下从左到右的方式进行扫描,考虑到在实际的环境下,一个图标或一个窗口肯定会横向和纵向跨好几个像素,因此用了两个参数来分别表示步进值。扫描的过程中用一个矩形区域来表示当前变化的范围,这个范围在整个扫描过程中会随时进行调整以适应变化的区域。

      下面举个例子来说明区域的设置:从第0行开始扫描,没有发现该行内的像素不同,加上步进的数值后(假如是3),扫描接着从第4(3+1)行开始,在该行的扫描中发现第2个像素到第10个像素(基数为0)都不同,于是设置矩形的Top边为4,Left为2,Right为11,Bottom为5。接着扫描第8行,发现这一行中第0个像素到第8个像素不一样,则调整矩形的区域(Top:4,Left:0,Right:11,Bottom:9),依次类推直到扫描到一行全都相同或扫描完最后一行时,这个矩形的区域则正式确定。这样该区域的图形就是最小的变化范围。

      这是大概的思路,具体的大家可以参考我的代码。按照这种方式,可以获取变化的最小区域,如果变化不多,则图片数据量明显变少,如果变化很多或干脆整个屏幕都不一样,那图片数据量仍然会很大,因此还需要进一步对图片数据进行压缩或采取降低图片颜色质量的方式,QQ的远程屏幕传输就降低了图片的质量,估计其图片质量为16位每像素。

      为了专门研究图片差异的获取,所以并没有实现其他诸如压缩、传输的功能。其中对像素的比较用的是BitmapData.Scan0来获取像素指针的方式进行比较,因为这样比较速度最快。我在测试时发现,我的这种实现方式(以我设置的步进值为基准)一次比较需要0.15秒左右(包括了存储差异部分到本地文件的时间,大约耗时0.04秒),速度上仍然有待提升。改变步进值可以适当的调节速度,但也会影响到变化区域的准确性。

     本着合作共赢的想法,提供我的代码供大家参考,希望大家在看了我的思路和代码后,能提出宝贵的意见,我会根据您的意见思路对代码进行不断改进,每一次重大改进我都会把代码发布出来。希望能早日完成一款Dot Net下的远程控制佳作。以下是核心代码:

 

FindDifferences

 项目打包下载:http://files.cnblogs.com/stg609/Pic.rar

 参考:http://iamgyg.blog.163.com/

 

作者:stg609
出处:http://stg609.cnblogs.com/ 、http://itisland.cn/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 

posted on 2009-11-28 21:48 stg609 阅读(3372) 评论(26) 编辑 收藏

 回复 引用 查看   
#1楼 2009-11-28 22:26 | 偶卖糕的      
建议使用支持vnc协议的方式,这样更通用
 回复 引用 查看   
#2楼 2009-11-28 22:54 |       
图像差分吧。曾经想过,不过现在没有这个需求。

也许在对比差分上面可以用一些复杂的算法。比如

神经网络、特征值、特征向量、小波分析。。。哈哈

 回复 引用 查看   
#3楼 2009-11-28 23:03 | breeze      
目前还没有软件能支持DirectX模式下的远程图像传输,你拿这个来做比较,你做出来的话,那......
 回复 引用 查看   
#4楼 2009-11-28 23:28 | lcs-帅      
这东东不错。以前也只是想过,从来都没有实际作过。支持一下。
 回复 引用 查看   
#5楼 2009-11-29 07:38 | 徐少侠      
同顶
和lcs-帅一样,都了解过,不过从来没仔细去实现过

祝贺一下先

理解了一下你的代码,似乎是把图片预先分割成一定尺寸的小块,然后逐一比较小块的相同.这样可以更快得去完成整个图片的比较,而不是逐像素完成.

 回复 引用 查看   
#6楼 2009-11-29 08:58 | cnyao      
可以看一下UltraVNC的源码,其实我认为它已经做得很好了,主要就是有两点需要改进的地方,一个是只支持公网IP,但是这个就涉及到P2P,打洞,或者服务器中转;还有一个就是如果远程机器屏幕分辨率和自己机器不同的话,就会出现滚动条,不是很爽。其他都很好。
 回复 引用 查看   
#7楼 2009-11-29 10:30 | dongzz      
TM2009的远程协助在双方分辨率不同时会花屏,你们遇到过没?腾讯咋不解决下呢
 回复 引用 查看   
#8楼 2009-11-29 11:19 | 风海迷沙      
QQ都实现高清了。
 回复 引用   
#9楼 2009-11-29 13:11 | www.***[未注册用户]
从此网ASP.NET学习网:www.***
 回复 引用 查看   
#10楼 2009-11-29 14:07 | Ivony...      
很好,稍微改改就成了找茬外挂。。。。
 回复 引用 查看   
#11楼[楼主] 2009-11-29 14:20 | stg609      
谢谢大家的支持

@徐少侠
谢谢你的支持,你的理解和我的思路有点出入,我并没有分小块进行比较,我也是逐个像素的比较,只是并不是每行每列都比较。
@cnyao
UltraVNC的源码并没有看过,因为实在不知道这个从何下手,看了下它的介绍,似乎在2000、XP上使用的是Mirror Driver。
@dongzz
是因为分辨率的关系吗?我现在的QQ2009 sp4和其它电脑上的QQ远程时就会出现花屏
@风海迷沙
都高清了??是哪个版本的?

 回复 引用 查看   
#12楼[楼主] 2009-11-29 14:36 | stg609      
Mirror Driver这种速度应该是最快了,但是因为开发难度比起GDI+这种方式更难,所以目前并不打算使用
 回复 引用 查看   
#14楼[楼主] 2009-11-29 18:32 | stg609      
@灵感之源
赎我愚昧,vncsharp是不是只是一个用于接收远程控制的图片和命令等的客户端,而没有发送屏幕的服务端?

 回复 引用 查看   
#15楼 2009-11-29 19:03 | GreySky      
@dongzz
QQ2009也是这样的

 回复 引用 查看   
#16楼 2009-11-30 13:47 | niukun      
看了楼主的文章让我想起以前写的代码
类似的我也研究过,当时是仿照了vnc的方法,搞一个mirrordriver,直接从mirror 队列里拿到差异矩形,然后做合并在做拆分。这样就可以得到单位时间最小变化矩形。
还有一种方式,直接拿到整个屏幕图像,用screen encoder压缩一下就可以了,不断的压缩,远程不断的解压缩,数据量很小,屏幕不变化数据基本就是0。不过这个是com组件,可以用c++调用一下,然后c#调用c++直接拿到压缩的数据。
然后就是网络传输了。
楼主还有很长的路要走呀。

 回复 引用 查看   
#17楼[楼主] 2009-12-01 11:39 | stg609      
@niukun
谢谢你的指点,确实还需要走很长的路。

 回复 引用 查看   
#18楼 2009-12-01 23:28 | 逍遥海盗船      
本来也想写个这个东东,后面没有写下去。
 回复 引用 查看   
#19楼 2009-12-10 09:24 | Old      
支持一下楼主
 回复 引用 查看   
#20楼 2009-12-10 10:00 | answer      
比较结果有问题,测试如下图:

 回复 引用   
#21楼 2009-12-10 14:51 | kardon[未注册用户]
以前做个类市的项目.直接把bitmap专成jpg就比较小了.
 回复 引用 查看   
#22楼[楼主] 2009-12-10 18:12 | stg609      
@answer
如果使用默认的步进值,的确是有问题,如果把步进值调整的小一点,就会效果好了。

@kardon
可能会采用jpg格式,不过jpg格式对文字的压缩效果不是很好,比较模糊。

 回复 引用 查看   
#23楼 2009-12-11 18:41 | 万仓一黍      
提一个思路
 回复 引用 查看   
#24楼 2009-12-11 18:44 | 万仓一黍      
首先将前后两幅图片进行XOR运算得到差值矩阵,由于图片变化的部分不多。因此得到的矩阵大多数以0为主。这样进行压缩,一是速度快,二是压缩率很高。再传输到远程机上。在远程机上用XOR运算回转。看行不行
 回复 引用 查看   
#25楼[楼主] 2009-12-11 20:29 | stg609      
@万仓一黍
谢谢你的提议,这是个不错的办法。会考虑采用。

 回复 引用 查看   
#26楼 2011-02-13 11:56 | 杨锦龙      
强悍,支持