首先: 图片如下
18.jpg 19.jpg
20.jpg 21.jpg
2.jpg
3.jpg
算法以及对比结果
一、获取灰度像素的比较数组、获取两个图的汉明距离、通过汉明距离计算相似度,取值范围 [0.0, 1.0]
18/19/20/21 图片对比以及结果
package com.aliyun.picture.demo; import javax.imageio.ImageIO; import java.awt.*; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ColorConvertOp; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; /** * @BelongsProject: maven-demo * @BelongsPackage: com.aliyun.picture.demo * @Author: Guoyh * @CreateTime: 2018-10-12 15:25 * @Description: 对比图片相似度 */ public class ImageContrastUtil { // 对比方法 public static Double imageComparison(InputStream sampleInputStream,InputStream contrastInputStream ) throws IOException { //获取灰度像素的比较数组 int[] photoArrayTwo = getPhotoArray(contrastInputStream); int[] photoArrayOne = getPhotoArray(sampleInputStream); // 获取两个图的汉明距离 int hammingDistance = getHammingDistance(photoArrayOne, photoArrayTwo); // 通过汉明距离计算相似度,取值范围 [0.0, 1.0] double similarity = calSimilarity(hammingDistance); //返回相似精度 return similarity; } // 将任意Image类型图像转换为BufferedImage类型,方便后续操作 public static BufferedImage convertToBufferedFrom(Image srcImage) { BufferedImage bufferedImage = new BufferedImage(srcImage.getWidth(null), srcImage.getHeight(null), BufferedImage.TYPE_INT_ARGB); Graphics2D g = bufferedImage.createGraphics(); g.drawImage(srcImage, null, null); g.dispose(); return bufferedImage; } // 转换至灰度图 public static BufferedImage toGrayscale(Image image) { BufferedImage sourceBuffered = convertToBufferedFrom(image); ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); ColorConvertOp op = new ColorConvertOp(cs, null); BufferedImage grayBuffered = op.filter(sourceBuffered, null); return grayBuffered; } // 缩放至32x32像素缩略图 public static Image scale(Image image) { image = image.getScaledInstance(32, 32, Image.SCALE_SMOOTH); return image; } // 获取像素数组 public static int[] getPixels(Image image) { int width = image.getWidth(null); int height = image.getHeight(null); int[] pixels = convertToBufferedFrom(image).getRGB(0, 0, width, height, null, 0, width); return pixels; } // 获取灰度图的平均像素颜色值 public static int getAverageOfPixelArray(int[] pixels) { Color color; long sumRed = 0; for (int i = 0; i < pixels.length; i++) { color = new Color(pixels[i], true); sumRed += color.getRed(); } int averageRed = (int) (sumRed / pixels.length); return averageRed; } // 获取灰度图的像素比较数组(平均值的离差) public static int[] getPixelDeviateWeightsArray(int[] pixels, final int averageColor) { Color color; int[] dest = new int[pixels.length]; for (int i = 0; i < pixels.length; i++) { color = new Color(pixels[i], true); dest[i] = color.getRed() - averageColor > 0 ? 1 : 0; } return dest; } // 获取两个缩略图的平均像素比较数组的汉明距离(距离越大差异越大) public static int getHammingDistance(int[] a, int[] b) { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i] == b[i] ? 0 : 1; } return sum; } //获取灰度像素的比较数组 public static int[] getPhotoArray(InputStream inputStream) throws IOException { Image image = ImageIO.read(inputStream); // Image image = ImageIO.read(imageFile); // 转换至灰度 image = toGrayscale(image); // 缩小成32x32的缩略图 image = scale(image); // 获取灰度像素数组 int[] pixels = getPixels(image); // 获取平均灰度颜色 int averageColor = getAverageOfPixelArray(pixels); // 获取灰度像素的比较数组(即图像指纹序列) pixels = getPixelDeviateWeightsArray(pixels, averageColor); return pixels; } // 通过汉明距离计算相似度 public static double calSimilarity(int hammingDistance) { int length = 32 * 32; double similarity = (length - hammingDistance) / (double) length; // 使用指数曲线调整相似度结果 similarity = java.lang.Math.pow(similarity, 2); return similarity; } /** * @param args * @return void * @author Guoyh * @date 2018/10/12 15:27 */ public static void main(String[] args) throws Exception { //(数据类型)(最小值+Math.random()*(最大值-最小值+1)) for (int i = 18; i <= 21; i++) { Double imageComparison = imageComparison(new FileInputStream("G:/oss/pk/" + 18 + ".jpg"),new FileInputStream("G:/oss/pk/" +i + ".jpg")); System.out.println("\t" + "\t"+"G:/oss/pk/" + 18 + ".jpg"+"\t"+"与"+"\t"+"G:/oss/pk/" + i + ".jpg"+"\t"+"两张图片的相似度为:" + imageComparison * 100 + "%"); } } }
2/3 图片对比以及结果
package com.aliyun.picture.demo; import javax.imageio.ImageIO; import java.awt.*; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ColorConvertOp; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; /** * @BelongsProject: maven-demo * @BelongsPackage: com.aliyun.picture.demo * @Author: Guoyh * @CreateTime: 2018-10-12 15:25 * @Description: 对比图片相似度 */ public class ImageContrastUtil { // 对比方法 public static Double imageComparison(InputStream sampleInputStream,InputStream contrastInputStream ) throws IOException { //获取灰度像素的比较数组 int[] photoArrayTwo = getPhotoArray(contrastInputStream); int[] photoArrayOne = getPhotoArray(sampleInputStream); // 获取两个图的汉明距离 int hammingDistance = getHammingDistance(photoArrayOne, photoArrayTwo); // 通过汉明距离计算相似度,取值范围 [0.0, 1.0] double similarity = calSimilarity(hammingDistance); //返回相似精度 return similarity; } // 将任意Image类型图像转换为BufferedImage类型,方便后续操作 public static BufferedImage convertToBufferedFrom(Image srcImage) { BufferedImage bufferedImage = new BufferedImage(srcImage.getWidth(null), srcImage.getHeight(null), BufferedImage.TYPE_INT_ARGB); Graphics2D g = bufferedImage.createGraphics(); g.drawImage(srcImage, null, null); g.dispose(); return bufferedImage; } // 转换至灰度图 public static BufferedImage toGrayscale(Image image) { BufferedImage sourceBuffered = convertToBufferedFrom(image); ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); ColorConvertOp op = new ColorConvertOp(cs, null); BufferedImage grayBuffered = op.filter(sourceBuffered, null); return grayBuffered; } // 缩放至32x32像素缩略图 public static Image scale(Image image) { image = image.getScaledInstance(32, 32, Image.SCALE_SMOOTH); return image; } // 获取像素数组 public static int[] getPixels(Image image) { int width = image.getWidth(null); int height = image.getHeight(null); int[] pixels = convertToBufferedFrom(image).getRGB(0, 0, width, height, null, 0, width); return pixels; } // 获取灰度图的平均像素颜色值 public static int getAverageOfPixelArray(int[] pixels) { Color color; long sumRed = 0; for (int i = 0; i < pixels.length; i++) { color = new Color(pixels[i], true); sumRed += color.getRed(); } int averageRed = (int) (sumRed / pixels.length); return averageRed; } // 获取灰度图的像素比较数组(平均值的离差) public static int[] getPixelDeviateWeightsArray(int[] pixels, final int averageColor) { Color color; int[] dest = new int[pixels.length]; for (int i = 0; i < pixels.length; i++) { color = new Color(pixels[i], true); dest[i] = color.getRed() - averageColor > 0 ? 1 : 0; } return dest; } // 获取两个缩略图的平均像素比较数组的汉明距离(距离越大差异越大) public static int getHammingDistance(int[] a, int[] b) { int sum = 0; for (int i = 0; i < a.length; i++) { sum += a[i] == b[i] ? 0 : 1; } return sum; } //获取灰度像素的比较数组 public static int[] getPhotoArray(InputStream inputStream) throws IOException { Image image = ImageIO.read(inputStream); // Image image = ImageIO.read(imageFile); // 转换至灰度 image = toGrayscale(image); // 缩小成32x32的缩略图 image = scale(image); // 获取灰度像素数组 int[] pixels = getPixels(image); // 获取平均灰度颜色 int averageColor = getAverageOfPixelArray(pixels); // 获取灰度像素的比较数组(即图像指纹序列) pixels = getPixelDeviateWeightsArray(pixels, averageColor); return pixels; } // 通过汉明距离计算相似度 public static double calSimilarity(int hammingDistance) { int length = 32 * 32; double similarity = (length - hammingDistance) / (double) length; // 使用指数曲线调整相似度结果 similarity = java.lang.Math.pow(similarity, 2); return similarity; } /** * @param args * @return void * @author Guoyh * @date 2018/10/12 15:27 */ public static void main(String[] args) throws Exception { //(数据类型)(最小值+Math.random()*(最大值-最小值+1)) for (int i = 2; i <= 3; i++) { Double imageComparison = imageComparison(new FileInputStream("G:/oss/pk/" + 2 + ".jpg"),new FileInputStream("G:/oss/pk/" +i + ".jpg")); System.out.println("\t" + "\t"+"G:/oss/pk/" + 2 + ".jpg"+"\t"+"与"+"\t"+"G:/oss/pk/" + i + ".jpg"+"\t"+"两张图片的相似度为:" + imageComparison * 100 + "%"); } } }
二、通过 所谓图形学当中的直方图的概念比较
18/19/20/21 图片对比以及结果
package com.aliyun.picture.demo; /** * @BelongsProject: maven-demo * @BelongsPackage: com.aliyun.picture.demo * @Author: Guoyh * @CreateTime: 2018-10-12 14:55 * @Description: 比较两张图像的相似度 */ import javax.imageio.*; import java.awt.image.*; import java.awt.*;//Color import java.io.*; public class PhotoDigest { public static int[] getData(String name) { try { BufferedImage img = ImageIO.read(new File(name)); BufferedImage slt = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); slt.getGraphics().drawImage(img, 0, 0, 100, 100, null); // ImageIO.write(slt,"jpeg",new File("slt.jpg")); int[] data = new int[256]; for (int x = 0; x < slt.getWidth(); x++) { for (int y = 0; y < slt.getHeight(); y++) { int rgb = slt.getRGB(x, y); Color myColor = new Color(rgb); int r = myColor.getRed(); int g = myColor.getGreen(); int b = myColor.getBlue(); data[(r + g + b) / 3]++; } } // data 就是所谓图形学当中的直方图的概念 return data; } catch (Exception exception) { System.out.println("有文件没有找到,请检查文件是否存在或路径是否正确"); return null; } } public static float compare(int[] s, int[] t) { try { float result = 0F; for (int i = 0; i < 256; i++) { int abs = Math.abs(s[i] - t[i]); int max = Math.max(s[i], t[i]); result += (1 - ((float) abs / (max == 0 ? 1 : max))); } return (result / 256) * 100; } catch (Exception exception) { return 0; } } public static void main(String[] args) throws Exception { //(数据类型)(最小值+Math.random()*(最大值-最小值+1)) for (int i = 18; i <= 21; i++) { float percent = compare(getData("G:/oss/pk/" + 18 + ".jpg"), getData("G:/oss/pk/" + i + ".jpg")); if (percent == 0) { System.out.println("无法比较"); } else { System.out.println("\t"+"G:/oss/pk/" + 18 + ".jpg"+"\t"+"与"+"\t"+"G:/oss/pk/" + i + ".jpg"+"\t"+"两张图片的相似度为:" + percent + "%"); } } } }
2/3 图片对比以及结果
package com.aliyun.picture.demo; /** * @BelongsProject: maven-demo * @BelongsPackage: com.aliyun.picture.demo * @Author: Guoyh * @CreateTime: 2018-10-12 14:55 * @Description: 比较两张图像的相似度 */ import javax.imageio.*; import java.awt.image.*; import java.awt.*;//Color import java.io.*; public class PhotoDigest { public static int[] getData(String name) { try { BufferedImage img = ImageIO.read(new File(name)); BufferedImage slt = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); slt.getGraphics().drawImage(img, 0, 0, 100, 100, null); // ImageIO.write(slt,"jpeg",new File("slt.jpg")); int[] data = new int[256]; for (int x = 0; x < slt.getWidth(); x++) { for (int y = 0; y < slt.getHeight(); y++) { int rgb = slt.getRGB(x, y); Color myColor = new Color(rgb); int r = myColor.getRed(); int g = myColor.getGreen(); int b = myColor.getBlue(); data[(r + g + b) / 3]++; } } // data 就是所谓图形学当中的直方图的概念 return data; } catch (Exception exception) { System.out.println("有文件没有找到,请检查文件是否存在或路径是否正确"); return null; } } public static float compare(int[] s, int[] t) { try { float result = 0F; for (int i = 0; i < 256; i++) { int abs = Math.abs(s[i] - t[i]); int max = Math.max(s[i], t[i]); result += (1 - ((float) abs / (max == 0 ? 1 : max))); } return (result / 256) * 100; } catch (Exception exception) { return 0; } } public static void main(String[] args) throws Exception { //(数据类型)(最小值+Math.random()*(最大值-最小值+1)) for (int i = 2; i <= 3; i++) { float percent = compare(getData("G:/oss/pk/" + 2 + ".jpg"), getData("G:/oss/pk/" + i + ".jpg")); if (percent == 0) { System.out.println("无法比较"); } else { System.out.println("\t"+"G:/oss/pk/" + 2 + ".jpg"+"\t"+"与"+"\t"+"G:/oss/pk/" + i + ".jpg"+"\t"+"两张图片的相似度为:" + percent + "%"); } } } }
三、将图片转为二进制码,比较像素
18/19/20/21 图片对比以及结果
package com.aliyun.picture.demo; /** * 比较两张图片的相似度 * * @BelongsProject: maven-demo * @BelongsPackage: com.aliyun.picture.demo * @Author: Guoyh * @CreateTime: 2018-10-12 14:12 * @Description: 图像比对技术 */ import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; public class BMPLoader { // 改变成二进制码 public static String[][] getPX(String args) { int[] rgb = new int[3]; File file = new File(args); BufferedImage bi = null; try { bi = ImageIO.read(file); } catch (Exception e) { e.printStackTrace(); } int width = bi.getWidth(); int height = bi.getHeight(); int minx = bi.getMinX(); int miny = bi.getMinY(); String[][] list = new String[width][height]; for (int i = minx; i < width; i++) { for (int j = miny; j < height; j++) { int pixel = bi.getRGB(i, j); rgb[0] = (pixel & 0xff0000) >> 16; rgb[1] = (pixel & 0xff00) >> 8; rgb[2] = (pixel & 0xff); list[i][j] = rgb[0] + "," + rgb[1] + "," + rgb[2]; } } return list; } public static void compareImage(String imgPath1, String imgPath2) { String[] images = {imgPath1, imgPath2}; if (images.length == 0) { System.out.println("Usage >java BMPLoader ImageFile.bmp"); System.exit(0); } // 分析图片相似度 begin String[][] list1 = getPX(images[0]); String[][] list2 = getPX(images[1]); int xiangsi = 0; int busi = 0; int i = 0, j = 0; for (String[] strings : list1) { if ((i + 1) == list1.length) { continue; } for (int m = 0; m < strings.length; m++) { try { String[] value1 = list1[i][j].toString().split(","); String[] value2 = list2[i][j].toString().split(","); int k = 0; for (int n = 0; n < value2.length; n++) { if (Math.abs(Integer.parseInt(value1[k]) - Integer.parseInt(value2[k])) < 5) { xiangsi++; } else { busi++; } } } catch (RuntimeException e) { continue; } j++; } i++; } list1 = getPX(images[1]); list2 = getPX(images[0]); i = 0; j = 0; for (String[] strings : list1) { if ((i + 1) == list1.length) { continue; } for (int m = 0; m < strings.length; m++) { try { String[] value1 = list1[i][j].toString().split(","); String[] value2 = list2[i][j].toString().split(","); int k = 0; for (int n = 0; n < value2.length; n++) { if (Math.abs(Integer.parseInt(value1[k]) - Integer.parseInt(value2[k])) < 5) { xiangsi++; } else { busi++; } } } catch (RuntimeException e) { continue; } j++; } i++; } String baifen = ""; try { baifen = ((Double.parseDouble(xiangsi + "") / Double.parseDouble((busi + xiangsi) + "")) + ""); baifen = baifen.substring(baifen.indexOf(".") + 1, baifen.indexOf(".") + 3); } catch (Exception e) { baifen = "0"; } if (baifen.length() <= 0) { baifen = "0"; } if (busi == 0) { baifen = "100"; } System.out.println(imgPath1 + "\t" + " PK " + imgPath2 + "\t" + "相似像素数量:" + "\t" + xiangsi + "\t" + "不相似像素数量:" + "\t" + busi + "\t" + "相似率:" + "\t" + Integer.parseInt(baifen) + "%"); } public static void main(String[] args) { //(数据类型)(最小值+Math.random()*(最大值-最小值+1)) for (int i = 18; i <= 21; i++) { BMPLoader.compareImage("G:/oss/pk/" + 18 + ".jpg", "G:/oss/pk/" + i + ".jpg"); } } }
2/3 图片对比以及结果
package com.aliyun.picture.demo; /** * 比较两张图片的相似度 * * @BelongsProject: maven-demo * @BelongsPackage: com.aliyun.picture.demo * @Author: Guoyh * @CreateTime: 2018-10-12 14:12 * @Description: 图像比对技术 */ import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; public class BMPLoader { // 改变成二进制码 public static String[][] getPX(String args) { int[] rgb = new int[3]; File file = new File(args); BufferedImage bi = null; try { bi = ImageIO.read(file); } catch (Exception e) { e.printStackTrace(); } int width = bi.getWidth(); int height = bi.getHeight(); int minx = bi.getMinX(); int miny = bi.getMinY(); String[][] list = new String[width][height]; for (int i = minx; i < width; i++) { for (int j = miny; j < height; j++) { int pixel = bi.getRGB(i, j); rgb[0] = (pixel & 0xff0000) >> 16; rgb[1] = (pixel & 0xff00) >> 8; rgb[2] = (pixel & 0xff); list[i][j] = rgb[0] + "," + rgb[1] + "," + rgb[2]; } } return list; } public static void compareImage(String imgPath1, String imgPath2) { String[] images = {imgPath1, imgPath2}; if (images.length == 0) { System.out.println("Usage >java BMPLoader ImageFile.bmp"); System.exit(0); } // 分析图片相似度 begin String[][] list1 = getPX(images[0]); String[][] list2 = getPX(images[1]); int xiangsi = 0; int busi = 0; int i = 0, j = 0; for (String[] strings : list1) { if ((i + 1) == list1.length) { continue; } for (int m = 0; m < strings.length; m++) { try { String[] value1 = list1[i][j].toString().split(","); String[] value2 = list2[i][j].toString().split(","); int k = 0; for (int n = 0; n < value2.length; n++) { if (Math.abs(Integer.parseInt(value1[k]) - Integer.parseInt(value2[k])) < 5) { xiangsi++; } else { busi++; } } } catch (RuntimeException e) { continue; } j++; } i++; } list1 = getPX(images[1]); list2 = getPX(images[0]); i = 0; j = 0; for (String[] strings : list1) { if ((i + 1) == list1.length) { continue; } for (int m = 0; m < strings.length; m++) { try { String[] value1 = list1[i][j].toString().split(","); String[] value2 = list2[i][j].toString().split(","); int k = 0; for (int n = 0; n < value2.length; n++) { if (Math.abs(Integer.parseInt(value1[k]) - Integer.parseInt(value2[k])) < 5) { xiangsi++; } else { busi++; } } } catch (RuntimeException e) { continue; } j++; } i++; } String baifen = ""; try { baifen = ((Double.parseDouble(xiangsi + "") / Double.parseDouble((busi + xiangsi) + "")) + ""); baifen = baifen.substring(baifen.indexOf(".") + 1, baifen.indexOf(".") + 3); } catch (Exception e) { baifen = "0"; } if (baifen.length() <= 0) { baifen = "0"; } if (busi == 0) { baifen = "100"; } System.out.println(imgPath1 + "\t" + " PK " + imgPath2 + "\t" + "相似像素数量:" + "\t" + xiangsi + "\t" + "不相似像素数量:" + "\t" + busi + "\t" + "相似率:" + "\t" + Integer.parseInt(baifen) + "%"); } public static void main(String[] args) { //(数据类型)(最小值+Math.random()*(最大值-最小值+1)) for (int i = 2; i <= 3; i++) { BMPLoader.compareImage("G:/oss/pk/" + 2 + ".jpg", "G:/oss/pk/" + i + ".jpg"); } } }
四、感知哈希算法(均值哈希算法)比较两图的相似性
18/19/20/21 图片对比以及结果
package com.aliyun.picture.demo; /** * @BelongsProject: maven-demo * @BelongsPackage: com.aliyun.picture.demo * @Author: Guoyh * @CreateTime: 2018-10-12 15:05 * @Description: 感知哈希算法(均值哈希算法)比较两图的相似性 */ import javax.imageio.ImageIO; 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; import java.io.File; 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; } else { result[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; } else { throw 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; } } @Override public 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(); } @Override public boolean equals(Object obj) { if (obj instanceof FingerPrint) { return Arrays.equals(this.binaryzationMatrix, ((FingerPrint) obj).binaryzationMatrix); } else { return 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)); } /** * @param args * @return void * @author Guoyh * @date 2018/10/12 15:07 */ public static void main(String[] args) throws Exception { for (int i = 18; i <= 21; i++) { FingerPrint fp1 = new FingerPrint(ImageIO.read(new File("G:/oss/pk/" + 18 + ".jpg"))); FingerPrint fp2 = new FingerPrint(ImageIO.read(new File("G:/oss/pk/" + i + ".jpg"))); System.out.println("\t" + "\t"+"G:/oss/pk/" + 18 + ".jpg"+"\t"+"与"+"\t"+"G:/oss/pk/" + i + ".jpg"+"\t"+"两张图片的相似度为:" + fp1.compare(fp2) * 100 + "%"); } } }
2/3 图片对比以及结果
package com.aliyun.picture.demo; /** * @BelongsProject: maven-demo * @BelongsPackage: com.aliyun.picture.demo * @Author: Guoyh * @CreateTime: 2018-10-12 15:05 * @Description: 感知哈希算法(均值哈希算法)比较两图的相似性 */ import javax.imageio.ImageIO; 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; import java.io.File; 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; } else { result[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; } else { throw 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; } } @Override public 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(); } @Override public boolean equals(Object obj) { if (obj instanceof FingerPrint) { return Arrays.equals(this.binaryzationMatrix, ((FingerPrint) obj).binaryzationMatrix); } else { return 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)); } /** * @param args * @return void * @author Guoyh * @date 2018/10/12 15:07 */ public static void main(String[] args) throws Exception { for (int i = 2; i <= 3; i++) { FingerPrint fp1 = new FingerPrint(ImageIO.read(new File("G:/oss/pk/" + 2 + ".jpg"))); FingerPrint fp2 = new FingerPrint(ImageIO.read(new File("G:/oss/pk/" + i + ".jpg"))); System.out.println("\t" + "\t"+"G:/oss/pk/" + 2 + ".jpg"+"\t"+"与"+"\t"+"G:/oss/pk/" + i + ".jpg"+"\t"+"两张图片的相似度为:" + fp1.compare(fp2) * 100 + "%"); } } }
通过分析比较:算法方案1、2、4比较适用于图像相似性比较
算法方案 3 比较适用于图像“找茬”比较
参考:
https://blog.csdn.net/zhuason/article/details/78933250
https://blog.csdn.net/qq_37320823/article/details/80538933
https://blog.csdn.net/10km/article/details/70949272
https://blog.csdn.net/gaoxiang24/article/details/79214834