前阵子在阮一峰的博客上看到了这篇《相似图片搜索原理》博客,就有一种冲动要将这些原理实现出来了。

Google "相似图片搜索":你可以用一张图片,搜索互联网上所有与它相似的图片。

打开Google图片搜索页面:

点击使用上传一张原图:

点击搜索后,Google将会找出与之相似的图片,图片相似度越高就越排在前面。如:

这种技术的原理是什么?计算机怎么知道两张图片相似呢?

根据Neal Krawetz博士的解释,实现相似图片搜素的关键技术叫做"感知哈希算法"(Perceptualhash algorithm),它的作用是对每张图片生成一个"指纹"(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。

以下是一个最简单的Java实现:

预处理:读取图片

File inputFile = newFile(filename); BufferedImage sourceImage = ImageIO.read(inputFile);//读取图片文件

第一步,缩小尺寸。

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

int width= 8; intheight = 8; // targetW,targetH分别表示目标长和宽 int type= sourceImage.getType();// 图片类型 BufferedImagethumbImage = null; double sx= (double) width / sourceImage.getWidth(); double sy= (double) height / sourceImage.getHeight(); // 将图片宽度和高度都设置成一样,以长度短的为准 if (b) { if(sx > sy) { sx= sy; width= (int) (sx * sourceImage.getWidth()); }else { sy= sx; height= (int) (sy * sourceImage.getHeight()); } } // 自定义图片 if (type== BufferedImage.TYPE_CUSTOM) { // handmade ColorModelcm = sourceImage.getColorModel(); WritableRasterraster = cm.createCompatibleWritableRaster(width, height); booleanalphaPremultiplied = cm.isAlphaPremultiplied(); thumbImage= new BufferedImage(cm, raster, alphaPremultiplied, null); } else { // 已知图片,如jpg,png,gif thumbImage= new BufferedImage(width, height, type); } // 调用画图类画缩小尺寸后的图 Graphics2Dg = target.createGraphics(); //smoother than exlax: g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g.drawRenderedImage(sourceImage,AffineTransform.getScaleInstance(sx, sy)); g.dispose();

第二步,简化色彩。

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

int[]pixels = new int[width * height]; for (inti = 0; i < width; i++) { for(int j = 0; j < height; j++) { pixels[i* height + j] = rgbToGray(thumbImage.getRGB(i, j)); } } /** * 灰度值计算 * @param pixels 彩色RGB值(Red-Green-Blue 红绿蓝) * @return int 灰度值 */ public static int rgbToGray(int pixels) { // int _alpha =(pixels >> 24) & 0xFF; int _red = (pixels >> 16) & 0xFF; int _green = (pixels >> 8) & 0xFF; int _blue = (pixels) & 0xFF; return (int) (0.3 * _red + 0.59 * _green + 0.11 * _blue); }

第三步,计算平均值。

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

int avgPixel= 0; int m = 0; for (int i =0; i < pixels.length; ++i) { m +=pixels[i]; } m = m /pixels.length; avgPixel = m;

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

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

int[] comps= new int[width * height]; for (inti = 0; i < comps.length; i++) { if(pixels[i] >= avgPixel) { comps[i]= 1; }else { comps[i]= 0; } }

第五步,计算哈希值。

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

== 8f373714acfcf4d0

StringBufferhashCode = new StringBuffer(); for (inti = 0; i < comps.length; i+= 4) { intresult = comps[i] * (int) Math.pow(2, 3) + comps[i + 1] * (int) Math.pow(2, 2)+ comps[i + 2] * (int) Math.pow(2, 1) + comps[i + 2]; hashCode.append(binaryToHex(result));//二进制转为16进制 } StringsourceHashCode = hashCode.toString();

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

int difference = 0; int len =sourceHashCode.length(); for (inti = 0; i < len; i++) { if(sourceHashCode.charAt(i) != hashCode.charAt(i)) { difference++; } }

你可以将几张图片放在一起,也计算出他们的汉明距离对比,就可以看看两张图片是否相似。

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

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

以上内容大部分直接从阮一峰的网站上复制过来,想看原著的童鞋可以去在最上面的链接点击进去看。

提供源码下载,源码下载链接:http://download.csdn.net/detail/luohong722/3965112

图片相似原理 - Java实现相关推荐

  1. 图片浏览器java程序_图片浏览器用java实现

    该程序实现了图片的缩放以及浏览 package graphics; /** * 图片的缩放功能实现: *为什么图片不能无限放大,因为Thread的run方法不断在调整. *必须选择jpg或png的图片 ...

  2. 对接天猫接口之获取宝贝主图和购买宝贝对应的SKU图片(Java实现)

    获取宝贝主图,Java实现如下: public static String getDownloadImgURL(String auctionId) throws Exception {        ...

  3. java 图片处理 图片缩略图,java怎么生成图片缩略图,缩小图片,高清图片缩小

    java如何生成图片缩略图,缩小图片,高清图片缩小 可以把图片缩小到理想的倍数,也可以根据自己的需要来具体规定图片转化后的大小 对于类型为jpg的图片来说,只需要三个参数就能转化得到自己想要的图片 参 ...

  4. 使用Thumbnails压缩或放大图片大小(java)

    首先看下缩放图片的核心代码,其实只有一行而已 //ins表示ByteArrayInputStream形式的图片 //scale中的数据就是缩小或者放大的比例,比如小于1则表示压缩,大于1表示放大 // ...

  5. java script的图片隐藏,java和javascript中过滤掉img形式的字符串不显示图片的方法...

    本文实例讲述了java和javascript中过滤掉img形式的字符串不显示图片的方法.分享给大家供大家参考.具体实现方法如下: 1. javascript过滤掉和形式的字符串 复制代码代码如下: 过 ...

  6. 生成最简单的验证码图片的Java代码

    后端代码: package priv.lwx.servlet.sl.web; /*** description** @author liaowenxiong* @date 2022/3/25 09:5 ...

  7. 生成验证码图片的Java代码

    文章目录 验证码演示代码 请求资源路径为什么要添加一个随机数的参数 验证码演示代码 package priv.lwx.javaex.servlet_demo.web.servlet.response; ...

  8. java如何叠加图片_图片叠加效果Java代码实现

    本文实例为大家分享了Java实现图片叠加效果展示的具体代码,供大家参考,具体内容如下 import java.awt.AlphaComposite; import java.awt.Graphics2 ...

  9. java 判断图片合适,Java 判断图片色彩

    package cardshibie; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOut ...

  10. 批量压缩图片软件 JAVA

    批量压缩图片软件 - CompressImages 软件介绍: CompressImages是一款批量压缩图片的软件,它能够将指定文件夹(含子目录)中的所有图片文件进行压缩,并且是在图片不失真的前提下 ...

最新文章

  1. 软件工程硕士和计算机硕士论文题目,计算机硕士毕业论文答辩自述
  2. mac mysql ip访问不了_解决mysql中只能通过localhost访问不能通过ip访问的问题
  3. 什么能在main()函数之前或之后执行
  4. php项目课题,php课题
  5. 怎么实现注解_通透!一口气搞懂注解到底怎么用
  6. java异常代码分析
  7. P3512 [POI2010]PIL-Pilots(单调队列+二分)
  8. 经典论文阅读记录-持续更新
  9. 海明码计算(校验码)
  10. 软考软件设计师下午真题-面向对象的程序设计与实现-享元设计模式(2021年下半年试题六))Java代码讲解
  11. mysql字段动态扩展_数据库动态扩展字段
  12. r语言和python混合_jupyter notebook同时使用python和R语言
  13. android 锤子桌面壁纸,锤子桌面使用技巧|锤子桌面 1.5.1 安卓版_久友下载站_壁纸美化...
  14. 制造执行系统(MES)软件可以增加收入,创造更快的周转时间,提高制造商的质量
  15. Excel中如何使用字符串提取函数LEFT
  16. obs64位捕获yy开播伴侣
  17. 元数据是什么意思_中国股市:股票分红10转10股派5元,你看懂是什么意思了吗?...
  18. python计算差商_用Python求函数的差商
  19. 写出10以内的奇偶数php,幼儿园中班科学活动“认识奇数偶数”
  20. Matlab:Matlab编程语言应用之三维绘图可视化(基础知识点基本函)的使用方法简介、案例实现(三维曲线图机械阻尼振动三维等高线图等案例)之详细攻略

热门文章

  1. Linux -shell 基础
  2. github优秀代码锦集
  3. 如何计算机网络打印机,电脑如何连接网络打印机?网络打印机的连接教程
  4. 打印机脱机了怎么恢复打印
  5. 3.Orangepi PC2 使用busybox制作文件系统
  6. Linux禁用搜狗输入法的简繁切换快捷键
  7. 回归分析结果表格怎么填_excel回归分析结果解读
  8. UGUI 实现屏幕外怪物的指示箭头
  9. [小传]任正非:高中三年的理想只是吃个白面馒头 [zz.IS2120]
  10. python中[::-1][1:2][1::2]的用法