C#对比图片相似度

    参考Neal Krawetz博士的这篇文章, 实现这种功能的关键技术叫做"感知哈希算法"(Perceptual Hash Algorithm), 意思是为图片生成一个指纹(字符串格式), 两张图片的指纹越相似, 说明两张图片就越相似. 但关键是如何根据图片计算出"指纹"呢? 下面用最简单的步骤来说明一下原理:

第一步 缩小图片尺寸

        将图片缩小到8x8的尺寸, 总共64个像素. 这一步的作用是去除各种图片尺寸和图片比例的差异, 只保留结构、明暗等基本信息.

        

第二步 转为灰度图片

         将缩小后的图片, 转为64级灰度图片.

        

第三步 计算灰度平均值

         计算图片中所有像素的灰度平均值

第四步 比较像素的灰度

        将每个像素的灰度与平均值进行比较, 如果大于或等于平均值记为1, 小于平均值记为0.

第五步 计算哈希值

         将上一步的比较结果, 组合在一起, 就构成了一个64位的二进制整数, 这就是这张图片的指纹.

第六步 对比图片指纹

        得到图片的指纹后, 就可以对比不同的图片的指纹, 计算出64位中有多少位是不一样的. 如果不相同的数据位数不超过5, 就说明两张图片很相似, 如果大于10, 说明它们是两张不同的图片.

代码实现 (C#版本)

        下面我用C#代码根据上一节所阐述的步骤实现一下.

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
using  System;
using  System.IO;
using  System.Drawing;
 
namespace  SimilarPhoto
{
     class  SimilarPhoto
     {
         Image SourceImg;
 
         public  SimilarPhoto( string  filePath)
         {
             SourceImg = Image.FromFile(filePath);
         }
 
         public  SimilarPhoto(Stream stream)
         {
             SourceImg = Image.FromStream(stream);
         }
 
         public  String GetHash()
         {
             Image image = ReduceSize();
             Byte[] grayValues = ReduceColor(image);
             Byte average = CalcAverage(grayValues);
             String reslut = ComputeBits(grayValues, average);
             return  reslut;
         }
 
         // Step 1 : Reduce size to 8*8
         private  Image ReduceSize( int  width = 8, int  height = 8)
         {
             Image image = SourceImg.GetThumbnailImage(width, height, () => { return  false ; }, IntPtr.Zero);
             return  image;
         }
 
         // Step 2 : Reduce Color
         private  Byte[] ReduceColor(Image image)
         {
             Bitmap bitMap = new  Bitmap(image);
             Byte[] grayValues = new  Byte[image.Width * image.Height];
 
             for ( int  x = 0; x<image.Width; x++)
                 for  ( int  y = 0; y < image.Height; y++)
                 {
                     Color color = bitMap.GetPixel(x, y);
                     byte  grayValue = ( byte )((color.R * 30 + color.G * 59 + color.B * 11) / 100);
                     grayValues[x * image.Width + y] = grayValue;
                 }
             return  grayValues;
         }
 
         // Step 3 : Average the colors
         private  Byte CalcAverage( byte [] values)
         {
             int  sum = 0;
             for  ( int  i = 0; i < values.Length; i++)
                 sum += ( int )values[i];
             return  Convert.ToByte(sum / values.Length);
         }
 
         // Step 4 : Compute the bits
         private  String ComputeBits( byte [] values, byte  averageValue)
         {
             char [] result = new  char [values.Length];
             for  ( int  i = 0; i < values.Length; i++)
             {
                 if  (values[i] < averageValue)
                     result[i] = '0' ;
                 else
                     result[i] = '1' ;
             }
             return  new  String(result);
         }
 
         // Compare hash
         public  static  Int32 CalcSimilarDegree( string  a, string  b)
         {
             if  (a.Length != b.Length)
                 throw  new  ArgumentException();
             int  count = 0;
             for  ( int  i = 0; i < a.Length; i++)
             {
                 if  (a[i] != b[i])
                     count++;
             }
             return  count;
         }
     }
}

        谷歌服务器里的图片数量是百亿级别的, 我电脑里的图片数量当然没法比, 但以前做过爬虫程序, 电脑里有40,000多人的头像照片, 就拿它们作为对比结果吧! 我计算出这些图片的"指纹", 放在一个txt文本中, 格式如下.

        用ASP.NET写一个简单的页面, 允许用户上传一张图片, 后台计算出该图片的指纹, 并与txt文本中各图片的指纹对比, 整理出结果显示在页面中, 效果如下:

本文地址: http://www.cnblogs.com/technology/archive/2012/07/12/Perceptual-Hash-Algorithm.html

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
C是一门广泛应用于计算机科学、统计学、物理学、化学、金融等领域的编程语言。它是由AT&T贝尔实验室的Dennis Ritchie于1972年设计出来的。C语言是一种高效、可移植和灵活的语言,除了提供底层访问硬件的能力,同时也是高级程序设计的首选语言之一。 C语言最初用于UNIX操作系统的开发,但随着时间的推移,C语言被广泛应用于各个领域,例如嵌入式系统、游戏开发、框架开发、编译器的开发等。C语言具有灵活性,使得程序员可以方便地通过底层的算法解决问题。它还提供了丰富的库函数和语法特性,适合用于底层的编程,使得程序员可以更好地掌控内存和CPU的使用情况,提高程序的效率。C语言的语法简洁,使得学习和使用C语言变得更加容易,即使是初学者也可以很快掌握它。 作为一门编程语言,C语言有一些缺点。首先,它没有内置的对象模型(Object Model),这意味着开发者需要自己处理内存分配和释放,指针操作也需要谨慎,这在初学者中很容易犯错;其次,C语言的性能很高,但这也意味着如果程序员出现差错,可能会导致程序崩溃或者产生很严重的安全问题。因此,需要开发者具备较高的编程经验和严谨的态度才能开发出稳定可靠的程序。 总之,虽然C语言并不是最新的编程语言,但它在编程领域中仍然有着重要的地位。通过学习C语言,人们不仅可以了解底层的编程方式,还可以提高代码的效率和可移植性,为今后的编程打下坚实的基础。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值