这阵子发现我的图像数据库中有不少内容一样的图像需要剔除,这些内容一样的图像可能尺寸不一样,通道数也可能不一样(灰度/彩色),如下三张图内容完全一样,只是亮度或色彩通道数不同,

于是想到了用google或baidu的识图功能所用到的“感知哈希算法”来搜索数据库内容一样的图像。
通过这篇文章搞清楚了“感知哈希算法”的基本原理,
《三种基于感知哈希算法的相似图像检索技术》,发现原理很简单,很适合我等粗人,呵呵,于是在java下实现了这个算法的代码 :

java实现

package net.gdface.image;import java.awt.Graphics;
import java.awt.Image;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.util.Arrays;/*** 均值哈希实现图像指纹比较* @author guyadong**/
public final class FingerPrint {/*** 图像指纹的尺寸,将图像resize到指定的尺寸,来计算哈希数组 */private static final int HASH_SIZE=16;/*** 保存图像指纹的二值化矩阵*/private final byte[] binaryzationMatrix;public FingerPrint(byte[] hashValue) {if(hashValue.length!=HASH_SIZE*HASH_SIZE)throw new IllegalArgumentException(String.format("length of hashValue must be %d",HASH_SIZE*HASH_SIZE ));this.binaryzationMatrix=hashValue;}public FingerPrint(String hashValue) {this(toBytes(hashValue));}public FingerPrint (BufferedImage src){this(hashValue(src));}private static byte[] hashValue(BufferedImage src){BufferedImage hashImage = resize(src,HASH_SIZE,HASH_SIZE);byte[] matrixGray = (byte[]) toGray(hashImage).getData().getDataElements(0, 0, HASH_SIZE, HASH_SIZE, null); return  binaryzation(matrixGray);}/*** 从压缩格式指纹创建{@link FingerPrint}对象* @param compactValue* @return*/public static FingerPrint createFromCompact(byte[] compactValue){return new FingerPrint(uncompact(compactValue));}public static boolean validHashValue(byte[] hashValue){if(hashValue.length!=HASH_SIZE)return false;for(byte b:hashValue){if(0!=b&&1!=b)return false;         }return true;}public static boolean validHashValue(String hashValue){if(hashValue.length()!=HASH_SIZE)return false;for(int i=0;i<hashValue.length();++i){if('0'!=hashValue.charAt(i)&&'1'!=hashValue.charAt(i))return false;         }return true;}public byte[] compact(){return compact(binaryzationMatrix);}/*** 指纹数据按位压缩* @param hashValue* @return*/private static byte[] compact(byte[] hashValue){byte[] result=new byte[(hashValue.length+7)>>3];byte b=0;for(int i=0;i<hashValue.length;++i){if(0==(i&7)){b=0;}if(1==hashValue[i]){b|=1<<(i&7);}else if(hashValue[i]!=0)throw new IllegalArgumentException("invalid hashValue,every element must be 0 or 1");if(7==(i&7)||i==hashValue.length-1){result[i>>3]=b;}}return result;}/*** 压缩格式的指纹解压缩* @param compactValue* @return*/private static byte[] uncompact(byte[] compactValue){byte[] result=new byte[compactValue.length<<3];for(int i=0;i<result.length;++i){if((compactValue[i>>3]&(1<<(i&7)))==0)result[i]=0;elseresult[i]=1;}return result;      }/*** 字符串类型的指纹数据转为字节数组* @param hashValue* @return*/private static byte[] toBytes(String hashValue){hashValue=hashValue.replaceAll("\\s", "");byte[] result=new byte[hashValue.length()];for(int i=0;i<result.length;++i){char c = hashValue.charAt(i);if('0'==c)result[i]=0;else if('1'==c)result[i]=1;elsethrow new IllegalArgumentException("invalid hashValue String");}return result;}/*** 缩放图像到指定尺寸* @param src* @param width* @param height* @return*/private static BufferedImage resize(Image src,int width,int height){BufferedImage result = new BufferedImage(width, height,  BufferedImage.TYPE_3BYTE_BGR); Graphics g = result.getGraphics();try{g.drawImage(src.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);}finally{g.dispose();}return result;      }/*** 计算均值* @param src* @return*/private static  int mean(byte[] src){long sum=0;// 将数组元素转为无符号整数for(byte b:src)sum+=(long)b&0xff;return (int) (Math.round((float)sum/src.length));}/*** 二值化处理* @param src* @return*/private static byte[] binaryzation(byte[]src){byte[] dst = src.clone();int mean=mean(src);for(int i=0;i<dst.length;++i){// 将数组元素转为无符号整数再比较dst[i]=(byte) (((int)dst[i]&0xff)>=mean?1:0);}return dst;}/*** 转灰度图像* @param src* @return*/private static BufferedImage toGray(BufferedImage src){if(src.getType()==BufferedImage.TYPE_BYTE_GRAY){return src;}else{// 图像转灰BufferedImage grayImage = new BufferedImage(src.getWidth(), src.getHeight(),  BufferedImage.TYPE_BYTE_GRAY);new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null).filter(src, grayImage);return grayImage;       }}@Overridepublic String toString() {return toString(true);}/*** @param multiLine 是否分行* @return*/public String toString(boolean multiLine) {StringBuffer buffer=new StringBuffer();int count=0;for(byte b:this.binaryzationMatrix){buffer.append(0==b?'0':'1');if(multiLine&&++count%HASH_SIZE==0)buffer.append('\n');}return buffer.toString();}@Overridepublic boolean equals(Object obj) {if(obj instanceof FingerPrint){return Arrays.equals(this.binaryzationMatrix,((FingerPrint)obj).binaryzationMatrix);}elsereturn super.equals(obj);}/*** 与指定的压缩格式指纹比较相似度* @param compactValue* @return* @see #compare(FingerPrint)*/public float compareCompact(byte[] compactValue){return compare(createFromCompact(compactValue));}/*** @param hashValue* @return* @see #compare(FingerPrint)*/public float compare(String hashValue){return compare(new FingerPrint(hashValue));}/*** 与指定的指纹比较相似度* @param hashValue* @return* @see #compare(FingerPrint)*/public float compare(byte[] hashValue){return compare(new FingerPrint(hashValue));}/*** 与指定图像比较相似度* @param image2* @return* @see #compare(FingerPrint)*/public float compare(BufferedImage image2){return compare(new FingerPrint(image2));}/*** 比较指纹相似度* @param src* @return * @see #compare(byte[], byte[])*/public float compare(FingerPrint src){if(src.binaryzationMatrix.length!=this.binaryzationMatrix.length)throw new IllegalArgumentException("length of hashValue is mismatch");return compare(binaryzationMatrix,src.binaryzationMatrix);}/*** 判断两个数组相似度,数组长度必须一致否则抛出异常* @param f1* @param f2* @return 返回相似度(0.0~1.0)*/private static float compare(byte[] f1,byte[] f2){if(f1.length!=f2.length)throw new IllegalArgumentException("mismatch FingerPrint length");int sameCount=0;for(int i=0;i<f1.length;++i){if(f1[i]==f2[i])++sameCount;}return (float)sameCount/f1.length;}public static float compareCompact(byte[] f1,byte[] f2){return compare(uncompact(f1),uncompact(f2));}public static float compare(BufferedImage image1,BufferedImage image2){return new FingerPrint(image1).compare(new FingerPrint(image2));}
}

调用示例

junit测试代码

package test;import java.io.File;
import java.io.IOException;import javax.imageio.ImageIO;import org.junit.Test;
import net.gdface.image.FingerPrint;
import net.gdface.image.NotImage;
import net.gdface.image.UnsupportedFormat;public class TestFingerPrint {@Testpublic void testCompare() throws IOException{FingerPrint fp1 = new FingerPrint(ImageIO.read(new File("d:\\tmp\\he049-black.jpg")));FingerPrint fp2 =new FingerPrint(ImageIO.read(new File("d:\\tmp\\he049-gray.jpg")));System.out.println(fp1.toString(true));System.out.printf("sim=%f",fp1.compare(fp2));}
}

java:均值哈希实现图像内容相似度比较相关推荐

  1. Java实现标题相似度计算,文本内容相似度匹配,Java通过SimHash计算标题文本内容相似度

     目录 一.前言 二.关于SimHash 补充知识 一).什么是海明距离 二).海明距离的应用 三).什么是编辑距离 三.SimHash算法的几何意义和原理 一).SimHash算法的几何意义 二). ...

  2. 相似图像搜索的哈希算法思想及实现(差值哈希算法和均值哈希算法)

    图像相似度比较哈希算法: 什么是哈希(Hash)? • 散列函数(或散列算法,又称哈希函数,英语:Hash Function)是一种从任何一种数据中创建小 的数字"指纹"的方法.散 ...

  3. 计算机视觉编程——图像内容分类

    文章目录 图像内容分类 1 K近邻分类法(KNN) 1.1 一个简单的二维示例 1.2 用稠密SIFT作为图像特征 1.3 图像分类:手势识别 2 贝叶斯分类器 3 支持向量机 图像内容分类 1 K近 ...

  4. ML之Hash_HamMingDistance:基于输入图片哈希化(均值哈希+差值哈希)即8*8个元素的单向vector利用汉明距离算法进行判别

    ML之Hash_HamMingDistance:基于输入图片哈希化(均值哈希+差值哈希)即8*8个元素的单向vector利用汉明距离算法进行判别 目录 输出结果 代码实现 相关文章 ML之相似度计算: ...

  5. ML之Hash_EditDistance:基于输入图片哈希化(均值哈希+差值哈希)即8*8个元素的单向vector利用编辑距离算法进行判别

    ML之Hash_EditDistance:基于输入图片哈希化(均值哈希+差值哈希)即8*8个元素的单向vector利用编辑距离算法进行判别 目录 输出结果 代码实现 相关文章 ML之相似度计算:图像数 ...

  6. java 求集合平均数_图像二值化方法介绍(转载学习)

    ImageJ中图像二值化方法介绍 概述 二值图像分析在对象识别与模式匹配中有重要作用,同时也在机器人视觉中也是图像处理的关键步骤,选择不同图像二值化方法得到的结果也不尽相同.本文介绍超过十种以上的基于 ...

  7. java imageio_Java使用imageio 读写图像

    Java中进行图像I/O(即读图片和写图片,不涉及到复杂图像处理)有三个方法: JAI 中的 Image I/O Tools,支持更多图片类型,例如JPEG-LS, JPEG2000, 和 TIFF. ...

  8. 相似图片搜索中的均值哈希(aHash)

    1. 引入 参考1中介绍了相似图片搜索的基本原理,借助milvus(参考2)这样的相似性搜索引擎,我们可以非常快速的实现相似性搜索. 但实现搜索之前,需要把图片转换为特征向量.本文介绍的均值哈希,就是 ...

  9. python计算机视觉学习第8章——图像内容分类

    目录 引言 一. K邻近分类算法(KNN) 1.1 简单二维示例 1.2 用稠密SIFT作为图像特征 1.3 图像分类:手势识别 二 .贝叶斯分类器 三.支持向量机 3.1 使用LibSVM 四. 光 ...

最新文章

  1. [Big Data - Kafka] kafka学习笔记:知识点整理
  2. React使用ES6语法重构组件代码
  3. 小米亿级大数据实时分析与工具选型 【转】
  4. LeetCode # Array # Easy # 217. Contains Duplicate
  5. 高可用MySQL架构设计2
  6. Python学习笔记(十三)
  7. LAMP+LNMP(三)Apache(httpd)安装实践
  8. 数学建模 分支限界算法求解整数规划原理以及编程实现
  9. html留言板代码_接口测试平台代码实现19.首页优化
  10. python打开word内对象_Python操作Word:常用对象介绍
  11. 用 Tenorshare ReiBoot修复iPhone无法开机
  12. 为伍兹乳腺X线摄影数据集开发神经网络
  13. Devcpp(Dev-C++)代码编辑的快捷键
  14. lammps计算聚合物例子_lammps计算金属扩散
  15. 基于北斗高精度定位的运河航道安全导航解决方案
  16. 利用Depends查看win系统下exe程序的依赖项
  17. UI设计师的日常工作流程是怎样的?|优漫教育
  18. 新一代信息技术与互联网的资源观!
  19. 生产管理系统定制开发的项目流程
  20. 翻棋子游戏与Nim游戏

热门文章

  1. Path Finder for Mac(Mac文件管理工具)
  2. SpringBoot Web项目结构梳理
  3. GANs系列:用于图像风格迁移的CycleGAN网络原理解读
  4. php 打印curl请求的header信息和返回的header信息
  5. 苯乙烯基/綦乙烯基/苯丁烯基修饰BODIPY染料|聚合物BODIPY染料定制服务
  6. (14)高通AP10.4开发者指南——WLAN(3.3 分层)
  7. WPS表格 下拉列表+图表 实现动态显示图表
  8. 借条怎么写有法律效力「模板范本」
  9. 《原来我还可以这样活:拆掉思维里的墙》作者:古典
  10. 存储过程 输入输出参数