1. 程式人生 > >圖片相似演算法(1)

圖片相似演算法(1)

灰度瞭解:

灰度就是沒有色彩,RGB色彩分量全部相等。如果是一個二值灰度圖象,它的象素值只能為0或1,我們說它的灰度級為2。用個例子來說明吧:一個256級灰度的圖象,如果RGB三個量相同時,如:RGB(100,100,100)就代表灰度為100,RGB(50,50,50)代表灰度為50。
彩色圖象的灰度其實在轉化為黑白影象後的畫素值(是一種廣義的提法),轉化的方法看應用的領域而定,一般按加權的方法轉換,R, G,B 的比一般為3:6:1。
任何顏色都由紅、綠、藍三原色組成,假如原來某點的顏色為RGB(R,G,B),那麼,我們可以通過下面幾種方法,將其轉換為灰度:
1.浮點演算法:Gray=R*0.3+G*0.59+B*0.11
2.整數方法:Gray=(R*30+G*59+B*11)/100
3.移位方法:Gray =(R*77+G*151+B*28)>>8;
4.

平均值法:Gray=(R+G+B)/3;
5.僅取綠色:Gray=G;
通過上述任一種方法求得Gray後,將原來的RGB(R,G,B)中的R,G,B統一用Gray替換,形成新的顏色RGB(Gray,Gray,Gray),用它替換原來的RGB(R,G,B)就是灰度圖了。

感知雜湊演算法

這裡的關鍵技術叫做"感知雜湊演算法"(Perceptual hash algorithm),它的作用是對每張圖片生成一個"指紋"(fingerprint)字串,然後比較不同圖片的指紋。結果越接近,就說明圖片越相似。

下面是一個最簡單的實現:

第一步,縮小尺寸。

將圖片縮小到8x8的尺寸,總共64個畫素。這一步的作用是去除圖片的細節,只保留結構、明暗等基本資訊,摒棄不同尺寸、比例帶來的圖片差異。

 

第二步,簡化色彩。

將縮小後的圖片,轉為64級灰度。也就是說,所有畫素點總共只有64種顏色。

第三步,計算平均值。

計算所有64個畫素的灰度平均值。

第四步,比較畫素的灰度。

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

第五步,計算雜湊值。

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

 =  = 8f373714acfcf4d0

得到指紋以後,就可以對比不同的圖片,看看64位中有多少位是不一樣的。在理論上,這等同於計算"漢明距離"(Hamming distance)。如果不相同的資料位不超過5,就說明兩張圖片很相似;如果大於10,就說明這是兩張不同的圖片。

具體的程式碼實現,可以參見Wote用python語言寫的imgHash.py。程式碼很短,只有53行。使用的時候,第一個引數是基準圖片,第二個引數是用來比較的其他圖片所在的目錄,返回結果是兩張圖片之間不相同的資料位數量(漢明距離)。

這種演算法的優點是簡單快速,不受圖片大小縮放的影響,缺點是圖片的內容不能變更。如果在圖片上加幾個文字,它就認不出來了。所以,它的最佳用途是根據縮圖,找出原圖。

實際應用中,往往採用更強大的pHash演算法和SIFT演算法,它們能夠識別圖片的變形。只要變形程度不超過25%,它們就能匹配原圖。這些演算法雖然更復雜,但是原理與上面的簡便演算法是一樣的,就是先將圖片轉化成Hash字串,然後再進行比較。

昨天,我在isnowfy的網站看到,還有其他兩種方法也很簡單,這裡做一些筆記。

顏色分佈法

每張圖片都可以生成顏色分佈的直方圖(color histogram)。如果兩張圖片的直方圖很接近,就可以認為它們很相似。

任何一種顏色都是由紅綠藍三原色(RGB)構成的,所以上圖共有4張直方圖(三原色直方圖 + 最後合成的直方圖)。

如果每種原色都可以取256個值,那麼整個顏色空間共有1600萬種顏色(256的三次方)。針對這1600萬種顏色比較直方圖,計算量實在太大了,因此需要採用簡化方法。可以將0~255分成四個區:0~63為第0區,64~127為第1區,128~191為第2區,192~255為第3區。這意味著紅綠藍分別有4個區,總共可以構成64種組合(4的3次方)。

任何一種顏色必然屬於這64種組合中的一種,這樣就可以統計每一種組合包含的畫素數量。

上圖是某張圖片的顏色分佈表,將表中最後一欄提取出來,組成一個64維向量(7414, 230, 0, 0, 8, ..., 109, 0, 0, 3415, 53929)。這個向量就是這張圖片的特徵值或者叫"指紋"。

於是,尋找相似圖片就變成了找出與其最相似的向量。這可以用皮爾遜相關係數或者餘弦相似度算出。

內容特徵法

除了顏色構成,還可以從比較圖片內容的相似性入手。

首先,將原圖轉成一張較小的灰度圖片,假定為50x50畫素。然後,確定一個閾值,將灰度圖片轉成黑白圖片。

  

如果兩張圖片很相似,它們的黑白輪廓應該是相近的。於是,問題就變成了,第一步如何確定一個合理的閾值,正確呈現照片中的輪廓?

顯然,前景色與背景色反差越大,輪廓就越明顯。這意味著,如果我們找到一個值,可以使得前景色和背景色各自的"類內差異最小"(minimizing the intra-class variance),或者"類間差異最大"(maximizing the inter-class variance),那麼這個值就是理想的閾值。

1979年,日本學者大津展之證明了,"類內差異最小"與"類間差異最大"是同一件事,即對應同一個閾值。他提出一種簡單的演算法,可以求出這個閾值,這被稱為"大津法"(Otsu's method)。下面就是他的計算方法。

假定一張圖片共有n個畫素,其中灰度值小於閾值的畫素為 n1 個,大於等於閾值的畫素為 n2 個( n1 + n2 = n )。w1 和 w2 表示這兩種畫素各自的比重。

  w1 = n1 / n

  w2 = n2 / n

再假定,所有灰度值小於閾值的畫素的平均值和方差分別為 μ1 和 σ1,所有灰度值大於等於閾值的畫素的平均值和方差分別為 μ2 和 σ2。於是,可以得到

  類內差異 = w1(σ1的平方) + w2(σ2的平方)

  類間差異 = w1w2(μ1-μ2)^2

可以證明,這兩個式子是等價的:得到"類內差異"的最小值,等同於得到"類間差異"的最大值。不過,從計算難度看,後者的計算要容易一些。

下一步用"窮舉法",將閾值從灰度的最低值到最高值,依次取一遍,分別代入上面的算式。使得"類內差異最小"或"類間差異最大"的那個值,就是最終的閾值。具體的例項和Java演算法,請看這裡

有了50x50畫素的黑白縮圖,就等於有了一個50x50的0-1矩陣。矩陣的每個值對應原圖的一個畫素,0表示黑色,1表示白色。這個矩陣就是一張圖片的特徵矩陣。

兩個特徵矩陣的不同之處越少,就代表兩張圖片越相似。這可以用"異或運算"實現(即兩個值之中只有一個為1,則運算結果為1,否則運算結果為0)。對不同圖片的特徵矩陣進行"異或運算",結果中的1越少,就是越相似的圖片。