zoukankan      html  css  js  c++  java
  • [转].net中比较两幅图片是否相同

      .net中的GDI+能非常方便的进行图象处理,但是并未直接提供进行图片比较的类或者方法,本文的目的是探讨如何进行图片比较。

        首先要说明的是,进行比较的两幅图片必须具有相同的格式,并且最好是未经压缩的图片格式。否则,不能进行完全的相同性对比,只能做近似判断,比如各种颜色分量相差5%即认为一致。

        在自己动手之前先GOOGLE一番,找到了下面两篇比较典型的文章:

        (1)http://groups.google.com/group/microsoft.public.dotnet.languages.csharp/msg/4c8beaa229c2cbd6

    这篇文章使用Image.GetPixel方法获取每个像素点的Color,然后将各点组合成一个Hash值。关键代码段:

    [CLSCompliant(false)] 
        public static uint GetBitmapHashValue( 
          Bitmap image) 
        { 
          uint result = 0xFFFFFFFF;


          for (int x = 0; x < image.Width; x++) 
            for (int y = 0; y < image.Height; y++) 
              result = 
                BitmapHasher.CRCTable[ 
                (result ^ image.GetPixel(x, y).ToArgb()) & 0xFF] ^ 
                (result >> 8);


          return result ^ 0xFFFFFFFF; 
        }

        (2)http://topic.csdn.net/u/20080808/09/c92a057e-ebd0-438c-bc1d-64d0a78127f7.html

    CSDN论坛的这个帖子有很多实现图片比较的思路,比如:

    (a)比较直接用 memcmp , 10M以内的数据不会超过 1毫秒

    当然你不能用 GetPix, GetPix非常慢, 应该直接取出内存块来比较, 
    图像压缩可以把前后两幅图像相减, 再找个压缩算法压缩相减的结果 
    另外一边只要加上这个结果就是新的图像
    (b)将屏幕分成固定的若干小块,对其分别编号,分块比较,每次只传送发生改变的块
    (c)请参考请他VNC开源项目。 
    它们通常是安装钩子监视屏幕区域的变化,然后压缩传送变化的图片区。 
    用判断图像变化的办法效率太低,很少被使用。
    (d)保存上一张图片,抓到一张新图后,在内存中按块用memcmp比较,如果发现有不同,就发送此块,然后到客户端组装起来

    由于本文只讨论图片比较,所有(c)中的安装系统钩子不考虑,其他几点无非就是用GetPixel按像素比较,或者得到图片的内存块然后比较内存块是否一致。

    1.使用GetPixel得到各像素,然后逐个像素进行对比:

    /// <summary>
     /// 比较两幅图片是否一致
     /// </summary>
     /// <param name="bitmap1">图片1</param>
     /// <param name="bitmap2">图片2</param>
     /// <returns>如果两幅图片相同,返回0;如果图片1小于图片2,返回小于0的值;如果图片1大于图片2,返回大于0的值。</returns>
     public static int BitmapCompare(Bitmap bitmap1, Bitmap bitmap2)
     {
      int result = 0; //假设两幅图片相同
      if (bitmap1 == null || bitmap2 == null)
       return -1;
      if (bitmap1.Width == bitmap2.Width && bitmap1.Height == bitmap2.Height)
      {
       for (int i = 0; i < bitmap1.Width; i++)
       {
        for (int j = 0; j < bitmap1.Height; j++)
        {
         Color color1 = bitmap1.GetPixel(i, j);
         Color color2 = bitmap2.GetPixel(i, j);
         if (color1 != color2)
         {
          result = color1.ToArgb() - color2.ToArgb();
          break;
         }
        }
        if (result != 0)
         break;
       }
      }
      else if (bitmap1.Width != bitmap2.Width)
      {
       result = bitmap1.Width - bitmap2.Width;
      }
      else if (bitmap1.Height != bitmap2.Height)
      {
       result = bitmap1.Height - bitmap2.Height;
      }
      return result;
     }

    2.得到图片数据内存块,然后比较内存块是否一致:

        用Bitmap.LockBits方法可以得到BitmapData(位图数据),BitmapData.Scan0指向了位图数据部分的基地址。BitmapData.Stride给出了图象中每行所占的字节数目,注意BitmapData.Stride并不等于BitmapData.Width,原因如下:(1)BitmapData.Width是每行的像素数目,每个像素所占的字节数跟PixelFormat(像素格式)有关,可以是1、4、8、16、24、32、48、64等位数;(2)系统将图片数据行在内存中进行了对齐,每行所占字节数是4的倍数,且总是大于等于Width*BitsPerPixel/8。Marshal.Copy方法复制内存块到字节数组。

        得到图片数据内存块的代码如下:

        BitmapData bmd1 = bitmap1.LockBits(new Rectangle(0, 0, bitmap1.Width, bitmap1.Height), ImageLockMode.ReadOnly, bitmap1.PixelFormat);     //得到图片数据对象
       int bytes = bmd1.Stride * bitmap1.Height;          //图片数据大小
       byte[] buff1 = new byte[bytes];                         //保存图片数据的字节数组
       Marshal.Copy(bmd1.Scan0, buff1, 0, Marshal.SizeOf(typeof(byte)) * bytes);     //复制图片数据块
       //在这里对图片数据块执行操作
       bitmap1.UnlockBits(bmd1);                              //解锁图片数据块

        对内存块进行比较的方法有以下几种:

        (1)C API函数memcmp,该函数的原型为:

        int memcmp(const void *buf1,const void *buf2,size_t count);

       PINVOKE引用方式为:

       [DllImport("msvcrt.dll")]
       private static extern IntPtr memcmp(byte[] b1, byte[] b2, IntPtr count);

        (2)用Marshal.ReadByte方法读取内存中的字节,然后逐字节进行比较。

         BitmapData bmd1 = bitmap1.LockBits(new Rectangle(0, 0, bitmap1.Width, bitmap1.Height), ImageLockMode.ReadOnly, bitmap1.PixelFormat);
       IntPtr start1 = bmd1.Scan0;
       int sizeOfByte = Marshal.SizeOf(typeof(byte));
       for (int i = 0; i < sizeOfByte * bmd1.Stride * bitmap1.Height; i++)
       {
        byte b1 = Marshal.ReadByte(start1, i);

        //在这里对字节进行操作,也可以用Marshal.WriteByte修改字节内容
        }
       bitmap1.UnlockBits(bmd1);

        (3)自定义函数进行字节数组比较:

        public static int MemoryCompare2(byte[] b1, byte[] b2)
     {
      int result = 0;
      if (b1.Length != b2.Length)
       result = b1.Length - b2.Length;
      else
      {
       for (int i = 0; i < b1.Length; i++)
       {
        if (b1[i] != b2[i])
        {
         result = (int)(b1[i] - b2[i]);
         break;
        }
       }
      }
      return result;
     }

    3.各种图片比较方式的效率对比

    我写了一小段测试代码对上述四种图片比较方式的效率进行了对比,图片为一个24位色的1024X768像素位图,结果如下表所示:

    单位:毫秒

    图片比较方式 第一次 第二次 第三次 第一次 第二次 第三次 平均用时 备注
    Bitmap.GetPixel 4466 4296 4878 4416 4530 4584 4528.3 每次比较一个像素,内存占用低,耗时最长
    memcmp 31 30 40 70 41 31 40.5 一次比较所有图片内存数据,内存占用高,耗时最短;
    如果分块对比图片内存数据,可以减少内存占用,但是会增加处理时间
    Marshal.ReadByte 2103 1943 2103 2043 2068 2083 2057.2 每次比较一个字节,内存占用最低,耗时较长
    自定义字节数组比较 60 60 70 110 95 67 77 一次比较所有图片内存数据,内存占用高,耗时较短;
    如果分块对比图片内存数据,可以减少内存占用,但是会增加处理时间

     

    4.源代码下载

    https://files.cnblogs.com/xrwang/ImageCompare/ImageCompare.rar 

    转自:http://www.cnblogs.com/xrwang/articles/1276826.html

  • 相关阅读:
    postgis 利用 php 返回geojson格式数据
    openlayers 3读取加载geojson格式数据
    openlayers 3加载百度、高德、google瓦片地图
    ol2 和 bootstrap样式冲突的问题
    Openlayers 2 取消鼠标缩放地图的功能
    Struts2之2.5.10配置
    ol3修改右下键的Attribution
    openlayers 2 高亮显示元素以及通过属性查询高亮某一元素
    sql查看锁与解锁
    使用jQuery解析JSON数据
  • 原文地址:https://www.cnblogs.com/skylaugh/p/2513185.html
Copyright © 2011-2022 走看看