cda

全國校區

您的位置:首頁 > 精彩閱讀 > python圖像識別之圖片相似度計算

python圖像識別之圖片相似度計算

2019-11-19

<wordsbank_match class='wbmatch' location='/map/python/' style='cursor:pointer;'>python</wordsbank_match>圖像識別之圖片相似度計算

作者 | a1131825850瘋子

來源 | Python爬蟲scrapy

1.背景

要識別兩張圖片是否相似,首先我們可能會區分這兩張圖是人物照,還是風景照等......對應的風景照是藍天還是大海......做一系列的分類。

機器學習的的角度來說,首先要提取圖片的特征,將這些特征進行分類處理,訓練并建立模型,然后在進行識別。

但是讓計算機去區分這些圖片分別是哪一類是很不容易的,不過計算機可以知道圖像的像素值的,因此,在圖像識別過程中,通過顏色特征來識別是相似圖片是我們常用的(當然還有其特征還有紋理特征、形狀特征和空間關系特征等,這些有分為直方圖,顏色集,顏色局,聚合向量,相關圖等來計算顏色特征),

為了得到兩張相似的圖片,在這里通過以下幾種簡單的計算方式來計算圖片的相似度:

直方圖計算圖片的相似度

通過哈希值,漢明距離計算

通過圖片的余弦距離計算

通過圖片結構度量計算

一、直方圖計算圖片的相似度

上三張圖片,分別是img1.png, img2.jpg,img.png:

<wordsbank_match class='wbmatch' location='/map/python/' style='cursor:pointer;'>python</wordsbank_match>圖像識別之圖片相似度計算
<wordsbank_match class='wbmatch' location='/map/python/' style='cursor:pointer;'>python</wordsbank_match>圖像識別之圖片相似度計算
<wordsbank_match class='wbmatch' location='/map/python/' style='cursor:pointer;'>python</wordsbank_match>圖像識別之圖片相似度計算

可以看出上面這三張圖是挺相似的,在顏色上是差不多的,最相似的是哪兩張大家可以猜猜看,看和我們計算的是否一樣。

python中利用opencv中的calcHist()方法獲取其直方圖數據,返回的結果是一個列表:

# 計算圖img1的直方圖H1 = cv2.calcHist([img1], [1], None, [256], [0, 256])H1 = cv2.normalize(H1, H1, 0, 1, cv2.NORM_MINMAX, -1) # 對圖片進行歸一化處理

先計算img1的直方圖,在對其歸一化,最后在分別對img2,img3計算,做歸一化,然后在利用python自帶的compareHist()進行相似度的比較:

 利用compareHist()進行比較相似度similarity1 = cv2.compareHist(H1, H2, 0)

最后得到三張圖片的直方圖如下:

<wordsbank_match class='wbmatch' location='/map/python/' style='cursor:pointer;'>python</wordsbank_match>圖像識別之圖片相似度計算

圖像的x軸是指的圖片的0~255之間的像素變化,y軸指的是在這0~255像素所占的比列。

我們可以明顯的看出img2與img3的直方圖的變化趨勢是相符的有重合態的,運行結果如下:

<wordsbank_match class='wbmatch' location='/map/python/' style='cursor:pointer;'>python</wordsbank_match>圖像識別之圖片相似度計算

通過運行結果知道img2和img3是值是最為相似的(代碼calcImage.py)

上面的是直接調用opencv中的方法來實現的,下面還有自己寫的方法:

首先是將圖片轉化為RGB格式,在這里是用的pillow中的Image來對圖片做處理的:

# 將圖片轉化為RGBdef make_regalur_image(img, size=(64, 64)): gray_image = img.resize(size).convert('RGB') return gray_image

在計算兩圖片的直方圖

# 計算直方圖def hist_similar(lh, rh): assert len(lh) == len(rh) hist = sum(1 - (0 if l == r else float(abs(l - r)) / max(l, r)) for l, r in zip(lh, rh)) / len(lh) return hist

在計算其相似度:

# 計算相似度def calc_similar(li, ri): calc_sim = hist_similar(li.histogram(), ri.histogram())returncalc_sim

得到最終的運行結果:

<wordsbank_match class='wbmatch' location='/map/python/' style='cursor:pointer;'>python</wordsbank_match>圖像識別之圖片相似度計算

兩種方法的的結果還是有點差距的,可以看到img1和img3的結果相似度高些。

不過兩者的相似度計算方法如下:

<wordsbank_match class='wbmatch' location='/map/python/' style='cursor:pointer;'>python</wordsbank_match>圖像識別之圖片相似度計算

gi和si分別指的是兩條曲線的第i個點。

總結:

利用直方圖計算圖片的相似度時,是按照顏色的全局分布情況來看待的,無法對局部的色彩進行分析,同一張圖片如果轉化成為灰度圖時,在計算其直方圖時差距就更大了。

為了解決這個問題,可以將圖片進行等分,然后在計算圖片的相似度。不過在這里我就不敘述了,大家自行探討!!!

二、哈希算法計算圖片的相似度

在計算之前我們先了解一下圖像指紋和漢明距離:

圖像指紋:

圖像指紋和人的指紋一樣,是身份的象征,而圖像指紋簡單點來講,就是將圖像按照一定的哈希算法,經過運算后得出的一組二進制數字。

漢明距離:

假如一組二進制數據為101,另外一組為111,那么顯然把第一組的第二位數據0改成1就可以變成第二組數據111,所以兩組數據的漢明距離就為1。簡單點說,漢明距離就是一組二進制數據變成另一組數據所需的步驟數,顯然,這個數值可以衡量兩張圖片的差異,漢明距離越小,則代表相似度越高。漢明距離為0,即代表兩張圖片完全一樣。

感知哈希算法是一類算法的總稱,包括aHash、pHash、dHash。顧名思義,感知哈希不是以嚴格的方式計算Hash值,而是以更加相對的方式計算哈希值,因為“相似”與否,就是一種相對的判定。

幾種hash值的比較:

aHash:平均值哈希。速度比較快,但是常常不太精確。

pHash:感知哈希。精確度比較高,但是速度方面較差一些。

dHash:差異值哈希。精確度較高,且速度也非常快

1. 平均哈希算法(aHash):

該算法是基于比較灰度圖每個像素與平均值來實現。

aHash的hanming距離步驟:

先將圖片壓縮成8*8的小圖

將圖片轉化為灰度圖

計算圖片的Hash值,這里的hash值是64位,或者是32位01字符串

將上面的hash值轉換為16位的

通過hash值來計算漢明距離

# 均值哈希算法def ahash(image): # 將圖片縮放為8*8的 image = cv2.resize(image, (8, 8), interpolation=cv2.INTER_CUBIC) # 將圖片轉化為灰度圖 gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) # s為像素和初始灰度值,hash_str為哈希值初始值 s = 0 # 遍歷像素累加和 for i in range(8): for j in range(8): s = s + gray[i, j] # 計算像素平均值 avg = s / 64 # 灰度大于平均值為1相反為0,得到圖片的平均哈希值,此時得到的hash值為64位的01字符串 ahash_str = '' for i in range(8): for j in range(8): if gray[i, j] > avg: ahash_str = ahash_str + '1' else: ahash_str = ahash_str + '0' result = '' for i in range(0, 64, 4): result += ''.join('%x' % int(ahash_str[i: i + 4], 2)) # print("ahash值:",result) return result

2.感知哈希算法(pHash):

均值哈希雖然簡單,但是受均值影響大。如果對圖像進行伽馬校正或者進行直方圖均值化都會影響均值,從而影響哈希值的計算。所以就有人提出更健壯的方法,通過離散余弦(DCT)進行低頻提取。

離散余弦變換(DCT)是種圖像壓縮算法,它將圖像從像素域變換到頻率域。然后一般圖像都存在很多冗余和相關性的,所以轉換到頻率域之后,只有很少的一部分頻率分量的系數才不為0,大部分系數都為0(或者說接近于0)。

pHash的計算步驟:

縮小圖片:32 * 32是一個較好的大小,這樣方便DCT計算轉化為灰度圖

計算DCT:利用Opencv中提供的dct()方法,注意輸入的圖像必須是32位浮點型,所以先利用numpy中的float32進行轉換

縮小DCT:DCT計算后的矩陣是32 * 32,保留左上角的8 * 8,這些代表的圖片的最低頻率

計算平均值:計算縮小DCT后的所有像素點的平均值。

進一步減小DCT:大于平均值記錄為1,反之記錄為0.

得到信息指紋:組合64個信息位,順序隨意保持一致性。

最后比對兩張圖片的指紋,獲得漢明距離即可。

# phashdef phash(path): # 加載并調整圖片為32*32的灰度圖片 img = cv2.imread(path) img1 = cv2.resize(img, (32, 32),cv2.COLOR_RGB2GRAY) # 創建二維列表 h, w = img.shape[:2] vis0 = np.zeros((h, w), np.float32) vis0[:h, :w] = img1 # DCT二維變換 # 離散余弦變換,得到dct系數矩陣 img_dct = cv2.dct(cv2.dct(vis0)) img_dct.resize(8,8) # 把list變成一維list img_list = np.array().flatten(img_dct.tolist()) # 計算均值 img_mean = cv2.mean(img_list) avg_list = ['0' if i<img_mean else '1' for i in img_list] return ''.join(['%x' % int(''.join(avg_list[x:x+4]),2) for x in range(0,64,4)])

3. 差異值哈希算法(dHash):

相比pHash,dHash的速度要快的多,相比aHash,dHash在效率幾乎相同的情況下的效果要更好,它是基于漸變實現的。

dHash的hanming距離步驟:

先將圖片壓縮成9*8的小圖,有72個像素點

將圖片轉化為灰度圖

計算差異值:dHash算法工作在相鄰像素之間,這樣每行9個像素之間產生了8個不同的差異,一共8行,則產生了64個差異值,或者是32位01字符串。

獲得指紋:如果左邊的像素比右邊的更亮,則記錄為1,否則為0.

通過hash值來計算漢明距離

# 差異值哈希算法def dhash(image): # 將圖片轉化為8*8 image = cv2.resize(image, (9, 8), interpolation=cv2.INTER_CUBIC) # 將圖片轉化為灰度圖 gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) dhash_str = '' for i in range(8): for j in range(8): if gray[i, j] > gray[i, j + 1]: dhash_str = dhash_str + '1' else: dhash_str = dhash_str + '0' result = '' for i in range(0, 64, 4): result += ''.join('%x' % int(dhash_str[i: i + 4], 2)) # print("dhash值",result)returnresult

4. 計算哈希值差異

#計算兩個哈希值之間的差異def campHash(hash1, hash2): n = 0 # hash長度不同返回-1,此時不能比較 if len(hash1) != len(hash2): return -1 # 如果hash長度相同遍歷長度 for i in range(len(hash1)): if hash1[i] != hash2[i]: n = n + 1 return n

最終的運行結果:

aHash:

<wordsbank_match class='wbmatch' location='/map/python/' style='cursor:pointer;'>python</wordsbank_match>圖像識別之圖片相似度計算

dhash:

<wordsbank_match class='wbmatch' location='/map/python/' style='cursor:pointer;'>python</wordsbank_match>圖像識別之圖片相似度計算

p_hsah:

<wordsbank_match class='wbmatch' location='/map/python/' style='cursor:pointer;'>python</wordsbank_match>圖像識別之圖片相似度計算

通過上面運行的結果可以看出來,img1和img2的相似度高一些。

三、余弦相似度(cosin)

把圖片表示成一個向量,通過計算向量之間的余弦距離來表征兩張圖片的相似度。

1. 對圖片進行歸一化處理

# 對圖片進行統一化處理def get_thum(image, size=(64, 64), greyscale=False): # 利用image對圖像大小重新設置, Image.ANTIALIAS為高質量的 image = image.resize(size, Image.ANTIALIAS) if greyscale: # 將圖片轉換為L模式,其為灰度圖,其每個像素用8個bit表示 image = image.convert('L') return image

2. 計算余弦距離

# 計算圖片的余弦距離def image_similarity_vectors_via_numpy(image1, image2): image1 = get_thum(image1) image2 = get_thum(image2) images = [image1, image2] vectors = [] norms = [] for image in images: vector = [] for pixel_tuple in image.getdata(): vector.append(average(pixel_tuple)) vectors.append(vector) # linalg=linear(線性)+algebra(代數),norm則表示范數 # 求圖片的范數?? norms.append(linalg.norm(vector, 2)) a, b = vectors a_norm, b_norm = norms # dot返回的是點積,對二維數組(矩陣)進行計算 res = dot(a / a_norm, b / b_norm)returnres

最終運行結果:

<wordsbank_match class='wbmatch' location='/map/python/' style='cursor:pointer;'>python</wordsbank_match>圖像識別之圖片相似度計算

結果顯示img1和img2的相似度高一些,和計算hash值的漢明距離得到的結果是相一致的。

四、圖片SSIM(結構相似度量)

SSIM是一種全參考的圖像質量評價指標,分別從亮度、對比度、結構三個方面度量圖像相似性。SSIM取值范圍[0, 1],值越大,表示圖像失真越小。在實際應用中,可以利用滑動窗將圖像分塊,令分塊總數為N,考慮到窗口形狀對分塊的影響,采用高斯加權計算每一窗口的均值、方差以及協方差,然后計算對應塊的結構相似度SSIM,最后將平均值作為兩圖像的結構相似性度量,即平均結構相似性SSIM。

ssim1 = compare_ssim(img1, img2, multichannel=True)

這個是scikit-image庫自帶的一種計算方法

運行結果:

<wordsbank_match class='wbmatch' location='/map/python/' style='cursor:pointer;'>python</wordsbank_match>圖像識別之圖片相似度計算

可以看到img1和img2的相似度高。

好了,以上就是到目前為止我接觸到的圖片相似度的計算方法,肯定還有許多我沒有接觸到的計算方法,大家有需要的可以參考一下,有其他方法的大家可以留言一起探討!!!

完 謝謝觀看

分享
收藏

OK
3d彩经网免费预测