列舉一些算法對照片、圖像進行類似度對比分析比較

首先: 圖片以下java

           

                                       18.jpg                                                                                    19.jpg算法

           

                                       20.jpg                                                                                    21.jpg數組

                        2.jpgapp

                        3.jpgdom

算法以及對比結果maven

1、獲取灰度像素的比較數組、獲取兩個圖的漢明距離、經過漢明距離計算類似度,取值範圍 [0.0, 1.0]ide

 

 

18/19/20/21 圖片對比以及結果this

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 + "%");
        }
    }
}
ImageContrastUtil

 

2/3  圖片對比以及結果spa

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 + "%");
        }
    }
}
ImageContrastUtil

 

 

2、經過 所謂圖形學當中的直方圖的概念比較.net

 

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 + "%");
            }
        }
    }
}
PhotoDigest

 

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 + "%");
            }
        }
    }
}
PhotoDigest

 

 

3、將圖片轉爲二進制碼,比較像素

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");
        }
    }
}
BMPLoader

 

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");
        }
    }
}
BMPLoader

 

 

4、感知哈希算法(均值哈希算法)比較兩圖的類似性

 

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 + "%");
        }
    }
}
FingerPrint

 

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 + "%");
        }
    }
}
FingerPrint

 

 

經過分析比較:算法方案一、二、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

相關文章
相關標籤/搜索