以圖搜圖實現之均值哈希

前言

最近在逛淘寶時發現了淘寶的圖片搜索功能,多是我太Low了這個技術點已經實現很長時間了。想一想本身能不能實現這個功能,起初我是這麼想的,對兩張圖片從左上角的第一個像素點一直比較到右下角的最後一個像素點,並在比較時記錄它們的類似度,多是我太天真了(主要仍是知識限制了想象),這樣作有不少問題,好比說兩張圖片大小不一致、核心要素點的位置不一樣等...最終只得藉助網絡了,找到了一種叫作均值哈希的算法(Average hash algorithm),接下來具體闡述它的基本思路以及適用場景。php

均值哈希的基本思路

一、縮小尺寸:html

去除圖片的高頻和細節的最快方法是縮小圖片,將圖片縮小到8x8的尺寸,總共64個像素。不要保持縱橫比,只需將其變成8乘8的正方形。這樣就能夠比較任意大小的圖片,摒棄不一樣尺寸、比例帶來的圖片差別。算法

二、簡化色彩:網絡

將8乘8的小圖片轉換成灰度圖像。函數

三、計算平均值:spa

計算全部64個像素的灰度平均值。code

四、比較像素的灰度:cdn

將每一個像素的灰度,與平均值進行比較。大於或等於平均值,記爲1;小於平均值,記爲0。htm

五、計算hash值:blog

將上一步的比較結果,組合在一塊兒,就構成了一個64位的整數,這就是這張圖片的指紋。組合的次序並不重要,只要保證全部圖片都採用一樣次序就好了。

若是圖片放大或縮小,或改變縱橫比,結果值也不會改變。增長或減小亮度或對比度,或改變顏色,對hash值都不會太大的影響。最大的優勢:計算速度快!

那麼完成了以上步驟,一張圖片就至關於有了本身的"指紋"了,而後就是計算不一樣位的個數,也就是漢明距離(例如1010001與1011101的漢明舉例就是2,也就是不一樣的個數)。

若是漢明距離小於5,則表示有些不一樣,但比較相近,若是漢明距離大於10則代表徹底不一樣的圖片。

以上就是均值哈希的基本實現思路,整體來講是比較簡單的。

C#實現

public class ImageHashHelper
{
    /// <summary>
    /// 獲取縮略圖
    /// </summary>
    /// <returns></returns>
    private static Bitmap GetThumbImage(Image image, int w, int h) {
        Bitmap bitmap = new Bitmap(w, h);
        Graphics g = Graphics.FromImage(bitmap);
        g.DrawImage(image,
            new Rectangle(0, 0, bitmap.Width, bitmap.Height),
            new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
        return bitmap;
    }

    /// <summary>
    /// 將圖片轉換爲灰度圖像
    /// </summary>
    /// <returns></returns>
    private static Bitmap ToGray(Bitmap bmp) {
        for (int i = 0; i < bmp.Width; i++)
        {
            for (int j = 0; j < bmp.Height; j++)
            {
                //獲取該點的像素的RGB的顏色
                Color color = bmp.GetPixel(i, j);
                //利用公式計算灰度值
                int gray = (int)(color.R * 0.3 + color.G * 0.59 + color.B * 0.11);//計算方式1
                //int gray1 = (int)((color.R + color.G + color.B) / 3.0M);//計算方式2
                Color newColor = Color.FromArgb(gray, gray, gray);
                bmp.SetPixel(i, j, newColor);
            }
        }
        return bmp;
    }

    /// <summary>
    /// 獲取圖片的均值哈希
    /// </summary>
    /// <returns></returns>
    public static int[] GetAvgHash(Bitmap bitmap) {
        Bitmap newBitmap = ToGray(GetThumbImage(bitmap, 8, 8));
        int[] code = new int[64];
        //計算全部64個像素的灰度平均值。
        List<int> allGray = new List<int>();
        for (int row = 0; row < bitmap.Width; row++)
        {
            for (int col = 0; col < bitmap.Height; col++)
            {
                allGray.Add(newBitmap.GetPixel(row, col).R);
            }
        }
        double avg = allGray.Average(a => a);//拿到平均值
        //比較像素的灰度
        for (int i = 0; i < allGray.Count; i++)
        {
            code[i] = allGray[i] >= avg ? 1 : 0;//將比較結果進行組合
        }
        //返回結果
        return code;
    }

    /// <summary>
    /// 對兩個AvgHash進行比較
    /// </summary>
    /// <returns></returns>
    public static int Compare(int[] code1, int[] code2) {
        int v = 0;
        for (int i = 0; i < 64; i++)
        {
            if (code1[i] == code2[i])
            {
                v++;
            }
        }
        return v;
    }
}
複製代碼

這裏咱們在GetAvgHash函數中獲取64個像素的灰度值時直接經過了R來獲取,由於RGB都是同樣的,因此哪個均可以。

灰度值換算:baike.baidu.com/item/灰度值/10…

效果演示:

一、原圖查找

二、徹底馬賽克查找

源碼下載:

點擊下載源碼

最後

均值哈希適合縮略圖查找原圖,人相匹配等並不適用。

參考文獻:

www.hackerfactor.com/blog/index.…

相關文章
相關標籤/搜索