100 分求远程屏幕监控好点的算法思想和源码(100分)

  • 主题发起人 主题发起人 zbdbx
  • 开始时间 开始时间
讲讲原理吧
 
速度最快是RADMIN吧 特别是在远程鼠标控制方面 更加突出
 
看起来蛮高深哦~ 好快!
 
以前做过 处理流程是二值化->判断变化点的个数->发送
效果不是很理想,用的是UDP发送的 不知道是不是丢包了 经常会出现马赛克的.
 
嗯.我目前所知道最快的是Radmin.所以那个录像是专门和它进行比较的:)主要比较大区域变化和小区域---这已经覆盖了变化的范围了.不过忘记把radmin那个显示每秒钟多少祯的菜单打开了.不过应该是40~80帧/秒左右(30帧/秒对人眼已经是动画效果了),如果是局部网,速度就更加不用说了.估计它官方公布的测试数据应该是局部网的.
另外,QQ的远程协助速度并不快,而且非常占用CPU.我前几个月和别人测试过,QQ的延迟厉害,那边收到消息了,但是屏幕看到的是字还没打完.而我们的DEMO对方说感觉速度上跟操作自己的电脑没区别.
 
我今天试了下Radmin 它的截图好像是8位的/面且是无损的/

还有我发现jpeg虽然作为有损压缩,但是它的压缩率是最为稳定的
 
jingtao兄,

你的Demo不能下
http://www.138soft.com/GDIDemo.exe
 
Sorry,能下,看看
 
jingtao兄:

看了你的Demo,真是让人感叹呀!

认真看了几遍,发现你的屏幕和RA的屏幕相比,桌面上的图标有时明显有一个小方块,好像是经过压缩过后的图块,难道程序也是传输变化图块?把颜色分布较多的作为背景,这可能就是你说的比较大区域变化和小区域变化吧//
可我还是不清楚原理,到底是怎么实现的了>能给我们大伙讲讲思路,让我们也长长见识吗?
 
说老实话,这份源码谁看了的都心动,虽然我还没有想好用它来做什么

jingtao兄能拿出来做奖励的话,对你应该不是商业秘密。

但愿在大家聊过后能让我们长长见识。

猜我是没办法猜出来的了。

碰碰运气那天你心情好了能公开,^_^
 
zbdbx:
那个"明显有一个小方块"的原因应该是那个录像软件的导致的.实际使用中并没有看到小方块.你可以仔细观察,我在改变RA的颜色深度后,刷新时也有小方块.另外,刚开始录像的时候,本地桌面也有小方块.另外,估计也刷新也有关心,我的客户端只刷新变化部分.RA可能是每次整个刷新.这个不知道那录像软件录像的时候优化的原理,因为有时侯(例如最后停止录像的时候)任务栏程序标题处也有模糊现象出现.还有个原因就是,我传输使用的是32位真彩色---无论你桌面设置的是什么颜色.而为了减少录像的压缩率,我录像所在电脑使用的是16位色.或许也有这原因导致录制下来的有小方块.
我是整个取变化区域,然后ZLIB压缩传输,而不是取每个图标---因为,图标本身不会变化,所以除了第一次是取全屏幕外,平时除非图标所在区域发生变化,否则都是不会取的.所以原理上应该不会有小方块的.而且肉眼和抓图下来都没有看到小方块.总之,实际应用中看到的屏幕跟RA的是一模一样的.
全文检索兄:再等一天吧.没人回答的出来我再揭晓.
 
想问个问题,JINGTAO电脑中,任务栏中有个图标,2个箭头,一个向下,一个向上,访问网站会变蓝色,是防火墙吧??是什么软件?能不能说说名字?
 
jingtao前辈,我来回答一下吧。
我的机器配置C-M 1.5GHZ, 512M RAM.


我刚才做了一个测试,将屏屏存到TBitmap类中,用下面的方法来测试

var
FromTick: Integer;
DesktopDC: HDC;
CurrentDesktopScreen: TBitmap;
begin
CurrentDesktopScreen := TBitmap.Create;
CurrentDesktopScreen.Width := 800;
CurrentDesktopScreen.Height := 600;
FromTick := GetTickCount();
DesktopDC := GetDC(GetDesktopWindow());
Bitblt(CurrentDesktopScreen.Canvas.Handle, 0, 0, 800, 600, DesktopDC, 0, 0, SRCCOPY);
ReleaseDC(GetDesktopWindow(), DesktopDC);
Caption := Format('%d', [GetTickCount -FromTick]);

上面的方法只是截屏并存储到位图中(当然也可以通过CreateCompatibleDC来实现获取桌面),用时一般在15~30毫秒 按照40帧/秒的速度即处理这一步的用时应该在500毫秒以上,再加上图像比较,以及网路传输速度更慢。
基本上肯定不是你实现的方法!不知道对不对,先排除之

分块处理,即对将屏幕一块一块的截存入位图时发现 宽度 < 800 时,速度上有明显的变化。一直保持在15~16毫秒之间,
再总结一下发现当总面积在小于200x200像素的情况下,截屏的时间几呼~0。有可能是采用这种分块的方法来实现。


下面就是图像的比较,保存发送过去的屏幕图像,用于下一步变化的区域比较。设定 保存在 “PreDesktopScreen”(TBitmap)中
实现的思路可能是这样的,即创建一个线程按照200*200的块大小来扫描屏幕,并与PreDesktopScreen中相应的位置进行比较。

关于这个比较的算法,最笨的就是用的逐个像素的比较,然后将不一至的地方存入一个临时的链表中,
然后处理链表 获取最小的X与Y坐标构成变化区域的“左上端坐标”即 Rect 的Left, Top,再获取最大的X与Y坐标构成变化区域的“右下角坐标”即 Right, Bottom,
这样就形成了一个变化区域,假设为 DestRect。然后,再获取指定屏幕的变化区域DestRect,然后将这部份发送给客户端。
关于发送的结构,可以自定。但必须包含DestRect信息让客户端根据这个位置来画变化区域。

到这里,最重要的步骤就是“比较算法”。逐个像素的比较效率极其低下。
用GetPixel逐个像素的比较 200x200像素的图像,用时近1秒钟,所以这种方法肯定行不通。

所以需要一个快速的比较方法,那就是不要进行逐个的抛描,而是每15个像素比较一次。即下面的方法

for ix := 1 to CurrentDesktopScreen.Width div 15 do begin
for iy := 1 to CurrentDesktopScreen.Height div 15 do begin
if GetPixel(CurrentDesktopScreen.Canvas.Handle, ix*15, iy*15) <>
GetPixel(LastDesktopScreen.Canvas.Handle, ix*15, iy*15) then
//
//不同,加入链表
//
end;
end;

速度可以受得了基本上为 ~0毫秒

这就是我能及的想法了,不知道有没有跟jingtao前辈的方法搭上边。谢谢~
 
补充一下,我个人觉得这个程序最关健的地方就在于屏的变化区域比较。因为网速基本上定了。
采用Jpeg有损压缩,再加上用流压缩,我做过一个视频传输的结果是。
1024*768的图像
Bitmap图像大小为3000+KB
Jpeg图像大小为130+KB
Jpeg压缩度为50(相对清析的压缩比)的情况下,图像大小为 70+KB
ZLIB针对这种图像压缩比并不太高,只能减小1~5KB左右。
这就意味着每发送一次数据的大小至少保持在60K+

所以重点在于区域变化的传输,不知道对不对呢 ;-/
 
to iceapi:
我对这截屏时间研究了一下?看看下面截屏代码:

procedure GetScreen(var ScreenBmp: TBitmap);
var //主函数开始
DC: HDC;
time : integer;
begin
time := gettickcount;
ScreenBmp.Width := screen.Width;
ScreenBmp.Height := screen.Height;
ScreenBmp.PixelFormat := pf24bit ; //这一句关键
//type TPixelFormat = (pfDevice, pf1bit, pf4bit, pf8bit, pf15bit, pf16bit, pf24bit, pf32bit, pfCustom);
dc := GetWindowDC(0);
try
BitBlt(ScreenBmp.Canvas.Handle, 0,0,screen.Width,screen.Height,dc, 0,0,SRCCOPY);
finally
ReleaseDC(0, DC);
end;
label3.Caption := inttostr(integer(gettickcount)-time);
end; //主函数结束


上面指出来关键那一句//如果注释掉或者取值为pfDevice的时候
我在1024*768*32下的截屏时间都是0
也就是说如果你不改变位图的颜色质量的话,截屏截屏是基本不要时间的//
 
指定为 TPixelFormat =pfDevice 的时间 , 在我这边速度也是这个.
我测默认TPixelFormat 是不是pfDevice
 
我刚刚自己写了一段代码,就是演示只截取不同像素点保存在流中//
只是测试,还有很多的不足,请高手们多多给点建议

procedure TForm1.Button1Click(Sender: TObject);
var
bmp1,bmp2,bmp3 : Tbitmap;
Stream : Tstream;
RGB1, RGB2 : pRGBTriple;
list : TStringList;
i : integer; //时间测试
time : integer;
begin
bmp1 := Tbitmap.Create;
bmp2 := Tbitmap.Create;
bmp3 := Tbitmap.Create;
Stream := TMemoryStream.Create;
list := TStringList.Create;

bmp1.LoadFromFile('1.bmp');
bmp2.LoadFromFile('2.bmp');

RGB1 := bmp1.ScanLine[bmp1.height-1];//取得图像缓冲区首地址
RGB2 := bmp2.ScanLine[bmp2.height-1];

time := gettickcount;
for i:=1 to bmp1.Height*bmp1.Width+1 do
begin
if not CompareMem(RGB1,RGB2,3) then //比较值
begin
List.Add(inttostr(i)); //添加不同像素点位置,还需要改进/自己设计数据结构,减少数据
stream.WriteBuffer(RGB2,3); //添加到流
end;
inc(RGB1); //移动指针
inc(RGB2);
end;
label3.Caption := inttostr(integer(gettickcount)-time); //运行时间
label4.Caption := inttostr(List.Count); //变化点数目
bmp1.Free;
bmp2.Free;
bmp3.Free;
Stream.Free;
list.Free;

end;
 
讨论的热闹啊。如果屏幕截图应该做不到的。1024*768截下就是4M多。不失真压缩为JPE也有200K。做到实时不可能。曾经试过把图分为红、绿、兰三色分别压缩。倒是很小,但是在合成有问题。这个问题困了很久。有代码的富翁。我再送1000分。我已经顶了很多分了[:D]
 
bitblt不是耗费时间的重点,压缩和传输是大头,截屏的区域要尽可能小。
关注的重点是获取变化区域和如搂主提出的地方:压缩速度及比率。
 
继续讨论.这才是讨论技术啊.呵呵.
devilsniffer:我那个叫Sygate Personal Firewall.
 
后退
顶部