JAVA圖片類似度判斷(2)感知哈希算法

這裏用到的關鍵技術叫作」感知哈希算法」(Perceptual hash algorithm),它的做用是對每張圖片生成一個」指紋」(fingerprint)字符串,而後比較不一樣圖片的指紋。結果越接近,就說明圖片越類似。
下面是一個最簡單的實現:
第一步,縮小尺寸。
將圖片縮小到8×8的尺寸,總共64個像素。這一步的做用是去除圖片的細節,只保留結構、明暗等基本信息,摒棄不一樣尺寸、比例帶來的圖片差別。
第二步,簡化色彩。
將縮小後的圖片,轉爲64級灰度。也就是說,全部像素點總共只有64種顏色。
第三步,計算平均值
計算全部64個像素的灰度平均值。
第四步,比較像素的灰度。
將每一個像素的灰度,與平均值進行比較。大於或等於平均值,記爲1;小於平均值,記爲0。
第五步,計算哈希值。
將上一步的比較結果,組合在一塊兒,就構成了一個64位的整數,這就是這張圖片的指紋。組合的次序並不重要,只要保證全部圖片都採用一樣次序就好了。
獲得指紋之後,就能夠對比不一樣的圖片,看看64位中有多少位是不同的。在理論上,這等同於計算」漢明距離」(Hamming distance)。若是不相同的數據位不超過5,就說明兩張圖片很類似;若是大於10,就說明這是兩張不一樣的圖片。
這種算法的優勢是簡單快速,不受圖片大小縮放的影響,缺點是圖片的內容不能變動。若是在圖片上加幾個文字,它就認不出來了。因此,它的最佳用途是根據縮略圖,找出原圖。
實際應用中,每每採用更強大的pHash算法和SIFT算法,它們可以識別圖片的變形。只要變形程度不超過25%,它們就能匹配原圖。這些算法雖然更復雜,可是原理與上面的簡便算法是同樣的,就是先將圖片轉化成Hash字符串,而後再進行比較。java

貼上代碼算法

package com;

import java.awt.image.BufferedImage;
import java.awt.*;

public class ImagePerceptualHashSimilarity {

    public boolean perceptualHashSimilarity(BufferedImage src1, BufferedImage src2) {
        String code1 = this.perceptualHashSimilarity(src1);
        String code2 = this.perceptualHashSimilarity(src2);
        System.out.println(code1);
        System.out.println(code2);
        char[] ch1 = code1.toCharArray();
        char[] ch2 = code2.toCharArray();
        int diffCount = 0;
        for (int i = 0; i < 64; i++) {
            if (ch1[i] != ch2[i]) {
                diffCount++;
            }
        }
        System.out.println(diffCount);
        return diffCount <= 5;
    }

    public String perceptualHashSimilarity(BufferedImage src) {
        int width = 8;
        int height = 8;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
        Graphics graphics = image.createGraphics();
        graphics.drawImage(src, 0, 0, 8, 8, null);
        int total = 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);
                total = total + gray;
            }
        }
        StringBuffer res = new StringBuffer();
        int grayAvg = total / (width * height);
        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);
                if (gray >= grayAvg) {
                    res.append("1");
                } else {
                    res.append("0");
                }
            }
        }
        return res.toString();
    }

    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,算出灰度值
        return a | (rgb << 16) | (rgb << 8) | rgb;//將灰度值送入各個顏色份量
    }
}

調用代碼app

public class Test {
    public static void main(String[]args) throws IOException {
        BufferedImage image1 = ImageIO.read(new File("C:/feihp/work/datatoolkit/jocr/wordspic/timg.jpg"));
        BufferedImage image2 = ImageIO.read(new File("C:/feihp/work/datatoolkit/jocr/wordspic/timg4.jpg"));
        ImagePerceptualHashSimilarity is = new ImagePerceptualHashSimilarity();
        boolean code = is.perceptualHashSimilarity(image1,image2);
        System.out.println(code);
    }
}
相關文章
相關標籤/搜索