计算图像相似度——《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 也能幫助你哦~
?
本實驗的所有代碼和測試用例請猛擊這里下載,再次感謝提供圖片支持的西門同學。
總結
以上是生活随笔為你收集整理的计算图像相似度——《Python也可以》之一的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 分享一套 python 试题
- 下一篇: (已加马赛克)10 行代码判定色*情*图