ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

图片相似度算法(Java实现)

2020-06-24 23:02:22  阅读:241  来源: 互联网

标签:gray Java int ++ height width 算法 相似 sb


图片相似度算法(Java实现)


在公司实习的时候接到一个任务:对视频抽帧生成的图片做去重处理。所以调研了一些有关计算图像相似度的算法,目前只是用于对图片做去重处理,加以改进或许可以实现以图搜图。下面进入正题:

差值哈希算法

主要流程

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

代码

	/**
     * 差值哈希算法
     * @param src
     * @return
     */
    public char[] dHash(BufferedImage src) {
        int width = 9;
        int height = 8;
        BufferedImage image = this.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 = this.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 rgb
     * @return
     */
    private 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 src 原图片
     * @param height 目标高度
     * @param width 目标宽度
     * @return
     */
    private 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 c1
     * @param c2
     * @return
     */
    private 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 char[] aHash(BufferedImage src) {
        int width = 8;
        int height = 8;
        BufferedImage image = this.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 = this.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. 缩小尺寸为8*8
  2. 简化色彩,转变为灰度图
  3. 计算DCT,得到32*32的DCT系数矩阵
  4. 缩小DCT,只保留左上角的8*8的矩阵
  5. 计算DCT的平均值
  6. 计算哈希值

代码

	 /**
     * 感知哈希算法
     * @param src
     * @return
     */
    public char[] pHash(BufferedImage src) {
        int width = 8;
        int height = 8;
        BufferedImage image = this.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 = this.gray(pixel);
                dctDate[index++] = gray;
            }
        }
        dctDate = DCT.DCT(dctDate,width);
        int avg = DCT.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);
    }

简化色彩,缩小尺寸和比较汉明距离的代码同上。

测试发现Java对图片的读取速度非常慢,所以引入了OpenCV对图片进行处理,以下为OpenCV处理图片的代码:

import com.image.DCT;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import static org.opencv.imgcodecs.Imgcodecs.imread;

public class OpenCV {

    /** 均值哈希算法
     * @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.DCT(dctDate,width);
        int avg = DCT.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;
    }

    /** 计算汉明距离
     * @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;
    }

标签:gray,Java,int,++,height,width,算法,相似,sb
来源: https://blog.csdn.net/dsh153/article/details/106926376

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有