# 一、
用node.js爬虫爬了不少图片之后,尽管经过了简单的处理,但是图片重复率还是很高的。
本来打算用node继续写一个去重脚本,可是node处理图片的API文档不太足,也不是太简单,自己也不会用,还是回到了java写一个class一下吧。
对于图片来说,暴力hash算法是不可取的,因为只要有一个小像素点的改变,就导致不一样的hash。
网上查验得知可以用DHash算法,一种相对Hash值的算法进行检验去重。于是参照一篇别人的python计算DHash值的代码。
写了一个批量去重的java文件
# 二、代码如下
package util;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
/**
* DHash 算法对图片去重
*
* @author a9043
*/
public class DHashUtil {
/**
* 计算dHash方法
*
* @param file 文件
* @return hash
*/
private static long getDHash(File file) {
//读取文件
BufferedImage srcImage;
try {
srcImage = ImageIO.read(file);
} catch (IOException e) {
e.printStackTrace();
return -1;
}
//文件转成9*8像素,为算法比较通用的长宽
BufferedImage buffImg = new BufferedImage(9, 8, BufferedImage.TYPE_INT_RGB);
buffImg.getGraphics().drawImage(
srcImage.getScaledInstance(9, 8, Image.SCALE_SMOOTH), 0,
0, null);
//图片灰度化,灰度和加亮算法是网上找的,本人不懂图像
int width = buffImg.getWidth();
int height = buffImg.getHeight();
int[] grayPix = new int[width * height];
int i = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int rgb = buffImg.getRGB(x, y);
int r = rgb >> 16 & 0xff;
int g = rgb >> 8 & 0xff;
int b = rgb & 0xff;
int gray = (r * 30 + g * 59 + b * 11) / 100;
grayPix[i++] = gray;
}
}
//开始计算dHash 总共有9*8像素 每行相对有8个差异值 总共有 8*8=64 个
long figure = 0;
for (i = 0; i < 63; i++) {
long bit = grayPix[i] > grayPix[i + 1] ? 1 : 0;
figure |= bit << i;
}
return figure;
}
/**
* 计算海明距离, 计网接触过的
* <p>
* 原本用于编码的检错和纠错的一个算法
* 现在拿来计算相似度,如果差异值小于一定阈值则相似
*
* @param x hash
* @param y hash
* @return 距离
*/
private static long getHammingDistance(long x, long y) {
int cnt = 0;
x = x ^ y;
while (x != 0) {
if (1 == (x & 0x01)) {
cnt++;
}
x = x >> 1;
}
return cnt;
}
/**
* 去重函数
*
* @param files 文件列表
*/
private static void doDistinct(File[] files) {
//以File作Key, hash作val 新建一个map
Map<File, Long> hashMap = Arrays
.stream(files)
.parallel()
.collect(Collectors.toMap(File::getAbsoluteFile, DHashUtil::getDHash));
//有效的hashList
List<Long> hashList = new ArrayList<>();
//去重循环
for (Iterator<Map.Entry<File, Long>> it = hashMap.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<File, Long> entry = it.next();
//如果判定为重复,则移除map且删除
if (entry.getValue() != -1 && hashList.parallelStream().anyMatch(hash -> getHammingDistance(entry.getValue(), hash) < 10)) {
it.remove();
entry.getKey().deleteOnExit();
return;
}
//不重复/第一次 直接加入hashList
hashList.add(entry.getValue());
}
}
//DHashUtil 参数值为待处理文件夹
public static void main(String[] args) {
File file = new File(args[0]);
if (file.isDirectory()) {
doDistinct(file.listFiles());
}
}
}