首先: 图片如下
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 图片对比以及结果
PhotoDigest
三、将图片转为二进制码,比较像素
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 图片对比以及结果
BMPLoader
四、感知哈希算法(均值哈希算法)比较两图的相似性
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