相似图片搜索–哈希特征值
简介
搜索方法是计算出每一个图片的特征值,然后进行对比。目前的搜索速度较慢,之后可以加入线程池等使搜索速度得到极大的提升,欢迎大家指点。
~~~
思路
~~~
实现方法
~~~
从文件夹读取图片
递归得到文件
public static ArrayList<File> getallfile(File file, ArrayList<File> allfilelist) {
if (file.exists()) {
// 判断文件是否是文件夹,如果是,开始递归
if (file.isDirectory()) {
File f[] = file.listFiles();
for (File file2 : f) {
getallfile(file2, allfilelist);
}
} else {
allfilelist.add(file);
}
}
return allfilelist;
}
~~~
将图片路径放入队列
public Vector<String> getFileByDirectory(String filepath, String endFileName) {
File file = new File(filepath);
ArrayList<File> files = new ArrayList<File>();
// 拿到所有文件
ArrayList<File> allFiles = getallfile(file, files);
Vector<String> imagePathArray=new Vector<String>();
for (int i = 0; i < allFiles.size(); i++) {
if (allFiles.get(i).getName().endsWith(endFileName)) {// 筛选特定的文件
String icon=allFiles.get(i).getAbsolutePath();
imagePathArray.add(icon);
}
}
return imagePathArray;
}
压缩图片
将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。
public static BufferedImage thumb(BufferedImage source, int width, int height, boolean b) {
//返回此 ColorSpace 的颜色空间类型
int type = source.getType();
BufferedImage target = null;
//计算缩小倍数 sx=8/原宽度 sy=8/原高度
double sx = (double) width / source.getWidth();
double sy = (double) height / source.getHeight();
// 没有识别出图像类型,因此它必定是一个自定义图像。需要做处理
if (type == BufferedImage.TYPE_CUSTOM) {
ColorModel cm = source.getColorModel();
WritableRaster raster = cm.createCompatibleWritableRaster(width, height);
boolean alphaPremultiplied = cm.isAlphaPremultiplied();
target = new BufferedImage(cm, raster, alphaPremultiplied, null);
} else
target = new BufferedImage(width, height, type);
Graphics2D g = target.createGraphics();
g.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
//从图像空间到用户空间的转换。
g.drawRenderedImage(source, AffineTransform.getScaleInstance(sx, sy));
g.dispose();
return target;
}
色彩处理
/将缩小后的图片,转为64级灰度。转换以后,所有像素点总共只有64种颜色。之后比较灰度,做出色彩标记。
灰度转换
int[] pixels = new int[width * height];
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
pixels[i * height + j] = ImageHelper.rgbToGray(thumb.getRGB(i, j));
}
}
/**
* 灰度转化
* @param pixels 原数组
* @return
*/
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个像素的灰度平均值。
// 计算所有64个像素的灰度平均值。
int avgPixel = ImageHelper.average(pixels);
public static int average(int[] pixels) {
float m = 0;
for (int i = 0; i < pixels.length; ++i) {
m += pixels[i];
}
m = m / pixels.length;
return (int) m;
}
标记颜色
将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。
int[] comps = new int[width * height];
for (int i = 0; i < comps.length; i++) {
if (pixels[i] >= avgPixel) {
comps[i] = 1;
} else {
comps[i] = 0;
}
}
计算哈希值
将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。
StringBuffer hashCode = new StringBuffer();
for (int i = 0; i < comps.length; i+= 4) {
int result = 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));
}
比较哈希值
/**
* 计算"汉明距离"(Hamming distance)。
* 如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。(总共64位)
* @param sourceHashCode 源hashCode
* @param hashCode 与之比较的hashCode
*/
public static int hammingDistance(String sourceHashCode, String hashCode) {
int difference = 0;
int len = sourceHashCode.length();
for (int i = 0; i < len; i++) {
if (sourceHashCode.charAt(i) != hashCode.charAt(i)) {
difference ++;
}
}
return difference;
}