計算圖像相似度——《Python也可以》之一

計算圖像相似度——《Python也可以》之一

聲明:本文最初發表於賴勇浩(戀花蝶)的博客http://blog.csdn.NET/lanphaday,如蒙轉載,敬請確保全文完整,未經同意,不得用於商業用途。

關於《Python也可以》系列:這是我打算把這幾年裏做的一些實驗和代碼寫出來,涉及的面比較廣,也比較雜,可能會有圖像處理、檢索等方面的內容,也會有中文分詞、文本分類、拼音、糾錯等內容。毫不掩飾地說:在博客發這系列文章的原因在於宣傳 python ,所以這系列文章都會帶有源碼和相關的測試用例,這也是特色之一。但這系列文章都是“淺嘗輒止”的,不會深入到專屬領域,只是爲了表明 python 功能很強大,不僅適合於web 或者game 開發,也適合於科學研究。


要計算圖像的相似度,肯定是要找出圖像的特徵。這樣跟你描述一個人的面貌:國字臉,濃眉,雙眼皮,直鼻樑,大而厚的嘴脣。Ok,這些特徵決定了這個人跟你的同事、朋友、家人是不是有點像。圖像也一樣,要計算相似度,必須抽象出一些特徵比如藍天白雲綠草。常用的圖像特徵有顏色特徵、紋理特徵、形狀特徵和空間關係特徵等。顏色特徵的算是最常用的,在其中又分爲直方圖、顏色集、顏色矩、聚合向量和相關圖等。直方圖能夠描述一幅圖像中顏色的全局分佈,而且容易理解和實現,所以入門級的圖像相似度計算都是使用它的;作爲一篇示例性的“淺嘗輒止”的文章,我們也不例外。

在進行我們試驗之前,我們需要找到一批圖片來作爲測試用例。我上窮碧落下黃泉,最後終於在我的前同事西門的博客(http://blog.163.com/johnal1 )找到了一系列他在公司組織的年度旅遊時去西藏林芝拍的一組風光圖片(http://blog.163.com/johnal1/blog/static/9394912200812105654784 ),實在是難得之佳品,簡直可以說得到了它們我們的實驗已經完成了90%。哦耶!下面來看一下我們最重要的一組照片(兩張):

找到一組很好的測試圖片之後,我們需要再給 Python 環境安裝一個圖像庫,我的選擇是PIL(Python image library)。PIL 爲 Python 提供了圖像處理功能,並且支持數十種圖像格式。(關於 PIL 的介紹,可以查看我之前的文章《用Python做圖像處理》http://blog.csdn.net/lanphaday/archive/2007/10/28/1852726.aspx )

雖然這兩張圖片大小都是一樣的,但爲了通用性,我們有必要把所有的圖片都統一到特別的規格,在這裏我選擇是的256x256的分辨率。

因爲 PIL 爲 RGB 模式的圖像計算的 histogram 樣點數爲 768,計算量並不算太大,所以本文就直接使用,沒有再作降維處理了。

  6 def make_regalur_image(img, size = (256, 256)):

  7     return img.resize(size).convert('RGB')

轉化爲規則圖像之後,可以調用 img.histogram() 方法獲得直方圖數據,如上文兩圖的直方圖如下:

得到規則圖像之後,圖像的相似度計算就轉化爲直方圖的距離計算了,本文依照如下公式進行直方圖相似度的定量度量:

Sim(G,S)=

,其中G,S爲直方圖,N 爲顏色空間樣點數

轉換爲相應的 Python 代碼如下:

 19 def hist_similar(lh, rh):

 20     assert len(lh) == len(rh)

 21     return sum(1 - (0 if l == r else float(abs(l - r))/max(l, r)) for l, r in zip(lh, rh))/len(lh)

 22    

 23 def calc_similar(li, ri):

 24     return hist_similar(li.histogram(), ri.histogram())

短短十行代碼不到就完成了圖片相似度的計算,再加上從硬盤讀取圖像的函數和測試代碼,也不過二十行上下:

 28 def calc_similar_by_path(lf, rf):

 29     li, ri = make_regalur_image(Image.open(lf)), make_regalur_image(Image.open(rf))

 30     return calc_similar(li, ri)

 31    

 32 if __name__ == '__main__':

 33     path = r'test/TEST%d/%d.JPG'

 34     for i in xrange(1, 7):

 35        print 'test_case_%d: %.3f%%'%(i, calc_similar_by_path('test/TEST%d/%d.JPG'%(i, 1), 'test/TEST%d/%d.JPG'%(i, 2))*100)

那麼這樣做的效果到底怎麼樣呢?且來看看測試結果(測試用例和代碼請猛擊這裏下載):

test_case_1: 63.322%

test_case_2: 66.950%

test_case_3: 51.990%

test_case_4: 70.401%

test_case_5: 32.755%

test_case_6: 42.203%

結合我們肉眼對測試用例的觀察,這個程序工作得還算可以。不過 test_case_4 就暴露了直方圖的缺點:它只是圖像中顏色的全局分佈的描述,無法描述顏色的局部分佈和色彩所處的位置。test_case_4 的規則圖如下:

可以看到它們的色彩局部分佈有相當大的不同,但事實上它們的全局直方圖相當相似:

雖然從直方圖來看兩圖是極其相似的,但上述算法計算出相似度爲70.4%的結果肯定是不可接受的。那麼,怎麼樣才能克服直方圖的缺點呢?答案是把規則圖像分塊,再對相應的小塊進行相似度計算,最後根據各小塊的平均相似度來反映整個圖片的相似度。在實驗中,我們把規則圖像分爲 4x4 塊,每塊的分辨率爲 64x64:

分割圖像的代碼爲:

  9 def split_image(img, part_size = (64, 64)):

 10     w, h = img.size

 11     pw, ph = part_size

 12

 13     assert w % pw == h % ph == 0

 14

 15     return [img.crop((i, j, i+pw, j+ph)).copy() /

 16                 for i in xrange(0, w, pw) /

 17                 for j in xrange(0, h, ph)]

相應地,把計算相似圖的函數calc_similar()修改爲:

 23 def calc_similar(li, ri):

 24 #   return hist_similar(li.histogram(), ri.histogram())

 25     return sum(hist_similar(l.histogram(), r.histogram()) for l, r in zip(split_image(li), split_image(ri))) / 16.0

進行這樣的改進後,算法已經能夠在一定的程序上反映色彩的局倍分佈和顏色所處的位置,可以比較好的彌補全局直方圖算法的不足。新的算法計算出來的結果如下:

test_case_1: 56.273%

test_case_2: 54.925%

test_case_3: 49.326%

test_case_4: 40.254%

test_case_5: 30.776%

test_case_6: 39.460%

可以看到,test_case_4的相似度由 70.4% 下降到 40.25%,基本上跟肉眼的判斷是切合的;另外其它圖像的相似度略有下降,這是因爲加入了位置因子之的影響。從而可見基於分塊的直方圖相似算法是簡單有效的。

       圖像的相似度計算是圖像檢索、識別的基礎,本文只是淺嘗輒止地介紹了其中最基本的計算方法,如果你要學習和研究更好的算法,也請記住 Python 也能幫助你哦~

本實驗的所有代碼和測試用例請猛擊這裏下載,再次感謝提供圖片支持的西門同學。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章