根据Neal Krawetz博士的解释,原理非常简单易懂。我们可以用一个快速算法,就达到基本的效果。

这里的关键技术叫做"感知哈希算法"(Perceptual hash algorithm),它的作用是对每张图片生成一个"指纹"(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。

下面是一个最简单的实现:

第一步,缩小尺寸。

将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。

 

第二步,简化色彩。

将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。

第三步,计算平均值。

计算所有64个像素的灰度平均值。

第四步,比较像素的灰度。

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

第五步,计算哈希值。

将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。

 =  = 8f373714acfcf4d0

得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。在理论上,这等同于计算"汉明距离"(Hamming distance)。如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。

具体的代码实现,可以参见Wote用python语言写的imgHash.py。代码很短,只有53行。使用的时候,第一个参数是基准图片,第二个参数是用来比较的其他图片所在的目录,返回结果是两张图片之间不相同的数据位数量(汉明距离)。

这种算法的优点是简单快速,不受图片大小缩放的影响,缺点是图片的内容不能变更。如果在图片上加几个文字,它就认不出来了。所以,它的最佳用途是根据缩略图,找出原图。

实际应用中,往往采用更强大的pHash算法和SIFT算法,它们能够识别图片的变形。只要变形程度不超过25%,它们就能匹配原图。这些算法虽然更复杂,但是原理与上面的简便算法是一样的,就是先将图片转化成Hash字符串,然后再进行比较。

下面我们来看下上述理论用java来做一个DEMO版的具体实现:

?
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
import java.awt.Graphics2D;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import javax.imageio.ImageIO;
/*
* pHash-like image hash.
* Author: Elliot Shepherd (elliot@jarofworms.com
* Based On: http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html
*/
public class ImagePHash {
   private int size = 32;
   private int smallerSize = 8;
    
   public ImagePHash() {
       initCoefficients();
   }
    
   public ImagePHash(int size, int smallerSize) {
       this.size = size;
       this.smallerSize = smallerSize;
        
       initCoefficients();
   }
    
   public int distance(String s1, String s2) {
       int counter = 0;
       for (int k = 0; k < s1.length();k++) {
           if(s1.charAt(k) != s2.charAt(k)) {
               counter++;
           }
       }
       return counter;
   }
    
   // Returns a 'binary string' (like. 001010111011100010) which is easy to do a hamming distance on.
   public String getHash(InputStream is) throws Exception {
       BufferedImage img = ImageIO.read(is);
        
       /* 1. Reduce size.
        * Like Average Hash, pHash starts with a small image.
        * However, the image is larger than 8x8; 32x32 is a good size.
        * This is really done to simplify the DCT computation and not
        * because it is needed to reduce the high frequencies.
        */
       img = resize(img, size, size);
        
       /* 2. Reduce color.
        * The image is reduced to a grayscale just to further simplify
        * the number of computations.
        */
       img = grayscale(img);
        
       double[][] vals = new double[size][size];
        
       for (int x = 0; x < img.getWidth(); x++) {
           for (int y = 0; y < img.getHeight(); y++) {
               vals[x][y] = getBlue(img, x, y);
           }
       }
        
       /* 3. Compute the DCT.
        * The DCT separates the image into a collection of frequencies
        * and scalars. While JPEG uses an 8x8 DCT, this algorithm uses
        * a 32x32 DCT.
        */
       long start = System.currentTimeMillis();
       double[][] dctVals = applyDCT(vals);
       System.out.println("DCT: " + (System.currentTimeMillis() - start));
        
       /* 4. Reduce the DCT.
        * This is the magic step. While the DCT is 32x32, just keep the
        * top-left 8x8. Those represent the lowest frequencies in the
        * picture.
        */
       /* 5. Compute the average value.
        * Like the Average Hash, compute the mean DCT value (using only
        * the 8x8 DCT low-frequency values and excluding the first term
        * since the DC coefficient can be significantly different from
        * the other values and will throw off the average).
        */
       double total = 0;
        
       for (int x = 0; x < smallerSize; x++) {
           for (int y = 0; y < smallerSize; y++) {
               total += dctVals[x][y];
           }
       }
       total -= dctVals[0][0];
        
       double avg = total / (double) ((smallerSize * smallerSize) - 1);
    
       /* 6. Further reduce the DCT.
        * This is the magic step. Set the 64 hash bits to 0 or 1
        * depending on whether each of the 64 DCT values is above or
        * below the average value. The result doesn't tell us the
        * actual low frequencies; it just tells us the very-rough
        * relative scale of the frequencies to the mean. The result
        * will not vary as long as the overall structure of the image
        * remains the same; this can survive gamma and color histogram
        * adjustments without a problem.
        */
       String hash = "";
        
       for (int x = 0; x < smallerSize; x++) {
           for (int y = 0; y < smallerSize; y++) {
               if (x != 0 && y != 0) {
                   hash += (dctVals[x][y] > avg?"1":"0");
               }
           }
       }
        
       return hash;
   }
    
   private BufferedImage resize(BufferedImage image, int width,    int height) {
       BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
       Graphics2D g = resizedImage.createGraphics();
       g.drawImage(image, 00, width, height, null);
       g.dispose();
       return resizedImage;
   }
    
   private ColorConvertOp colorConvert = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
   private BufferedImage grayscale(BufferedImage img) {
       colorConvert.filter(img, img);
       return img;
   }
    
   private static int getBlue(BufferedImage img, int x, int y) {
       return (img.getRGB(x, y)) & 0xff;
   }
    
   // DCT function stolen from http://stackoverflow.com/questions/4240490/problems-with-dct-and-idct-algorithm-in-java
   private double[] c;
   private void initCoefficients() {
       c = new double[size];
        
       for (int i=1;i<size;i++) {
           c[i]=1;
       }
       c[0]=1/Math.sqrt(2.0);
   }
    
   private double[][] applyDCT(double[][] f) {
       int N = size;
        
       double[][] F = new double[N][N];
       for (int u=0;u<N;u++) {
         for (int v=0;v<N;v++) {
           double sum = 0.0;
           for (int i=0;i<N;i++) {
             for (int j=0;j<N;j++) {
               sum+=Math.cos(((2*i+1)/(2.0*N))*u*Math.PI)*Math.cos(((2*j+1)/(2.0*N))*v*Math.PI)*(f[i][j]);
             }
           }
           sum*=((c[u]*c[v])/4.0);
           F[u][v] = sum;
         }
       }
       return F;
   }
   public static void main(String[] args) {
        
       ImagePHash p = new ImagePHash();
       String image1;
       String image2;
       try {
           image1 = p.getHash(new FileInputStream(new File("C:/Users/june/Desktop/1.jpg")));
           image2 = p.getHash(new FileInputStream(new File("C:/Users/june/Desktop/1.jpg")));
           System.out.println("1:1 Score is " + p.distance(image1, image2));
           image1 = p.getHash(new FileInputStream(new File("C:/Users/june/Desktop/1.jpg")));
           image2 = p.getHash(new FileInputStream(new File("C:/Users/june/Desktop/2.jpg")));
           System.out.println("1:2 Score is " + p.distance(image1, image2));
           image1 = p.getHash(new FileInputStream(new File("C:/Users/june/Desktop/1.jpg")));
           image2 = p.getHash(new FileInputStream(new File("C:/Users/june/Desktop/3.jpg")));
           System.out.println("1:3 Score is " + p.distance(image1, image2));
           image1 = p.getHash(new FileInputStream(new File("C:/Users/june/Desktop/2.jpg")));
           image2 = p.getHash(new FileInputStream(new File("C:/Users/june/Desktop/3.jpg")));
           System.out.println("2:3 Score is " + p.distance(image1, image2));
            
           image1 = p.getHash(new FileInputStream(new File("C:/Users/june/Desktop/4.jpg")));
           image2 = p.getHash(new FileInputStream(new File("C:/Users/june/Desktop/5.jpg")));
           System.out.println("4:5 Score is " + p.distance(image1, image2));
            
       catch (FileNotFoundException e) {
           e.printStackTrace();
       catch (Exception e) {
           e.printStackTrace();
       }
   }
}

运行结果为:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DCT: 163
DCT: 158
1:1 Score is 0
DCT: 168
DCT: 164
1:2 Score is 4
DCT: 156
DCT: 156
1:3 Score is 3
DCT: 157
DCT: 157
2:3 Score is 1
DCT: 157
DCT: 156
4:5 Score is 21

说明:其中1,2,3是3张非常相似的图片,图片分别加了不同的文字水印,肉眼分辨的不是太清楚,下面会有附图,4、5是两张差异很大的图,图你可以随便找来测试,这两张我就不上传了。

结果说明:汉明距离越大表明图片差异越大,如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。从结果可以看到1、2、3是相似图片,4、5差异太大,是两张不同的图片。

附:图1、2、3

图1

图2

图3

参考地址:

代码参考:http://pastebin.com/Pj9d8jt5
原理参考:http://www.ruanyifeng.com/blog/2011/07/principle_of_similar_image_search.html
汉明距离:http://baike.baidu.com/view/725269.htm

来自:http://stackoverflow.com/questions/6971966/how-to-measure-percentage-similarity-between-two-images

转载于:https://www.cnblogs.com/lixun/p/4334977.html

用汉明距离进行图片相似度检测的Java实现相关推荐

  1. Java实现用汉明距离进行图片相似度检测的

    Google.Baidu 等搜索引擎相继推出了以图搜图的功能,测试了下效果还不错~ 那这种技术的原理是什么呢?计算机怎么知道两张图片相似呢? 根据Neal Krawetz博士的解释,原理非常简单易懂. ...

  2. linux图片相似度检测软件下载,移动端图像相似度算法选型

    概述 电商场景中,卖家为获取流量,常常出现重复铺货现象,当用户发布上传图像或视频时,在客户端进行图像特征提取和指纹生成,再将其上传至云端指纹库对比后,找出相似图片,杜绝重复铺货造成的计算及存储资源浪费 ...

  3. 图片相似度识别算法公式,图片相似度检测算法

    计算图像相似度的算法有哪些 SIM=StructuralSIMilarity(结构相似性),这是一种用来评测图像质量的一种方法. 由于人类视觉很容易从图像中抽取出结构信息,因此计算两幅图像结构信息的相 ...

  4. linux图片相似度检测软件下载,文档相似性检测工具

    文档相似性检测工具是通过比对源文档和目标文档的相似性给出相似度结果的一种信息处理系统.可以分段粘贴进去查 的确很给力哦.文档相似性检测工具和其他系统覆盖文献有80%以上不同,本系统通过混合引擎覆盖18 ...

  5. python+openCV使用SIFT算法实现印章的总相似度检测

    python实现,使用SIFT算法和文字相似度检测算法,并使用了pyqt5做的印章相似度检测工具,还有很大优化空间,对于我这水平费了不少力气,记录一下. 首先整体流程是预建了一个印章库,包含若干张图片 ...

  6. 图片相似度计算:深入理解DCT变换以及感知哈希

    缘起 Android上硬件编解码一直是个老大难问题,就解码来说,硬解码本身并不困难,只要按照MediaCodec的流程开发即可.但由于系统碎片化,硬件规格不一致,硬件解码会到黑屏,花屏,绿屏之类的显示 ...

  7. python 图像识别_python图像识别之图片相似度计算

    作者 | a1131825850疯子 来源 | Python爬虫scrapy 原文 | python图像识别---------图片相似度计算 1.背景 要识别两张图片是否相似,首先我们可能会区分这两张 ...

  8. 背景差分法android代码,【学术论文】基于背景差分法的尾气烟度检测系统设计...

    摘要随着机动车尾气的大量排放,为减少大气污染,对尾气进行相关的检测也越来越重要.为自动检测机动车尾气黑度,设计并实现了一种对尾气图片使用图像处理的方法进行林格曼级数检测的系统.通过对汽车尾气进行拍照处 ...

  9. 页面相似度检测,对SEO起到什么作用?

    一.页面相似度检测是什么意思 所谓的相似度检测就是利用工具进行A页面与B页面的指纹对比,(一般来说也有站内相似页面,但我们可以避免而站外数据量众多我们必须借助相似度检测工具)包括: 1.字数 2.语义 ...

最新文章

  1. bzoj千题计划201:bzoj1820: [JSOI2010]Express Service 快递服务
  2. SpringBoot应用的集成测试
  3. 《Linux菜鸟入门》Linux网络管理
  4. [CF/AT]各大网站网赛 体验部部长第一季度工作报告
  5. monkey测试===通过monkey测试检查app内存泄漏和cpu占用
  6. C语言课后习题(56)
  7. 批标准化 tf.keras.layers.BatchNormalization 参数解析与应用分析
  8. dropzone.js重写断点续传功能
  9. 【UVA11988】Broken Keyboard (模拟链表 or 双端队列+栈)
  10. linux合并mp4,Linux 下使用ffmpeg 将批量合并ts文件,合成mp4格式
  11. 数学悖论与三次数学危机
  12. ICG-PEG-OH 结构式,吲哚菁绿-聚乙二醇-羟基的相关说明
  13. 13种加密与解密算法【一】
  14. css 从右到左滚动,CSS 文字从左到右滚动 (右进左出)
  15. 我所知道的张小龙 by和菜头
  16. creator中关于旋转所使用的欧拉角和四元数
  17. 协同开发冲突怎么解决?
  18. Maven中如何使用tomcat8的插件
  19. js字符串截取函数的三种方式(slice()、substring()、substr())
  20. 微商洗脑广告文案 微商顶级文案大全 整合下载

热门文章

  1. hbuilder新建web apk项目_【CUCS】Ionic利用你喜欢的(html css js) web技术创建跨平台的移动app...
  2. python32位 最大内存_64位windows上的Python 32位内存限制
  3. 数据结构题及c语言版4.31答案,数据结构参考题及答案修正版.doc
  4. python中的模块_python3.0中重载模块
  5. 存储器和 I/O 端口有哪两种编址方式?简要说明各自特点
  6. 将数所有奇数移到数组前java_全国2014年4月自考Java语言程序设计(一)真题
  7. c++:ISO C++ forbids declaration of ‘xxx’ with no type
  8. 【本人秃顶程序员】Java程序员成长三部曲!
  9. javascript第二天学习
  10. Vue.js 的开始!