1.背景

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

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

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

Python資源共享群:626017123

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

直方圖計算圖片的相似度

透過雜湊值,漢明距離計算

透過圖片的餘弦距離計算

透過圖片結構度量計算

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

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

Python影象識別,圖片相似度計算!

Python影象識別,圖片相似度計算!

Python影象識別,圖片相似度計算!

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

在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)

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

Python影象識別,圖片相似度計算!

影象的x軸是指的圖片的0~255之間的畫素變化,y軸指的是在這0~255畫素所佔的比列。

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

Python影象識別,圖片相似度計算!

透過執行結果知道img2和img3是值是最為相似的(程式碼calcImage。py)

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

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

# 將圖片轉化為RGB

def 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())

return calc_sim

得到最終的執行結果:

Python影象識別,圖片相似度計算!

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

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

Python影象識別,圖片相似度計算!

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個資訊位,順序隨意保持一致性。

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

# phash

def 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

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)

return result

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:

Python影象識別,圖片相似度計算!

dhash:

Python影象識別,圖片相似度計算!

p_hsah:

Python影象識別,圖片相似度計算!

透過上面執行的結果可以看出來,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)

return res

最終執行結果:

Python影象識別,圖片相似度計算!

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

四、圖片SSIM(

結構相似度量

SSIM是一種全參考的影象質量評價指標,分別從亮度、對比度、結構三個方面度量影象相似性。

SSIM取值範圍[0, 1],值越大,表示影象失真越小。在實際應用中,可以利用滑動窗將影象分塊,令分塊總數為N,考慮到視窗形狀對分塊的影響,採用高斯加權計算每一視窗的均值、方差以及協方差,然後計算對應塊的結構相似度SSIM,最後將平均值作為兩影象的結構相似性度量,即平均結構相似性SSIM。

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

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

執行結果:

Python影象識別,圖片相似度計算!

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

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