java三种图像算法

  • 差值哈希算法
  • 均值哈希算法
  • 感知哈希算法
  • 引入了OpenCV对图片进行处理,以下为OpenCV处理图片的代码:
  • 测试执行
  • 附上openvc pom
  • 在额外赠送两种算法
  • 一、
  • 二、


差值哈希算法

主要步骤:
1.缩小尺寸为9*8
2.简化色彩,转变为灰度图
3.计算灰度差值
4.计算哈希值

/**
     * 差值哈希算法
     * @param src
     * @return
     */
    public static char[] dHash(BufferedImage src) {
        int width = 9;
        int height = 8;
        BufferedImage image = resize(src,height,width);
        int[] ints = new int[width * height];
        int index = 0;
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                int pixel = image.getRGB(j, i);
                int gray = gray(pixel);
                ints[index++] = gray;
            }
        }
        StringBuilder builder = new StringBuilder();
        for (int i = 0;i < height;i++){
            for (int j = 0;j < width - 1;j++){
                if (ints[9 * j + i] >= ints[9 * j + i + 1]){
                    builder.append(1);
                }else {
                    builder.append(0);
                }
            }
        }
        return builder.toString().toCharArray();
    }

/**
     * 改变图片尺寸
     * @param src 原图片
     * @param height 目标高度
     * @param width 目标宽度
     * @return
     */
    private static BufferedImage resize(BufferedImage src, int height, int width) {
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
        Graphics graphics = image.createGraphics();
        graphics.drawImage(src, 0, 0, width, height, null);
        return image;
    }
/**
     * 简化色彩
     * @param rgb
     * @return
     */
    private static int gray(int rgb) {
        int a = rgb & 0xff000000;//将最高位(24-31)的信息(alpha通道)存储到a变量
        int r = (rgb >> 16) & 0xff;//取出次高位(16-23)红色分量的信息
        int g = (rgb >> 8) & 0xff;//取出中位(8-15)绿色分量的信息
        int b = rgb & 0xff;//取出低位(0-7)蓝色分量的信息
        rgb = (r * 77 + g * 151 + b * 28) >> 8;    // NTSC luma,算出灰度值
        //(int)(r * 0.3 + g * 0.59 + b * 0.11)
        return a | (rgb << 16) | (rgb << 8) | rgb;//将灰度值送入各个颜色分量
    }

 /**
     * 计算汉明距离
     *
     * @param c1
     * @param c2
     * @return
     */
    private static int diff(char[] c1, char[] c2) {
        int diffCount = 0;
        for (int i = 0; i < c1.length; i++) {
            if (c1[i] != c2[i]) {
                diffCount++;
            }
        }
        return diffCount;
    }

均值哈希算法

主要步骤:
1.缩小尺寸为8*8
2.简化色彩,转变为灰度图
3.计算64个像素的灰度平均值
4.比较每个像素的灰度
5.计算哈希值

/**
     * 均值哈希算法
     * @param src
     * @return
     */
    public static char[] aHash(BufferedImage src) {
        int width = 8;
        int height = 8;
        BufferedImage image = resize(src,height,width);
        int total = 0;
        int[] ints = new int[width * height];
        int index = 0;
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                int pixel = image.getRGB(j, i);
                int gray = gray(pixel);
                ints[index++] = gray;
                total = total + gray;
            }
        }
        StringBuffer res = new StringBuffer();
        int grayAvg = total / (width * height);
        for (int anInt : ints) {
            if (anInt >= grayAvg) {
                res.append("1");
            } else {
                res.append("0");
            }
        }
        return res.toString().toCharArray();
    }

简化色彩,缩小尺寸和比较汉明距离的代码和差值哈希算法里的一样,这里就不赘述了。

感知哈希算法

主要步骤:
1.缩小尺寸为88
2.简化色彩,转变为灰度图
3.计算DCT,得到32
32的DCT系数矩阵
4.缩小DCT,只保留左上角的8*8的矩阵
5.计算DCT的平均值
6.计算哈希值

/**
     * 感知哈希算法
     * @param src
     * @return
     */
    public static char[] pHash(BufferedImage src) {
        int width = 8;
        int height = 8;
        BufferedImage image = resize(src,height,width);
        int[] dctDate = new int[width * height];
        int index = 0;
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                int pixel = image.getRGB(j, i);
                int gray = gray(pixel);
                dctDate[index++] = gray;
            }
        }
        dctDate = DCT(dctDate,width);
        int avg = averageGray(dctDate ,width,height);
        StringBuilder sb = new StringBuilder();
        for(int i=0; i<height; i++) {
            for(int j=0; j<width; j++) {
                if(dctDate[i*height + j] >= avg) {
                    sb.append("1");
                } else {
                    sb.append("0");
                }
            }
        }
        long result;
        if(sb.charAt(0) == '0') {
            result = Long.parseLong(sb.toString(), 2);
        } else {
            //如果第一个字符是1,则表示负数,不能直接转换成long,
            result = 0x8000000000000000l ^ Long.parseLong(sb.substring(1), 2);
        }

        sb = new StringBuilder(Long.toHexString(result));
        if(sb.length() < 16) {
            int n = 16-sb.length();
            for(int i=0; i<n; i++) {
                sb.insert(0, "0");
            }
        }
        return sb.toString().toCharArray();
    }

	/**
     * 离散余弦变换
     * @param pix 原图像的数据矩阵
     * @param n 原图像(n*n)的高或宽
     * @return 变换后的矩阵数组
     */
    public static int[] DCT(int[] pix, int n) {
        double[][] iMatrix = new double[n][n];
        for(int i=0; i<n; i++) {
            for(int j=0; j<n; j++) {
                iMatrix[i][j] = (double)(pix[i*n + j]);
            }
        }
        double[][] quotient = coefficient(n);	//求系数矩阵
        double[][] quotientT = transposingMatrix(quotient, n);	//转置系数矩阵

        double[][] temp = matrixMultiply(quotient, iMatrix, n);
        iMatrix =  matrixMultiply(temp, quotientT, n);

        int newpix[] = new int[n*n];
        for(int i=0; i<n; i++) {
            for(int j=0; j<n; j++) {
                newpix[i*n + j] = (int)iMatrix[i][j];
            }
        }
        return newpix;
    }
    
    /**
     * 矩阵转置
     * @param matrix 原矩阵
     * @param n 矩阵(n*n)的高或宽
     * @return 转置后的矩阵
     */
    private static double[][]  transposingMatrix(double[][] matrix, int n) {
        double nMatrix[][] = new double[n][n];
        for(int i=0; i<n; i++) {
            for(int j=0; j<n; j++) {
                nMatrix[i][j] = matrix[j][i];
            }
        }
        return nMatrix;
    }
    
    /**
     * 求离散余弦变换的系数矩阵
     * @param n n*n矩阵的大小
     * @return 系数矩阵
     */
    private static double[][] coefficient(int n) {
        double[][] coeff = new double[n][n];
        double sqrt = 1.0/Math.sqrt(n);
        for(int i=0; i<n; i++) {
            coeff[0][i] = sqrt;
        }
        for(int i=1; i<n; i++) {
            for(int j=0; j<n; j++) {
                coeff[i][j] = Math.sqrt(2.0/n) * Math.cos(i*Math.PI*(j+0.5)/(double)n);
            }
        }
        return coeff;
    }
    
    /**
     * 矩阵相乘
     * @param A 矩阵A
     * @param B 矩阵B
     * @param n 矩阵的大小n*n
     * @return 结果矩阵
     */
    private static double[][] matrixMultiply(double[][] A, double[][] B, int n) {
        double nMatrix[][] = new double[n][n];
        double t;
        for(int i=0; i<n; i++) {
            for(int j=0; j<n; j++) {
                t = 0;
                for(int k=0; k<n; k++) {
                    t += A[i][k]*B[k][j];
                }
                nMatrix[i][j] = t;
            }
        }
        return nMatrix;
    }

    /**
     * 求灰度图像的均值
     * @param pix 图像的像素矩阵
     * @param w 图像的宽
     * @param h 图像的高
     * @return 灰度均值
     */
    public static int averageGray(int[] pix, int w, int h) {
        int sum = 0;
        for(int i=0; i<h; i++) {
            for(int j=0; j<w; j++) {
                sum = sum+pix[i*w + j];
            }
        }
        return sum/(w*h);
    }

引入了OpenCV对图片进行处理,以下为OpenCV处理图片的代码:

openvc dll文件 百度网盘:https://pan.baidu.com/s/1p2_1D4HUnho9zC_YhEJhjQ,提取码:n49t

//使用opencv前先引用opencv_java340-x64.dll
    static {
        System.load("E:\\opencv\\opencv_java340-x64.dll");
    }

	/**
     * 均值哈希算法
     *
     * @param src 图片路径
     * @return
     */
    public static char[] aHash(String src) {
        StringBuffer res = new StringBuffer();
        try {
            int width = 8;
            int height = 8;
            Mat mat = imread(src);
            Mat resizeMat = new Mat();
            Imgproc.resize(mat, resizeMat, new Size(width, height), 0, 0);
            // 将缩小后的图片转换为64级灰度(简化色彩)
            int total = 0;
            int[] ints = new int[64];
            int index = 0;
            for (int i = 0; i < height; i++) {
                for (int j = 0; j < width; j++) {
                    int gray = gray(resizeMat.get(i, j));
                    ints[index++] = gray;
                    total = total + gray;
                }
            }
            // 计算灰度平均值
            int grayAvg = total / (width * height);
            // 比较像素的灰度
            for (int anInt : ints) {
                if (anInt >= grayAvg) {
                    res.append("1");
                } else {
                    res.append("0");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res.toString().toCharArray();
    }

    /**
     * 感知哈希算法
     *
     * @param src
     * @return
     */
    public static char[] pHash(String src) {
        int width = 8;
        int height = 8;
        Mat mat = imread(src);
        Mat resizeMat = new Mat();
        Imgproc.resize(mat, resizeMat, new Size(width, height), 0, 0);
        int[] dctDate = new int[width * height];
        int index = 0;
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                dctDate[index++] = gray(resizeMat.get(i, j));
            }
        }
        dctDate = DCT(dctDate, width);
        int avg = averageGray(dctDate, width, height);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                if (dctDate[i * height + j] >= avg) {
                    sb.append("1");
                } else {
                    sb.append("0");
                }
            }
        }
        long result;
        if (sb.charAt(0) == '0') {
            result = Long.parseLong(sb.toString(), 2);
        } else {
            //如果第一个字符是1,则表示负数,不能直接转换成long,
            result = 0x8000000000000000l ^ Long.parseLong(sb.substring(1), 2);
        }

        sb = new StringBuilder(Long.toHexString(result));
        if (sb.length() < 16) {
            int n = 16 - sb.length();
            for (int i = 0; i < n; i++) {
                sb.insert(0, "0");
            }
        }
        return sb.toString().toCharArray();
    }

    /**
     * 差值哈希算法
     *
     * @param src
     * @return
     */
    public static char[] dHash(String src) {
        int width = 9;
        int height = 8;
        Mat mat = imread(src);
        Mat resizeMat = new Mat();
        Imgproc.resize(mat, resizeMat, new Size(width, height), 0, 0);
        int[] ints = new int[width * height];
        int index = 0;
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                ints[index++] = gray(resizeMat.get(i, j));
            }
        }
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width - 1; j++) {
                if (ints[9 * j + i] >= ints[9 * j + i + 1]) {
                    builder.append(1);
                } else {
                    builder.append(0);
                }
            }
        }
        return builder.toString().toCharArray();
    }

	/** 简化色彩
     * @param bgr
     * @return
     */
    private static int gray(double[] bgr) {
        int rgb = (int) (bgr[2] * 77 + bgr[1] * 151 + bgr[0] * 28) >> 8;
        int gray = (rgb << 16) | (rgb << 8) | rgb;
        return gray;
    }

测试执行

public static void main(String[] args) throws Exception {
		//openvc 测试
        String str1 = "E:\\tupian\\1234567890.jpeg";
        String str2 = "E:\\tupian\\.jpg";
        char[] a1 = dHash(str1); //差值哈希算法
        char[] a2 = aHash(str1); //均值哈希算法
        char[] a3 = pHash(str1); //感知哈希算法

        char[] a11 = dHash(str2); //差值哈希算法
        char[] a21 = aHash(str2); //均值哈希算法
        char[] a31 = pHash(str2); //感知哈希算法
        Double d = Double.valueOf(diff(a1,a11));
        Double d1 = Double.valueOf(diff(a2,a21));
        Double d2 = Double.valueOf(diff(a3,a31));
        System.out.println(d);
        System.out.println("相似度为d:" + ((64-d)/64));
        System.out.println(d1);
        System.out.println("相似度为d1:" + ((64-d1)/64));
        System.out.println(d2);
        System.out.println("相似度为d2:" + ((16-d2)/16));
        System.out.println("-----------------------------------------------------------------------");
        
        //普通 测试
        BufferedImage src = ImageIO.read(new File("E:\\tupian\\1234567890.jpeg"));
        BufferedImage src1 = ImageIO.read(new File("E:\\tupian\\.jpg"));
        char[] c1 = dHash(src);  //差值哈希算法
        char[] c2 = aHash(src);  //均值哈希算法
        char[] c3 = pHash(src);  //感知哈希算法

        char[] c11 = dHash(src1);  //差值哈希算法
        char[] c21 = aHash(src1);  //均值哈希算法
        char[] c31 = pHash(src1);  //感知哈希算法
        Double d11 = Double.valueOf(diff(c1,c11));
        Double d12 = Double.valueOf(diff(c2,c21));
        Double d13 = Double.valueOf(diff(c3,c31));
        System.out.println(d11);
        System.out.println("相似度为d11:" + ((64-d11)/64));
        System.out.println(d12);
        System.out.println("相似度为d12:" + ((64-d12)/64));
        System.out.println(d13);
        System.out.println("相似度为d11:" + ((16-d13)/16));
//        float percent = compare(getData("C:\\Users\\73153\\Desktop\\tupian\\2\\1623121914(1).png"),
//                getData("C:\\Users\\73153\\Desktop\\tupian\\2\\1623121914(2).png"));
//        if (percent == 0) {
//            System.out.println("无法比较");
//        } else {
//            System.out.println("两张图片的相似度为:" + percent + "%");
//        }
    }

附上openvc pom

<dependency>
        <groupId>org.bytedeco.javacpp-presets</groupId>
        <artifactId>opencv</artifactId>
        <version>4.0.1-1.4.4</version>
    </dependency>

在额外赠送两种算法

一、

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 {
        float percent = compare(getData("C:\\Users\\73153\\Desktop\\tupian\\2\\1623121914(1).png"),
                getData("C:\\Users\\73153\\Desktop\\tupian\\2\\1623121914(2).png"));
        if (percent == 0) {
           System.out.println("无法比较");
        } else {
            System.out.println("两张图片的相似度为:" + percent + "%");
        }
   }

二、

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.File;
import java.io.IOException;

/**
 * 图形相似度计算
 */
public class FigureLineUtiles {
    public static void main(String[] args) throws IOException {
        String strPath = "E:\\tupian\\1234567890.jpeg";
        String strPath1 = "E:\\tupian\\.jpg";
        int[] d = getAverageColor(strPath);
        int[] d1 = getAverageColor(strPath1);
        System.out.println(d);
        System.out.println(d1);
        int hammingDistance = getHammingDistance(d, d1);
        // 通过汉明距离计算相似度,取值范围 [0.0, 1.0]
        double similarity = calSimilarity(hammingDistance);
        System.out.println(similarity);
     }

    // 获取灰度像素的比较数组(即图像指纹序列)
    public static int[] getAverageColor(String strPath) throws IOException {
        // 获取图像
        File imageFile = new File(strPath);
        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;
    }

    // 将任意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 double calSimilarity(int hammingDistance){
     int length = 32*32;
     double similarity = (length - hammingDistance) / (double) length;

     // 使用指数曲线调整相似度结果
     similarity = java.lang.Math.pow(similarity, 2);
     return similarity;
 }
}

最后推荐opencv均值哈希算法,opencv处理速度快。

java中图片与像素矩阵转换 java图像算法_算法