Dot Net下实现屏幕图像差异获取v2.0

      前几篇利用类似隔行扫描的方式实现了对屏幕图像差异的获取,并基于该算法实现了一个屏幕传输程序,通过使用该屏幕传输程序,明显感受到该算法的性能存在一定的不足,因此需要改进。最近参考了DG大哥DGScreenSpy_0.4e版的算法实现了分块的方式。由于还没有实现基于此算法的屏幕传输程序,所以目前还无法断然认为该算法一定优于隔行的方式。不过,从当前对该算法的测试数据上来看,估计可以达到20帧每秒(不考虑网络传输等的影响)。

      按照惯例应该先帖几张效果图,但是发现和之前发布的图像差异获取v1.0中的图片相差无几,所以就不帖了。直接来看下文。

      我们对两幅图像的比较,可以逐个像素比较,也可以先把图像分块,然后比较对应块中的数据。那么,到底分块是什么个概念,又该如何分块,分块后的数据如何比较呢?下面我将逐一进行介绍。

     分块也就是说把图片分成“几乘几”的小块,如图1所示一幅200*80的图像。按横向划分4块,纵向划分2块的方式,可以画出一个二行四列的网格,其中每一格就是分块数据。

screenshare-v2-1 图1

      看起来似乎很简单,其实实现上也很简单,^_^。在Dot Net中,Bitmap类有一个很好用的方法就是Clone,该方法的其中一个重载方法为:public Bitmap Clone(Rectangle rect, PixelFormat format); 怎么样?知道怎么做了吧。你只需要建立一个图像数组用于存储所有块数据,然后其中的每一个块数据均通过Clone方法从母图像中获取。

      分块后的数据其实仍然是一个图像,怎么比较这个图像呢?我第一次接触到分块算法的时候,就愣住了,难道对于分块数据仍然采用像素扫描的方式?研究了DG的算法后才发现自己太天真了。对于分块数据,可以直接在内存中进行比较,这种比较方式速度最快。说到这里,不知道是否有朋友会有疑问:“既然内存中比较速度最快,那为什么还要搞隔行、分块呢?”,答案很简单,因为我们不止是要比较出不同,更重要的是要把不同的部分加以利用。不管是隔行还是分块,目的都是为了能最小化变化的区域,只有这样才能有效降低网络负载。

      如果对上述内容理解清楚了,那接下来就来看下分块的思路。这个思路主要还要感谢DG的算法。

      首先对原图进行分块的初始化,经过这步之后会得到一个最原始的分块数组。然后拿该分块数组与第二张图片进行内存比较。比较的时候,并非逐个分块进行比较,而是有选择的进行比较,这种选择建立在三种假设的基础上:

     1. 鼠标所在的块会发生变化;

     2. 当一个块变化的时候,该块周围的块也会发生变化;

     3. 图片第一行和最后一行会发生变化;

     当比较出不同的时候,即可以采取我们想要的一些行为。另外,要注意的就是选择一个合适分块粒度(即你要把图像分成“几乘几”)。如果分块多,则每一个分块的数据量就小,但是比较的次数就会变多。如果分块少,则每一个分块的数据量较多,但是比较次数就会变少。因此选择一个合适的粒度会影响程序的性能,据文献资料的记载和他人的尝试,一般认为把屏幕分成16*8块最为合适。

      说了这么多,下面来看下关键代码吧:

ExpandedBlockStart.gif 初始化所有分块的数据
///   <summary>
///  初始化所有分块的数据
///   </summary>
public   void  InitializeBlocks()
{
    
int  top  =   0 ;
    
int  left  =   0 ;

    _blocks 
=   new  List < Bitmap > (_blocksInColumn  *  _blocksInRow);
    _isSupposedChanged 
=   new  List < bool > (_blocks.Capacity);
    _isScanned 
=   new  List < bool > (_blocks.Capacity);

    _blockWidth 
=  (_oldBmp.Width  +  _blocksInRow  -   1 /  _blocksInRow;
    _blockHeight 
=  (_oldBmp.Height  +  _blocksInColumn  -   1 /  _blocksInColumn;

    
for  ( int  i  =   1 ; i  <=  _blocks.Capacity; i ++ )
    {
        top 
=  ((i  +  _blocksInRow  -   1 /  _blocksInRow)  -   1 ;
        left 
=  i  -  _blocksInRow  *  top  -   1 ;

        _blocks.Add(_oldBmp.Clone(
new  Rectangle(left  *  _blockWidth, top  *  _blockHeight, _blockWidth, _blockHeight), _oldBmp.PixelFormat));
        
if  (i  <=  _blocksInRow  ||  _blocks.Capacity  -  i  -   1   <  _blocksInRow)
        {
            _isSupposedChanged.Add(
true );
        }
        
else
        {
            _isSupposedChanged.Add(
false );
        }
        _isScanned.Add(
false );
    }
}

ExpandedBlockStart.gif 查找差异的块
///   <summary>
///  查找出差异的块,调用该方法前请确认已经调用了InitalizeBlocks方法对所有块进行初始化
///   </summary>
///   <param name="bmp"> 目标图片 </param>
///   <param name="cursorPoint"> 鼠标所在的坐标 </param>
public   void  FindDifferences(Bitmap bmp, Point cursorPoint)
{
    
if  (cursorPoint.X  >=  _oldBmp.Width  ||  cursorPoint.X  <   0   ||  cursorPoint.Y  >=  _oldBmp.Height  ||  cursorPoint.Y  <   0 )
    {
        
return ;
    }

    
int  cursorBlockIndex;
    
int  currentIndex;
    
int  blockTop;
    
int  blockLeft;
    BitmapData bdOldBmp;
    BitmapData bdNewBmp;
    Bitmap newBmpBlock;
    Rectangle rectBlock;

    cursorBlockIndex 
=  (cursorPoint.X  /  _blockWidth)  +  (cursorPoint.Y  /  _blockHeight)  *  _blocksInRow;
    _isSupposedChanged[cursorBlockIndex] 
=   true ;
    _isScanned[cursorBlockIndex] 
=   false ;
    currentIndex 
=   0 ;
    
    
unsafe
    {
        
byte *  pointerToOldBmp;
        
byte *  pointerToNewBmp;

        
while  (currentIndex  <  _blocks.Capacity)
        {
            
if  ( ! _isScanned[currentIndex]  &&  _isSupposedChanged[currentIndex])
            {
                _isScanned[currentIndex] 
=   true ;
                blockTop 
=  (currentIndex  /  _blocksInRow)  *  _blockHeight;
                blockLeft 
=  (currentIndex  %  _blocksInRow)  *  _blockWidth;
                rectBlock
= new  Rectangle(blockLeft, blockTop, _blockWidth, _blockHeight);
                newBmpBlock 
=  (Bitmap)bmp.Clone(rectBlock,_format); // 克隆的时间需要0.015~0.016s左右
                rectBlock.X  =   0 ;
                rectBlock.Y 
=   0 ;

                bdOldBmp 
=  _blocks[currentIndex].LockBits(rectBlock, ImageLockMode.ReadWrite, _format);
                bdNewBmp 
=  newBmpBlock.LockBits(rectBlock, ImageLockMode.ReadWrite, _format);
                
                
int  k  =  RtlCompareMemory(bdOldBmp.Scan0, bdNewBmp.Scan0, _blockWidth  *   3   *  _blockHeight);
                
if  (k  <  bdOldBmp.Stride  *  _blockHeight)
                {
                    pointerToOldBmp 
=  ( byte * )bdOldBmp.Scan0.ToPointer();
                    pointerToNewBmp 
=  ( byte * )bdNewBmp.Scan0.ToPointer();

                    
for  ( int  height  =   0 ; height  <  _blockHeight; height ++ )
                    {
                        
for  ( int  width  =   0 ; width  <  _blockWidth; width ++ )
                        {
                            pointerToOldBmp[
0 =  pointerToNewBmp[ 0 ];
                            pointerToOldBmp[
1 =  pointerToNewBmp[ 1 ];
                            pointerToOldBmp[
2 =  pointerToNewBmp[ 2 ];
                            pointerToNewBmp 
+=   3 ;
                            pointerToOldBmp 
+=   3 ;
                        }
                        pointerToNewBmp 
+=  bdNewBmp.Stride  -  newBmpBlock.Width  *   3 ;
                        pointerToOldBmp 
+=  bdNewBmp.Stride  -  newBmpBlock.Width  *   3 ;
                    }

                    
if  (currentIndex  -  _blocksInRow  -   1   >=   0 )
                    {
                        _isSupposedChanged[currentIndex 
-  _blocksInRow  -   1 =   true ;
                        _isSupposedChanged[currentIndex 
-  _blocksInRow]  =   true ;
                    }
                    
else   if  (currentIndex  -  _blocksInRow  >=   0 )
                    {
                        _isSupposedChanged[currentIndex 
-  _blocksInRow]  =   true ;
                    }

                    
if  (currentIndex  +  _blocksInRow  +   1   <  _blocks.Capacity)
                    {
                        _isSupposedChanged[currentIndex 
+  _blocksInRow  +   1 =   true ;
                        _isSupposedChanged[currentIndex 
+  _blocksInRow]  =   true ;
                    }
                    
else   if  (currentIndex  +  _blocksInRow  <  _blocks.Capacity)
                    {
                        _isSupposedChanged[currentIndex 
+  _blocksInRow]  =   true ;
                    }

                    
if  (currentIndex  %  _blocksInRow  >   1 )
                    {
                        _isSupposedChanged[currentIndex 
-   2 =   true ;
                        _isSupposedChanged[currentIndex 
-   1 =   true ;
                    }
                    
else   if  (currentIndex  %  _blocksInRow  >   0 )
                    {
                        _isSupposedChanged[currentIndex 
-   1 =   true ;
                    }

                    
if  (currentIndex  %  _blocksInRow  <  _blocksInRow  -   2 )
                    {
                        _isSupposedChanged[currentIndex 
+   2 =   true ;
                        _isSupposedChanged[currentIndex 
+   1 =   true ;
                    }
                    
else   if  (currentIndex  %  _blocksInRow  <  _blocksInRow  -   1 )
                    {
                        _isSupposedChanged[currentIndex 
+   1 =   true ;
                    }

                    
if  (handler  !=   null )
                    {
                        handler(newBmpBlock, rectBlock);
                    }
                }

                _blocks[currentIndex].UnlockBits(bdOldBmp);
                newBmpBlock.UnlockBits(bdNewBmp);
                currentIndex 
=  Math.Max(Math.Min(currentIndex  -  _blocksInRow  -   1 , currentIndex  -   2 ),  0 );
            }
            
else
            {
                currentIndex
++ ;
            }
        }
// end of while
    } // end of unsafe
} // end of FindDifferences

      注:该实现由于并不是特地为了实现屏幕传输,所以在比较的时候,我利用了Clone方式获取第二张图片的对应块。该方式需要耗时0.015s左右,因此比较100块图像就会额外使用1.5s左右的时间。

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

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

 

 

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值