【转】感知哈希算法——找出相似的图片

Google 图片搜索功能

        在谷歌图片搜索中, 用户可以上传一张图片, 谷歌显示因特网中与此图片相同或者相似的图片.

        比如我上传一张照片试试效果:

原理讲解

        参考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

如果认为此文对您有帮助,别忘了支持一下哦!

作者:齐飞

来源:http://youring2.cnblogs.com/

声明:本博客原创文字只代表本人工作中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未授权,贴子请以现状保留,转载时必须保留此段声明,且在文章页面明显位置给出原文连接。

转载:http://www.cnblogs.com/youring2/p/4652081.html

时间: 2024-12-29 08:05:14

【转】感知哈希算法——找出相似的图片的相关文章

数组 算法-找出两数组中不同的数据,并查看他们在以前数组中的索引值

问题描述 找出两数组中不同的数据,并查看他们在以前数组中的索引值 var aa = [1,21,21,21,28]; var bb = [3,4,27,39,21]; var cc = []; var tmp = aa.concat(bb); var o = {}; for (var i = 0; i < tmp.length; i ++){ (tmp[i] in o) ? o[tmp[i]] ++ : o[tmp[i]] = 1; } for (x in o){ if (o[x] == 1){

贪心算法-找出一个-1,0,1三值矩阵中的最大全1子块

问题描述 找出一个-1,0,1三值矩阵中的最大全1子块 并不要求子块仍为一个矩阵,但要求形状为凸多边形,可进行行列变换,只要求所求子块最大. 我的理解是:用贪心法找出一个连续的最全1块,再进行行列变换保证子块形状为凸. 数据量较大,文件形式给出.

用Python实现通过哈希算法检测图片重复的教程

 Iconfinder 是一个图标搜索引擎,为设计师.开发者和其他创意工作者提供精美图标,目前托管超过 34 万枚图标,是全球最大的付费图标库.用户也可以在 Iconfinder 的交易板块上传出售原创作品.每个月都有成千上万的图标上传到Iconfinder,同时也伴随而来大量的盗版图.Iconfinder 工程师 Silviu Tantos 在本文中提出一个新颖巧妙的图像查重技术,以杜绝盗版. 我们将在未来几周之内推出一个检测上传图标是否重复的功能.例如,如果用户下载了一个图标然后又试图通过上

用Python实现通过哈希算法检测图片重复的教程_python

Iconfinder 是一个图标搜索引擎,为设计师.开发者和其他创意工作者提供精美图标,目前托管超过 34 万枚图标,是全球最大的付费图标库.用户也可以在 Iconfinder 的交易板块上传出售原创作品.每个月都有成千上万的图标上传到Iconfinder,同时也伴随而来大量的盗版图.Iconfinder 工程师 Silviu Tantos 在本文中提出一个新颖巧妙的图像查重技术,以杜绝盗版. 我们将在未来几周之内推出一个检测上传图标是否重复的功能.例如,如果用户下载了一个图标然后又试图通过上传

求一个程序算法,关于找出符合条件的操作符排列?

问题描述 求一个程序算法,关于找出符合条件的操作符排列? 给一个初始操作数a,然后对这个操作数执行n次加减乘余的计算操作[每次操作a自增1,而且不考虑运算符优先级,谁在前面先算谁],最后会得到一个结果数x,问如何求出这些操作符?? 重要:程序不能使用递归,最好只用一个主函数!! 比如,给你一个初始数3,执行7次加减乘余操作,最后得到结果147,那么有一种操作符序列满足条件:* + + - * + + 既:3*4+5+6-7*8+9+10=147 解决方案 亲测合格,请验证: #include #

求解答-试编写一个算法,找出一个循环链表中的最小值。我是新手,编了一个程序,不知错在哪

问题描述 试编写一个算法,找出一个循环链表中的最小值.我是新手,编了一个程序,不知错在哪 #includeusing namespace std; class LinkNode{ int data; LinkNode *link; LinkNode(int d=0LinkNode *l=0){data=d;link=l;}}; class List{private: LinkNode *first; int n;public: List() { first=new LinkNode; first

数据结构 算法-如何用java中串的操作方法找出两个字符串中所有共同的字符

问题描述 如何用java中串的操作方法找出两个字符串中所有共同的字符 通过实现对串的基本操作的算法设计,运用模式匹配算法KMP和Brute-Force,展出两个字符串中所有共同的字符,判断一个字符串是否为E-mail地址

数组大小为2n+1-数组相关算法java,找出需求的数据

问题描述 数组相关算法java,找出需求的数据 存在一个数组,数组大小为2n+2,里面有n对个数,例如:1,2,2,3,4,1.(数组是无序的,考虑排序的话一定会超过限制)这,6个数中的单独的数就是3,4,要你用你能想到的最高效率的方法找出来 解决方案 如果数组是连续的则可以用byte[] b = new byte[n+1];然后遍历一遍原数组,将遍历的值放入b的下标中计数,最后为1的那个下标表示数据是单独的. 这样的话总最多做3n+3次操作就能找全单独的数. 如果数组里面的数是无规律的,那么可

一种新的数学算法,能够找出网络谣言发起人

瑞士洛桑联邦工学院研究人员近日发明了一种新的数学http://www.aliyun.com/zixun/aggregation/4793.html">算法,能够找出网络谣言发起人.据测试,调查人员查看15人至20人的消息后,便可以找出经过社交网站传递至500名网络用户的一则谣言最初是谁发出的.据悉,这一算法同样可以追踪到电脑病毒的起源. 微评 炸死他度:以后我们说话要小心了. 悟悦子:利用算法找到发起人,逻辑上似乎说不通,提高寻找的速度倒是可以,但提供精确判断的依据是什么呢? 微笑的大祥: