java实现图片比较

来源:互联网 发布:mac装win10wifi用不了 编辑:程序博客网 时间:2024/06/15 23:15

一 感知哈希

/** * 感知哈希算法 * @author LCJA *1.缩小图片:32 * 32是一个较好的大小,这样方便DCT计算 * *2.转化为灰度图:把缩放后的图片转化为256阶的灰度图。(具体算法见平均哈希算法步骤) * *3.计算DCT:DCT把图片分离成分率的集合 * *4.缩小DCT:DCT是32*32,保留左上角的8*8,这些代表的图片的最低频率 * *5.计算平均值:计算缩小DCT后的所有像素点的平均值。 * *6.进一步减小DCT:大于平均值记录为1,反之记录为0. * *7.得到信息指纹:组合64个信息位,顺序随意保持一致性即可。 * *8.对比指纹:计算两幅图片的指纹,计算汉明距离(从一个指纹到另一个指纹需要变几次),汉明距离越大则说明图片越不一致, *反之,汉明距离越小则说明图片越相似,当距离为0时,说明完全相同。(通常认为距离>10 就是两张完全不同的图片) * */

代码

public class PHash {    /**     * 缩小图片     * @param image     * @param width     * @param height     * @return     */    public static BufferedImage reduceSize(BufferedImage image, int width,int height) {        BufferedImage new_image = null;        double width_times = (double) width / image.getWidth();        double height_times = (double) height / image.getHeight();        if (image.getType() == BufferedImage.TYPE_CUSTOM) {            ColorModel cm = image.getColorModel();            WritableRaster raster = cm.createCompatibleWritableRaster(width,height);            boolean alphaPremultiplied = cm.isAlphaPremultiplied();            new_image = new BufferedImage(cm, raster, alphaPremultiplied, null);        } else{            new_image = new BufferedImage(width, height, image.getType());        }        Graphics2D g = new_image.createGraphics();        g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);        g.drawRenderedImage(image, AffineTransform.getScaleInstance(width_times, height_times));        g.dispose();        return new_image;    }    /**     * 得到灰度值     * @param image     * @return     */    public static double[][] getGrayValue(BufferedImage image) {        int width = image.getWidth();          int height = image.getHeight();          double[][] pixels = new double[width][height];        for(int i = 0; i < width; i++){            for(int j = 0; j < height; j++){                pixels[i][j] = computeGrayValue(image.getRGB(i, j));             }          }          return pixels;    }    /**     * 计算灰度值     * @param pixels     * @return     */    public static double computeGrayValue(int pixel) {        int red = (pixel >> 16) & 0xFF;        int green = (pixel >> 8) & 0xFF;        int blue = (pixel) & 255;        return  0.3 * red + 0.59 * green + 0.11 * blue;    }    public static int avgImage(int[][] smallImage){        int avg=-1;        int sum=0;        int count=0;        for(int i=0;i<smallImage.length;i++){            for(int j=0;j<smallImage[i].length;j++){                sum+=smallImage[i][j];                count++;            }        }        avg=sum/count;        return avg;    }    public static String to64(int avg,int[][] smallImage){        String result = "";        for(int i=0;i<smallImage.length;i++){            for(int j=0;j<smallImage[i].length;j++){                if(smallImage[i][j]>avg){                    result+="1";                }else{                    result+="0";                }            }        }        return result;    }    //越小越相似    public static int compareFingerPrint(String orgin_fingerprint, String compared_fingerprint) {        int count = 0;        for(int i=0;i<orgin_fingerprint.length();i++){            if(orgin_fingerprint.charAt(i)!=compared_fingerprint.charAt(i)){                count++;            }        }        return count;    }    public static String toPhash(BufferedImage image){        //缩小图片        BufferedImage newImage= reduceSize(image, 32, 32);        //转换为256位灰度        double[][] pixels = getGrayValue(image);        //计算DCT        DCT dct =new DCT(25);        int[][] tempDCT = dct.forwardDCT(pixels);        //缩小DCT        int[][] smallImage = dct.dequantitizeImage(tempDCT,false);        //计算平均值        int avg = avgImage(smallImage);        //进一步减小DCT,得到信息指纹        String result =to64(avg, smallImage);        return result;    }    public static void main(String[] args) throws IOException {        String phash1 = toPhash(ImageIO.read(new File("C:/Users/dell/Desktop/11.png")));        String phash2 = toPhash(ImageIO.read(new File("C:/Users/dell/Desktop/12.png")));        System.out.println(phash1);        System.out.println(phash2);        int r1 =compareFingerPrint(phash1,phash2);        String ahash1 =SimilarImageSearch.produceFingerPrint(ImageIO.read(new File("C:/Users/dell/Desktop/11.png")));        String ahash2 =SimilarImageSearch.produceFingerPrint(ImageIO.read(new File("C:/Users/dell/Desktop/12.png")));        System.out.println(ahash1);        System.out.println(ahash2);        int r2 =compareFingerPrint(ahash1,ahash2);        System.out.println("r1="+r1);        System.out.println("r2="+r2);    }

DCT转换,这个是找的现成的,具体算法不太了解

/*** *  *  *//** * <h3><b>DCT - A Java implementation of the Discreet Cosine Transform</b></h3><br><br> * <hr> * The discreet cosine transform converts spatial information to "frequency" or * spectral information, with the X and Y axes representing frequencies of the * signal in different dimensions. This allows for "lossy" compression of image * data by determining which information can be thrown away without compromising * the image.<br><br> * The DCT is used in many compression and transmission codecs, such as JPEG, MPEG * and others. The pixels when transformed are arraged from the most signifigant pixel * to the least signifigant pixel. The DCT functions themselves are lossless. * Pixel loss occurs when the least signifigant pixels are quantitized to 0. * <br><br> * This is NOT a JPEG or JFIF compliant implementation however it could * be with very little extra work. (i.e. A huffman encoding stage needs * to be added.) I am making this source availible in the hopes that * someone will add this functionality to the class, if you do, please * email me! As always, if you have any problems feel free to contact * me. (Or comments, or praise, etc..) * <br><br> * <b>Keep in mind</b> that when compressing color images with this, you will * need to break the image up into it's R G B components and preform * the calculations three times!! * <br><br> * <b>A general algorithim for DCT compression with this class:</b> <br><br> * 1) Create a DCT Object.<br> * 2) Set up your program to read pixel information in 8*8 blocks. See example.<br> * 3) Run the forwardDCT() on all blocks.<br> * 4) Run the quantitizeImage() on all blocks.<br> * 5) If you want, send the information to the imageCompressor().<br><br> * <b>A general algorithim for DCT decompression with this class:</b> <br><br> * 1) Create a DCT Object. <br> * 2) Set up the program to convert compressed data in 8*8 blocks. (if compressed)<br> * 3) Run the data through dequantitizeImage(). <br> * 4) Run the data through inverseDCT().<br> * <br><br> * A complete implementation of an image compressor which compares * the quality of the two images is also availible. See the * JEncode/Decode <a href="JEncode.java">source code.</a> The * <a href="DCT.java">DCT.java source code</a> is also availible. * The implementation is also handy for seeing how to break the image * down, read in 8x8 blocks, and reconstruct it. The <a href="steve.jpg"> * sample graphic</a> is handy to have too. (Bad pic of me) * The best way to get ahold of me is through my * <a href="http://eagle.uccb.ns.ca/steve/home.html">homepage</a>. There's * lots of goodies there too. * @version 1.0.1 August 22nd 1996 * @author <a href="http://eagle.uccb.ns.ca/steve/home.html">Stephen Manley</a> - smanley@eagle.uccb.ns.ca */// <pre> public class DCT{    /**     * DCT Block Size - default 8     */    public int N        = 8;    /**     * Image Quality (0-25) - default 25 (worst image / best compression)     */    public int QUALITY  = 25;    /**     * Image width - must correspond to imageArray bounds - default 320     */    public int ROWS     = 320;    /**     * Image height - must correspond to imageArray bounds - default 200     */    public int COLS     = 240;    /**     * The ZigZag matrix.     */    public int zigZag[][] = new int[64][2];    /**     * Cosine matrix. N * N.     */    public double c[][]        = new double[N][N];    /**     * Transformed cosine matrix, N*N.     */    public double cT[][]       = new double[N][N];    /**     * Quantitization Matrix.     */    public int quantum[][]     = new int[N][N];    /**     * DCT Result Matrix     */    public int resultDCT[][] = new int[ROWS][COLS];    /**     * Constructs a new DCT object. Initializes the cosine transform matrix     * these are used when computing the DCT and it's inverse. This also     * initializes the run length counters and the ZigZag sequence. Note that     * the image quality can be worse than 25 however the image will be     * extemely pixelated, usually to a block size of N.     *     * @param QUALITY The quality of the image (0 best - 25 worst)     *     */    public DCT(int QUALITY)    {        initZigZag();        initMatrix(QUALITY);    }    /**     * This method sets up the quantization matrix using the Quality parameter     * and then sets up the Cosine Transform Matrix and the Transposed CT.     * These are used by the forward and inverse DCT. The RLE encoding     * variables are set up to track the number of consecutive zero values     * that have output or will be input.     * @param quality The quality scaling factor     */    private void initMatrix(int quality)    {        int i;        int j;        for (i = 0; i < N; i++)        {            for (j = 0; j < N; j++)            {                quantum[i][j] = (1 + ((1 + i + j) * quality));            }        }        for (j = 0; j < N; j++)        {            double nn = (double)(N);            c[0][j]  = 1.0 / Math.sqrt(nn);            cT[j][0] = c[0][j];        }        for (i = 1; i < 8; i++)        {            for (j = 0; j < 8; j++)            {                double jj = (double)j;                double ii = (double)i;                c[i][j]  = Math.sqrt(2.0/8.0) * Math.cos(((2.0 * jj + 1.0) * ii * Math.PI) / (2.0 * 8.0));                cT[j][i] = c[i][j];            }        }    }    /**     * Initializes the ZigZag matrix.     */    private void initZigZag()    {        zigZag[0][0] = 0; // 0,0        zigZag[0][1] = 0;        zigZag[1][0] = 0; // 0,1        zigZag[1][1] = 1;        zigZag[2][0] = 1; // 1,0        zigZag[2][1] = 0;        zigZag[3][0] = 2; // 2,0        zigZag[3][1] = 0;        zigZag[4][0] = 1; // 1,1        zigZag[4][1] = 1;        zigZag[5][0] = 0; // 0,2        zigZag[5][1] = 2;        zigZag[6][0] = 0; // 0,3        zigZag[6][1] = 3;        zigZag[7][0] = 1; // 1,2        zigZag[7][1] = 2;        zigZag[8][0] = 2; // 2,1        zigZag[8][1] = 1;        zigZag[9][0] = 3; // 3,0        zigZag[9][1] = 0;        zigZag[10][0] = 4; // 4,0        zigZag[10][1] = 0;        zigZag[11][0] = 3; // 3,1        zigZag[11][1] = 1;        zigZag[12][0] = 2; // 2,2        zigZag[12][1] = 2;        zigZag[13][0] = 1; // 1,3        zigZag[13][1] = 3;        zigZag[14][0] = 0; // 0,4        zigZag[14][1] = 4;        zigZag[15][0] = 0; // 0,5        zigZag[15][1] = 5;        zigZag[16][0] = 1; // 1,4        zigZag[16][1] = 4;        zigZag[17][0] = 2; // 2,3        zigZag[17][1] = 3;        zigZag[18][0] = 3; // 3,2        zigZag[18][1] = 2;        zigZag[19][0] = 4; // 4,1        zigZag[19][1] = 1;        zigZag[20][0] = 5; // 5,0        zigZag[20][1] = 0;        zigZag[21][0] = 6; // 6,0        zigZag[21][1] = 0;        zigZag[22][0] = 5; // 5,1        zigZag[22][1] = 1;        zigZag[23][0] = 4; // 4,2        zigZag[23][1] = 2;        zigZag[24][0] = 3; // 3,3        zigZag[24][1] = 3;        zigZag[25][0] = 2; // 2,4        zigZag[25][1] = 4;        zigZag[26][0] = 1; // 1,5        zigZag[26][1] = 5;        zigZag[27][0] = 0; // 0,6        zigZag[27][1] = 6;        zigZag[28][0] = 0; // 0,7        zigZag[28][1] = 7;        zigZag[29][0] = 1; // 1,6        zigZag[29][1] = 6;        zigZag[30][0] = 2; // 2,5        zigZag[30][1] = 5;        zigZag[31][0] = 3; // 3,4        zigZag[31][1] = 4;        zigZag[32][0] = 4; // 4,3        zigZag[32][1] = 3;        zigZag[33][0] = 5; // 5,2        zigZag[33][1] = 2;        zigZag[34][0] = 6; // 6,1        zigZag[34][1] = 1;        zigZag[35][0] = 7; // 7,0        zigZag[35][1] = 0;        zigZag[36][0] = 7; // 7,1        zigZag[36][1] = 1;        zigZag[37][0] = 6; // 6,2        zigZag[37][1] = 2;        zigZag[38][0] = 5; // 5,3        zigZag[38][1] = 3;        zigZag[39][0] = 4; // 4,4        zigZag[39][1] = 4;        zigZag[40][0] = 3; // 3,5        zigZag[40][1] = 5;        zigZag[41][0] = 2; // 2,6        zigZag[41][1] = 6;        zigZag[42][0] = 1; // 1,7        zigZag[42][1] = 7;        zigZag[43][0] = 2; // 2,7        zigZag[43][1] = 7;        zigZag[44][0] = 3; // 3,6        zigZag[44][1] = 6;        zigZag[45][0] = 4; // 4,5        zigZag[45][1] = 5;        zigZag[46][0] = 5; // 5,4        zigZag[46][1] = 4;        zigZag[47][0] = 6; // 6,3        zigZag[47][1] = 3;        zigZag[48][0] = 7; // 7,2        zigZag[48][1] = 2;        zigZag[49][0] = 7; // 7,3        zigZag[49][1] = 3;        zigZag[50][0] = 6; // 6,4        zigZag[50][1] = 4;        zigZag[51][0] = 5; // 5,5        zigZag[51][1] = 5;        zigZag[52][0] = 4; // 4,6        zigZag[52][1] = 6;        zigZag[53][0] = 3; // 3,7        zigZag[53][1] = 7;        zigZag[54][0] = 4; // 4,7        zigZag[54][1] = 7;        zigZag[55][0] = 5; // 5,6        zigZag[55][1] = 6;        zigZag[56][0] = 6; // 6,5        zigZag[56][1] = 5;        zigZag[57][0] = 7; // 7,4        zigZag[57][1] = 4;        zigZag[58][0] = 7; // 7,5        zigZag[58][1] = 5;        zigZag[59][0] = 6; // 6,6        zigZag[59][1] = 6;        zigZag[60][0] = 5; // 5,7        zigZag[60][1] = 7;        zigZag[61][0] = 6; // 6,7        zigZag[61][1] = 7;        zigZag[62][0] = 7; // 7,6        zigZag[62][1] = 6;        zigZag[63][0] = 7; // 7,7        zigZag[63][1] = 7;    }    /**     * This method preforms a matrix multiplication of the input pixel data matrix     * by the transposed cosine matrix and store the result in a temporary     * N * N matrix. This N * N matrix is then multiplied by the cosine matrix     * and the result is stored in the output matrix.     *     * @param input The Input Pixel Matrix     * @returns output The DCT Result Matrix     */    public int[][] forwardDCT(double input[][])    {        int output[][] = new int[N][N];        double temp[][] = new double[N][N];        double temp1;        int i;        int j;        int k;        for (i = 0; i < N; i++)        {            for (j = 0; j < N; j++)            {                temp[i][j] = 0.0;                for (k = 0; k < N; k++)                {                    temp[i][j] += (((double)(input[i][k]) - 128) * cT[k][j]);                }            }        }        for (i = 0; i < N; i++)        {            for (j = 0; j < N; j++)            {                temp1 = 0.0;                for (k = 0; k < N; k++)                {                    temp1 += (c[i][k] * temp[k][j]);                }                output[i][j] = (int)Math.round(temp1);            }        }        return output;    }    /**     * This method reads in DCT codes  dequanitizes them     * and places them in the correct location. The codes are stored in the     * zigzag format so they need to be redirected to a N * N block through     * simple table lookup. After dequantitization the data needs to be     * run through an inverse DCT.     *     * @param inputData 8x8 Array of quantitized image data     * @param zigzag Boolean switch to enable/disable zigzag path.     * @returns outputData A N * N array of de-quantitized data     *     */    public int[][] dequantitizeImage(int[][] inputData, boolean zigzag)    {        int i = 0;        int j = 0;        int a = 0;        int b = 0;        int row;        int col;        int outputData[][] = new int[N][N];        double result;        if (zigzag)        {            for (i=0; i<(N*N); i++)            {                row = zigZag[i][0];                col = zigZag[i][1];                result = inputData[row][col] * quantum[row][col];                outputData[row][col] = (int)(Math.round(result));            }        }        else        {            for (i=0; i<8; i++)            {                for (j=0; j<8; j++)                {                    result = inputData[i][j] * quantum[i][j];                    outputData[i][j] = (int)(Math.round(result));                }            }        }        return outputData;    }    /**     * This method orders the DCT result matrix into a zigzag pattern and then     * quantitizes the data. The quantitized value is rounded to the nearest integer.     * Pixels which round or divide to zero are the loss associated with     * quantitizing the image. These pixels do not display in the AWT. (null)     * Long runs of zeros and the small ints produced through this technique     * are responsible for the small image sizes. For block sizes < or > 8,     * disable the zigzag optimization. If zigzag is disabled on encode it     * must be disabled on decode as well.     *     * @param inputData 8x8 array of DCT image data.     * @param zigzag Boolean switch to enable/disable zigzag path.     * @returns outputData The quantitized output data     */    public int[][] quantitizeImage(int inputData[][], boolean zigzag)    {        int outputData[][] = new int[N][N];        int i = 0;        int j = 0;        int a = 0;        int b = 0;        int row;        int col;        double result;        if (zigzag)        {            for (i = 0; i < (N*N); i++)            {                row = zigZag[i][0];                col = zigZag[i][1];                result = (inputData[row][col] / quantum[row][col]);                outputData[row][col] = (int)(Math.round(result));            }        }        else        {            for (i=0; i<N; i++)            {                for (j=0; j<N; j++)                {                    result = inputData[i][j] / quantum[i][j];                    outputData[i][j] = (int)(Math.round(result));                }            }        }        return outputData;    }    /**     * <br><br>     * This does not huffman code,     * it uses a minimal run-length encoding scheme. Huffman, Adaptive Huffman     * or arithmetic encoding will give much better preformance. The information     * accepted is quantitized DCT data. The output array should be     * scanned to determine where the end is.     *     * @param image Quantitized image data.     * @returns The string representation of the image. (Compressed)     */    public int[] compressImage(int[] QDCT, boolean log)    {        int i = 0;        int j = 0;        int k = 0;        int temp = 0;        int curPos = 0;        int runCounter = 0;        int imageLength = ROWS*COLS;        int pixel[] = new int[ROWS*COLS];        while((i<imageLength))        {            temp = QDCT[i];            while((i < imageLength) && (temp == QDCT[i]))            {                runCounter++;                i++;            }            if (runCounter > 4)            {                pixel[j] = 255;                j++;                pixel[j] = temp;                j++;                pixel[j] = runCounter;                j++;            }            else            {                for (k=0; k<runCounter; k++)                {                    pixel[j] = temp;                    j++;                }            }            if (log)            {                System.out.print("." + "\r");            }            runCounter = 0;            //i++;        }        return pixel;    }    /**     * This method determines the runs in the input data, decodes it     * and then returnss the corrected matrix. It is used to decode the data     * from the compressImage method. Huffman encoding, Adaptive Huffman or     * Arithmetic will give much better compression.     *     * @param DCT Compressed DCT int array (Expands to whole image).     * @returns The decompressed one dimensional array.     */    public int[] decompressImage(int[] DCT, boolean log)    {        int i = 0;        int j = 0;        int k = 0;        int temp = 0;        int imageLength = ROWS*COLS;        int pixel[] = new int[ROWS*COLS];        while (i < imageLength)        {            temp = DCT[i];            if (k < imageLength)            {                if (temp == 255)                {                    i++;                    int value = DCT[i];                    i++;                    int length = DCT[i];                    for(j=0; j<length; j++)                    {                        pixel[k] = value;                        k++;                    }                }                else                {                    pixel[k] = temp;                    k++;                }            }            if (log)            {                System.out.print(".." + "\r");            }            i++;        }        for (int a = 0; a < 80; a++)        {            System.out.print(pixel[a] + " ");        }        System.out.println();        for (int a = 0; a < 80; a++)        {            System.out.print(DCT[a] + " ");        }        return pixel;    }    /**     * This method is preformed using the reverse of the operations preformed in     * the DCT. This restores a N * N input block to the corresponding output     * block with values scaled to 0 to 255 and then stored in the input block     * of pixels.     *     * @param input N * N input block     * @returns output The pixel array output     */    public int[][] inverseDCT(int input[][])    {        int output[][] = new int[N][N];        double temp[][] = new double[N][N];        double temp1;        int i;        int j;        int k;        for (i=0; i<N; i++)        {            for (j=0; j<N; j++)            {                temp[i][j] = 0.0;                for (k=0; k<N; k++)                {                    temp[i][j] += input[i][k] * c[k][j];                }            }        }        for (i=0; i<N; i++)        {            for (j=0; j<N; j++)            {                temp1 = 0.0;                for (k=0; k<N; k++)                {                    temp1 += cT[i][k] * temp[k][j];                }                temp1 += 128.0;                if (temp1 < 0)                {                    output[i][j] = 0;                }                else if (temp1 > 255)                {                    output[i][j] = 255;                }                else                {                     output[i][j] = (int)Math.round(temp1);                }            }        }        return output;    }    /**     * This method uses bit shifting to convert an array of two bytes     * to a integer (16 bits max). Byte 0 is the most signifigant byte     * and Byte 1 is the least signifigant byte.     * @param bitSet Two bytes to convert     * @returns The constructed integer     */    private int bytetoInt(byte bitSet[])    {        int returnInt = 0;        byte MSB = bitSet[0];        byte LSB = bitSet[1];        returnInt = ((MSB+128) << 8) | (LSB+128);        return returnInt;    }    /**     * This method uses bit shifting to convert an integer to an array     * of two bytes, byte 0, the most signifigant and byte 1, the least     * signifigant.     * @param count The integer to convert. (16 bit max)     * @returns The array of two bytes.     */    private byte[] inttoByte(int count)    {        int LSB = 0;        int MSB = 0;        byte bitSet[] = new byte[2];        if (!(count > 65535))        {            LSB = ((count & 0x000000ff));            MSB = ((count & 0x0000ff00) >> 8);        }        else        {            System.out.println("Integer > than 16 bit. Exiting..");            System.exit(count);        }        bitSet[0] = (byte)(MSB-128);        bitSet[1] = (byte)(LSB-128);        return bitSet;    }}// </pre>

二 平均哈希

// 第一步,缩小尺寸。// 将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。// 第二步,简化色彩。// 将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。// 第三步,计算平均值。// 计算所有64个像素的灰度平均值。// 第四步,比较像素的灰度。// 将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。// 第五步,计算哈希值。// 将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。// 得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。
public class SimilarImageSearch {    private static Logger logger = LoggerFactory.getLogger(SimilarImageSearch.class);    public static List<String> hashCodes;    public static List<String> hashCodes1;    /**     * 计算"汉明距离"(Hamming distance)。     * 如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。     * @param sourceHashCode 源hashCode     * @param hashCode 与之比较的hashCode     */    public static int hammingDistance(String sourceHashCode, String hashCode) {        int difference = 0;        int len = sourceHashCode.length();        for (int i = 0; i < len; i++) {            if (sourceHashCode.charAt(i) != hashCode.charAt(i)) {                difference ++;            }         }        return difference;    }    public static String produceFingerPrint(BufferedImage source) {        int width = 12;        int height = 12;        // 第一步,缩小尺寸。        // 将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。        BufferedImage thumb = ImageUtil.thumb(source, width, height, false);        // 第二步,简化色彩。        // 将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。        int[] pixels = new int[width * height];        for (int i = 0; i < width; i++) {            for (int j = 0; j < height; j++) {                pixels[i * height + j] = ImageUtil.rgbToGray(thumb.getRGB(i, j));            }        }        // 第三步,计算平均值。        // 计算所有64个像素的灰度平均值。        int avgPixel = ImageUtil.average(pixels);        // 第四步,比较像素的灰度。        // 将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。        int[] comps = new int[width * height];        for (int i = 0; i < comps.length; i++) {            if (pixels[i] >= avgPixel) {                comps[i] = 1;            } else {                comps[i] = 0;            }        }        // 第五步,计算哈希值。        // 将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。        StringBuffer hashCode = new StringBuffer();        for (int i = 0; i < comps.length; i+= 4) {            int result = comps[i] * (int) Math.pow(2, 3) + comps[i + 1] * (int) Math.pow(2, 2) + comps[i + 2] * (int) Math.pow(2, 1) + comps[i + 2];            hashCode.append(binaryToHex(result));        }        // 得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。        return hashCode.toString();    }    /**     * 生成图片指纹     * @param filename 文件名     * @return 图片指纹     */    public static String produceFingerPrint(CommonsMultipartFile file) {        String filename = file.getOriginalFilename();        System.out.println(filename);        BufferedImage source = null;        try {            if(filename.endsWith("png")){                source = ImageUtil.readPNGImage(file.getInputStream());            }else if(filename.endsWith("jpg")){                source = ImageUtil.readJPEGImage(file.getInputStream());            }            return produceFingerPrint(source);        } catch (IOException e) {            e.printStackTrace();        }        return null;    }    public static String produceFingerPrint(File file) {        BufferedImage source = ImageUtil.readPNGImage(file);// 读取文件        int width = 12;        int height = 12;        // 第一步,缩小尺寸。        // 将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。        BufferedImage thumb = ImageUtil.thumb(source, width, height, false);        // 第二步,简化色彩。        // 将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。        int[] pixels = new int[width * height];        for (int i = 0; i < width; i++) {            for (int j = 0; j < height; j++) {                pixels[i * height + j] = ImageUtil.rgbToGray(thumb.getRGB(i, j));            }        }        // 第三步,计算平均值。        // 计算所有64个像素的灰度平均值。        int avgPixel = ImageUtil.average(pixels);        // 第四步,比较像素的灰度。        // 将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。        int[] comps = new int[width * height];        for (int i = 0; i < comps.length; i++) {            if (pixels[i] >= avgPixel) {                comps[i] = 1;            } else {                comps[i] = 0;            }        }        // 第五步,计算哈希值。        // 将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。        StringBuffer hashCode = new StringBuffer();        for (int i = 0; i < comps.length; i+= 4) {            int result = comps[i] * (int) Math.pow(2, 3) + comps[i + 1] * (int) Math.pow(2, 2) + comps[i + 2] * (int) Math.pow(2, 1) + comps[i + 2];            hashCode.append(binaryToHex(result));        }        System.out.println(hashCode.toString());        return hashCode.toString();    }    /**     * 十进制转为十六进制     * @param int binary     * @return char hex     */    private static char binaryToHex(int binary) {        char ch = ' ';        switch (binary)        {        case 0:            ch = '0';            break;        case 1:            ch = '1';            break;        case 2:            ch = '2';            break;        case 3:            ch = '3';            break;        case 4:            ch = '4';            break;        case 5:            ch = '5';            break;        case 6:            ch = '6';            break;        case 7:            ch = '7';            break;        case 8:            ch = '8';            break;        case 9:            ch = '9';            break;        case 10:            ch = 'a';            break;        case 11:            ch = 'b';            break;        case 12:            ch = 'c';            break;        case 13:            ch = 'd';            break;        case 14:            ch = 'e';            break;        case 15:            ch = 'f';            break;        default:            ch = ' ';        }        return ch;    }    public static List<String> getHashCodes() {        return hashCodes;    }    public static void setHashCodes(List<String> hashCodes) {        SimilarImageSearch.hashCodes = hashCodes;    }    public static List<String> getHashCodes1() {        return hashCodes1;    }    public static void setHashCodes1(List<String> hashCodes1) {        SimilarImageSearch.hashCodes1 = hashCodes1;    }}

相比较下 平均哈希比较的更准确,感知哈希可以找到更多相似

原创粉丝点击