做者 | a1131825850瘋子
來源 | Python爬蟲scrapy
1.背景python
要識別兩張圖片是否類似,首先咱們可能會區分這兩張圖是人物照,仍是風景照等......對應的風景照是藍天仍是大海......作一系列的分類。算法
從機器學習的的角度來講,首先要提取圖片的特徵,將這些特徵進行分類處理,訓練並創建模型,而後在進行識別。數組
可是讓計算機去區分這些圖片分別是哪一類是很不容易的,不過計算機能夠知道圖像的像素值的,所以,在圖像識別過程當中,經過顏色特徵來識別是類似圖片是咱們經常使用的(固然還有其特徵還有紋理特徵、形狀特徵和空間關係特徵等,這些有分爲直方圖,顏色集,顏色局,聚合向量,相關圖等來計算顏色特徵),app
爲了獲得兩張類似的圖片,在這裏經過如下幾種簡單的計算方式來計算圖片的類似度:機器學習
直方圖計算圖片的類似度scrapy
經過哈希值,漢明距離計算學習
經過圖片的餘弦距離計算spa
經過圖片結構度量計算3d
1、直方圖計算圖片的類似度code
上三張圖片,分別是img1.png, img2.jpg,img.png:
能夠看出上面這三張圖是挺類似的,在顏色上是差很少的,最類似的是哪兩張你們能夠猜猜看,看和咱們計算的是否同樣。
在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)
最後獲得三張圖片的直方圖以下:
圖像的x軸是指的圖片的0~255之間的像素變化,y軸指的是在這0~255像素所佔的比列。
咱們能夠明顯的看出img2與img3的直方圖的變化趨勢是相符的有重合態的,運行結果以下:
經過運行結果知道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
獲得最終的運行結果:
兩種方法的的結果仍是有點差距的,能夠看到img1和img3的結果類似度高些。
不過二者的類似度計算方法以下:
gi和si分別指的是兩條曲線的第i個點。
總結:
利用直方圖計算圖片的類似度時,是按照顏色的全局分佈狀況來看待的,沒法對局部的色彩進行分析,同一張圖片若是轉化成爲灰度圖時,在計算其直方圖時差距就更大了。
爲了解決這個問題,能夠將圖片進行等分,而後在計算圖片的類似度。不過在這裏我就不敘述了,你們自行探討!!!
2、哈希算法計算圖片的類似度
在計算以前咱們先了解一下圖像指紋和漢明距離:
圖像指紋:
圖像指紋和人的指紋同樣,是身份的象徵,而圖像指紋簡單點來說,就是將圖像按照必定的哈希算法,通過運算後得出的一組二進制數字。
漢明距離:
假如一組二進制數據爲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:
dhash:
p_hsah:
經過上面運行的結果能夠看出來,img1和img2的類似度高一些。
3、餘弦類似度(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
最終運行結果:
結果顯示img1和img2的類似度高一些,和計算hash值的漢明距離獲得的結果是相一致的。
4、圖片SSIM(結構類似度量)
SSIM是一種全參考的圖像質量評價指標,分別從亮度、對比度、結構三個方面度量圖像類似性。SSIM取值範圍[0, 1],值越大,表示圖像失真越小。在實際應用中,能夠利用滑動窗將圖像分塊,令分塊總數爲N,考慮到窗口形狀對分塊的影響,採用高斯加權計算每一窗口的均值、方差以及協方差,而後計算對應塊的結構類似度SSIM,最後將平均值做爲兩圖像的結構類似性度量,即平均結構類似性SSIM。
ssim1 = compare_ssim(img1, img2, multichannel=True)
這個是scikit-image庫自帶的一種計算方法
運行結果:
能夠看到img1和img2的類似度高。
好了,以上就是到目前爲止我接觸到的圖片類似度的計算方法,確定還有許多我沒有接觸到的計算方法,你們有須要的能夠參考一下,有其餘方法的你們能夠留言一塊兒探討!!!