
作者 | a1131825850瘋子
來源 | Python爬蟲scrapy
1.背景
要識(shí)別兩張圖片是否相似,首先我們可能會(huì)區(qū)分這兩張圖是人物照,還是風(fēng)景照等......對(duì)應(yīng)的風(fēng)景照是藍(lán)天還是大海......做一系列的分類。
從機(jī)器學(xué)習(xí)的的角度來說,首先要提取圖片的特征,將這些特征進(jìn)行分類處理,訓(xùn)練并建立模型,然后在進(jìn)行識(shí)別。
但是讓計(jì)算機(jī)去區(qū)分這些圖片分別是哪一類是很不容易的,不過計(jì)算機(jī)可以知道圖像的像素值的,因此,在圖像識(shí)別過程中,通過顏色特征來識(shí)別是相似圖片是我們常用的(當(dāng)然還有其特征還有紋理特征、形狀特征和空間關(guān)系特征等,這些有分為直方圖,顏色集,顏色局,聚合向量,相關(guān)圖等來計(jì)算顏色特征),
為了得到兩張相似的圖片,在這里通過以下幾種簡(jiǎn)單的計(jì)算方式來計(jì)算圖片的相似度:
直方圖計(jì)算圖片的相似度
通過哈希值,漢明距離計(jì)算
通過圖片的余弦距離計(jì)算
通過圖片結(jié)構(gòu)度量計(jì)算
一、直方圖計(jì)算圖片的相似度
上三張圖片,分別是img1.png, img2.jpg,img.png:
可以看出上面這三張圖是挺相似的,在顏色上是差不多的,最相似的是哪兩張大家可以猜猜看,看和我們計(jì)算的是否一樣。
在python中利用opencv中的calcHist()方法獲取其直方圖數(shù)據(jù),返回的結(jié)果是一個(gè)列表:
# 計(jì)算圖img1的直方圖H1 = cv2.calcHist([img1], [1], None, [256], [0, 256])H1 = cv2.normalize(H1, H1, 0, 1, cv2.NORM_MINMAX, -1) # 對(duì)圖片進(jìn)行歸一化處理
先計(jì)算img1的直方圖,在對(duì)其歸一化,最后在分別對(duì)img2,img3計(jì)算,做歸一化,然后在利用python自帶的compareHist()進(jìn)行相似度的比較:
利用compareHist()進(jìn)行比較相似度similarity1 = cv2.compareHist(H1, H2, 0)
最后得到三張圖片的直方圖如下:
圖像的x軸是指的圖片的0~255之間的像素變化,y軸指的是在這0~255像素所占的比列。
我們可以明顯的看出img2與img3的直方圖的變化趨勢(shì)是相符的有重合態(tài)的,運(yùn)行結(jié)果如下:
通過運(yùn)行結(jié)果知道img2和img3是值是最為相似的(代碼calcImage.py)
上面的是直接調(diào)用opencv中的方法來實(shí)現(xiàn)的,下面還有自己寫的方法:
首先是將圖片轉(zhuǎn)化為RGB格式,在這里是用的pillow中的Image來對(duì)圖片做處理的:
# 將圖片轉(zhuǎn)化為RGBdef make_regalur_image(img, size=(64, 64)): gray_image = img.resize(size).convert('RGB') return gray_image
在計(jì)算兩圖片的直方圖:
# 計(jì)算直方圖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
在計(jì)算其相似度:
# 計(jì)算相似度def calc_similar(li, ri): calc_sim = hist_similar(li.histogram(), ri.histogram())returncalc_sim
得到最終的運(yùn)行結(jié)果:
兩種方法的的結(jié)果還是有點(diǎn)差距的,可以看到img1和img3的結(jié)果相似度高些。
不過兩者的相似度計(jì)算方法如下:
gi和si分別指的是兩條曲線的第i個(gè)點(diǎn)。
總結(jié):
利用直方圖計(jì)算圖片的相似度時(shí),是按照顏色的全局分布情況來看待的,無法對(duì)局部的色彩進(jìn)行分析,同一張圖片如果轉(zhuǎn)化成為灰度圖時(shí),在計(jì)算其直方圖時(shí)差距就更大了。
為了解決這個(gè)問題,可以將圖片進(jìn)行等分,然后在計(jì)算圖片的相似度。不過在這里我就不敘述了,大家自行探討?。。?/span>
二、哈希算法計(jì)算圖片的相似度
在計(jì)算之前我們先了解一下圖像指紋和漢明距離:
圖像指紋:
圖像指紋和人的指紋一樣,是身份的象征,而圖像指紋簡(jiǎn)單點(diǎn)來講,就是將圖像按照一定的哈希算法,經(jīng)過運(yùn)算后得出的一組二進(jìn)制數(shù)字。
漢明距離:
假如一組二進(jìn)制數(shù)據(jù)為101,另外一組為111,那么顯然把第一組的第二位數(shù)據(jù)0改成1就可以變成第二組數(shù)據(jù)111,所以兩組數(shù)據(jù)的漢明距離就為1。簡(jiǎn)單點(diǎn)說,漢明距離就是一組二進(jìn)制數(shù)據(jù)變成另一組數(shù)據(jù)所需的步驟數(shù),顯然,這個(gè)數(shù)值可以衡量?jī)蓮垐D片的差異,漢明距離越小,則代表相似度越高。漢明距離為0,即代表兩張圖片完全一樣。
感知哈希算法是一類算法的總稱,包括aHash、pHash、dHash。顧名思義,感知哈希不是以嚴(yán)格的方式計(jì)算Hash值,而是以更加相對(duì)的方式計(jì)算哈希值,因?yàn)椤跋嗨啤迸c否,就是一種相對(duì)的判定。
幾種hash值的比較:
aHash:平均值哈希。速度比較快,但是常常不太精確。
pHash:感知哈希。精確度比較高,但是速度方面較差一些。
dHash:差異值哈希。精確度較高,且速度也非???/span>
1. 平均哈希算法(aHash):
該算法是基于比較灰度圖每個(gè)像素與平均值來實(shí)現(xiàn)。
aHash的hanming距離步驟:
先將圖片壓縮成8*8的小圖
將圖片轉(zhuǎn)化為灰度圖
計(jì)算圖片的Hash值,這里的hash值是64位,或者是32位01字符串
將上面的hash值轉(zhuǎn)換為16位的
通過hash值來計(jì)算漢明距離
# 均值哈希算法def ahash(image): # 將圖片縮放為8*8的 image = cv2.resize(image, (8, 8), interpolation=cv2.INTER_CUBIC) # 將圖片轉(zhuǎn)化為灰度圖 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] # 計(jì)算像素平均值 avg = s / 64 # 灰度大于平均值為1相反為0,得到圖片的平均哈希值,此時(shí)得到的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):
均值哈希雖然簡(jiǎn)單,但是受均值影響大。如果對(duì)圖像進(jìn)行伽馬校正或者進(jìn)行直方圖均值化都會(huì)影響均值,從而影響哈希值的計(jì)算。所以就有人提出更健壯的方法,通過離散余弦(DCT)進(jìn)行低頻提取。
離散余弦變換(DCT)是種圖像壓縮算法,它將圖像從像素域變換到頻率域。然后一般圖像都存在很多冗余和相關(guān)性的,所以轉(zhuǎn)換到頻率域之后,只有很少的一部分頻率分量的系數(shù)才不為0,大部分系數(shù)都為0(或者說接近于0)。
pHash的計(jì)算步驟:
縮小圖片:32 * 32是一個(gè)較好的大小,這樣方便DCT計(jì)算轉(zhuǎn)化為灰度圖
計(jì)算DCT:利用Opencv中提供的dct()方法,注意輸入的圖像必須是32位浮點(diǎn)型,所以先利用numpy中的float32進(jìn)行轉(zhuǎn)換
縮小DCT:DCT計(jì)算后的矩陣是32 * 32,保留左上角的8 * 8,這些代表的圖片的最低頻率
計(jì)算平均值:計(jì)算縮小DCT后的所有像素點(diǎn)的平均值。
進(jìn)一步減小DCT:大于平均值記錄為1,反之記錄為0.
得到信息指紋:組合64個(gè)信息位,順序隨意保持一致性。
最后比對(duì)兩張圖片的指紋,獲得漢明距離即可。
# phashdef phash(path): # 加載并調(diào)整圖片為32*32的灰度圖片 img = cv2.imread(path) img1 = cv2.resize(img, (32, 32),cv2.COLOR_RGB2GRAY) # 創(chuàng)建二維列表 h, w = img.shape[:2] vis0 = np.zeros((h, w), np.float32) vis0[:h, :w] = img1 # DCT二維變換 # 離散余弦變換,得到dct系數(shù)矩陣 img_dct = cv2.dct(cv2.dct(vis0)) img_dct.resize(8,8) # 把list變成一維list img_list = np.array().flatten(img_dct.tolist()) # 計(jì)算均值 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在效率幾乎相同的情況下的效果要更好,它是基于漸變實(shí)現(xiàn)的。
dHash的hanming距離步驟:
先將圖片壓縮成9*8的小圖,有72個(gè)像素點(diǎn)
將圖片轉(zhuǎn)化為灰度圖
計(jì)算差異值:dHash算法工作在相鄰像素之間,這樣每行9個(gè)像素之間產(chǎn)生了8個(gè)不同的差異,一共8行,則產(chǎn)生了64個(gè)差異值,或者是32位01字符串。
獲得指紋:如果左邊的像素比右邊的更亮,則記錄為1,否則為0.
通過hash值來計(jì)算漢明距離
# 差異值哈希算法def dhash(image): # 將圖片轉(zhuǎn)化為8*8 image = cv2.resize(image, (9, 8), interpolation=cv2.INTER_CUBIC) # 將圖片轉(zhuǎn)化為灰度圖 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. 計(jì)算哈希值差異
#計(jì)算兩個(gè)哈希值之間的差異def campHash(hash1, hash2): n = 0 # hash長(zhǎng)度不同返回-1,此時(shí)不能比較 if len(hash1) != len(hash2): return -1 # 如果hash長(zhǎng)度相同遍歷長(zhǎng)度 for i in range(len(hash1)): if hash1[i] != hash2[i]: n = n + 1 return n
最終的運(yùn)行結(jié)果:
aHash:
dhash:
p_hsah:
通過上面運(yùn)行的結(jié)果可以看出來,img1和img2的相似度高一些。
三、余弦相似度(cosin)
把圖片表示成一個(gè)向量,通過計(jì)算向量之間的余弦距離來表征兩張圖片的相似度。
1. 對(duì)圖片進(jìn)行歸一化處理
# 對(duì)圖片進(jìn)行統(tǒng)一化處理def get_thum(image, size=(64, 64), greyscale=False): # 利用image對(duì)圖像大小重新設(shè)置, Image.ANTIALIAS為高質(zhì)量的 image = image.resize(size, Image.ANTIALIAS) if greyscale: # 將圖片轉(zhuǎn)換為L(zhǎng)模式,其為灰度圖,其每個(gè)像素用8個(gè)bit表示 image = image.convert('L') return image
2. 計(jì)算余弦距離
# 計(jì)算圖片的余弦距離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(代數(shù)),norm則表示范數(shù) # 求圖片的范數(shù)?? norms.append(linalg.norm(vector, 2)) a, b = vectors a_norm, b_norm = norms # dot返回的是點(diǎn)積,對(duì)二維數(shù)組(矩陣)進(jìn)行計(jì)算 res = dot(a / a_norm, b / b_norm)returnres
最終運(yùn)行結(jié)果:
結(jié)果顯示img1和img2的相似度高一些,和計(jì)算hash值的漢明距離得到的結(jié)果是相一致的。
四、圖片SSIM(結(jié)構(gòu)相似度量)
SSIM是一種全參考的圖像質(zhì)量評(píng)價(jià)指標(biāo),分別從亮度、對(duì)比度、結(jié)構(gòu)三個(gè)方面度量圖像相似性。SSIM取值范圍[0, 1],值越大,表示圖像失真越小。在實(shí)際應(yīng)用中,可以利用滑動(dòng)窗將圖像分塊,令分塊總數(shù)為N,考慮到窗口形狀對(duì)分塊的影響,采用高斯加權(quán)計(jì)算每一窗口的均值、方差以及協(xié)方差,然后計(jì)算對(duì)應(yīng)塊的結(jié)構(gòu)相似度SSIM,最后將平均值作為兩圖像的結(jié)構(gòu)相似性度量,即平均結(jié)構(gòu)相似性SSIM。
ssim1 = compare_ssim(img1, img2, multichannel=True)
這個(gè)是scikit-image庫自帶的一種計(jì)算方法
運(yùn)行結(jié)果:
可以看到img1和img2的相似度高。
好了,以上就是到目前為止我接觸到的圖片相似度的計(jì)算方法,肯定還有許多我沒有接觸到的計(jì)算方法,大家有需要的可以參考一下,有其他方法的大家可以留言一起探討?。。?/span>
數(shù)據(jù)分析咨詢請(qǐng)掃描二維碼
若不方便掃碼,搜微信號(hào):CDAshujufenxi
SQL Server 中 CONVERT 函數(shù)的日期轉(zhuǎn)換:從基礎(chǔ)用法到實(shí)戰(zhàn)優(yōu)化 在 SQL Server 的數(shù)據(jù)處理中,日期格式轉(zhuǎn)換是高頻需求 —— 無論 ...
2025-09-18MySQL 大表拆分與關(guān)聯(lián)查詢效率:打破 “拆分必慢” 的認(rèn)知誤區(qū) 在 MySQL 數(shù)據(jù)庫管理中,“大表” 始終是性能優(yōu)化繞不開的話題。 ...
2025-09-18CDA 數(shù)據(jù)分析師:表結(jié)構(gòu)數(shù)據(jù) “獲取 - 加工 - 使用” 全流程的賦能者 表結(jié)構(gòu)數(shù)據(jù)(如數(shù)據(jù)庫表、Excel 表、CSV 文件)是企業(yè)數(shù)字 ...
2025-09-18DSGE 模型中的 Et:理性預(yù)期算子的內(nèi)涵、作用與應(yīng)用解析 動(dòng)態(tài)隨機(jī)一般均衡(Dynamic Stochastic General Equilibrium, DSGE)模 ...
2025-09-17Python 提取 TIF 中地名的完整指南 一、先明確:TIF 中的地名有哪兩種存在形式? 在開始提取前,需先判斷 TIF 文件的類型 —— ...
2025-09-17CDA 數(shù)據(jù)分析師:解鎖表結(jié)構(gòu)數(shù)據(jù)特征價(jià)值的專業(yè)核心 表結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 規(guī)范存儲(chǔ)的結(jié)構(gòu)化數(shù)據(jù),如數(shù)據(jù)庫表、Excel 表、 ...
2025-09-17Excel 導(dǎo)入數(shù)據(jù)含缺失值?詳解 dropna 函數(shù)的功能與實(shí)戰(zhàn)應(yīng)用 在用 Python(如 pandas 庫)處理 Excel 數(shù)據(jù)時(shí),“缺失值” 是高頻 ...
2025-09-16深入解析卡方檢驗(yàn)與 t 檢驗(yàn):差異、適用場(chǎng)景與實(shí)踐應(yīng)用 在數(shù)據(jù)分析與統(tǒng)計(jì)學(xué)領(lǐng)域,假設(shè)檢驗(yàn)是驗(yàn)證研究假設(shè)、判斷數(shù)據(jù)差異是否 “ ...
2025-09-16CDA 數(shù)據(jù)分析師:掌控表格結(jié)構(gòu)數(shù)據(jù)全功能周期的專業(yè)操盤手 表格結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 存儲(chǔ)的結(jié)構(gòu)化數(shù)據(jù),如 Excel 表、數(shù)據(jù) ...
2025-09-16MySQL 執(zhí)行計(jì)劃中 rows 數(shù)量的準(zhǔn)確性解析:原理、影響因素與優(yōu)化 在 MySQL SQL 調(diào)優(yōu)中,EXPLAIN執(zhí)行計(jì)劃是核心工具,而其中的row ...
2025-09-15解析 Python 中 Response 對(duì)象的 text 與 content:區(qū)別、場(chǎng)景與實(shí)踐指南 在 Python 進(jìn)行 HTTP 網(wǎng)絡(luò)請(qǐng)求開發(fā)時(shí)(如使用requests ...
2025-09-15CDA 數(shù)據(jù)分析師:激活表格結(jié)構(gòu)數(shù)據(jù)價(jià)值的核心操盤手 表格結(jié)構(gòu)數(shù)據(jù)(如 Excel 表格、數(shù)據(jù)庫表)是企業(yè)最基礎(chǔ)、最核心的數(shù)據(jù)形態(tài) ...
2025-09-15Python HTTP 請(qǐng)求工具對(duì)比:urllib.request 與 requests 的核心差異與選擇指南 在 Python 處理 HTTP 請(qǐng)求(如接口調(diào)用、數(shù)據(jù)爬取 ...
2025-09-12解決 pd.read_csv 讀取長(zhǎng)浮點(diǎn)數(shù)據(jù)的科學(xué)計(jì)數(shù)法問題 為幫助 Python 數(shù)據(jù)從業(yè)者解決pd.read_csv讀取長(zhǎng)浮點(diǎn)數(shù)據(jù)時(shí)的科學(xué)計(jì)數(shù)法問題 ...
2025-09-12CDA 數(shù)據(jù)分析師:業(yè)務(wù)數(shù)據(jù)分析步驟的落地者與價(jià)值優(yōu)化者 業(yè)務(wù)數(shù)據(jù)分析是企業(yè)解決日常運(yùn)營(yíng)問題、提升執(zhí)行效率的核心手段,其價(jià)值 ...
2025-09-12用 SQL 驗(yàn)證業(yè)務(wù)邏輯:從規(guī)則拆解到數(shù)據(jù)把關(guān)的實(shí)戰(zhàn)指南 在業(yè)務(wù)系統(tǒng)落地過程中,“業(yè)務(wù)邏輯” 是連接 “需求設(shè)計(jì)” 與 “用戶體驗(yàn) ...
2025-09-11塔吉特百貨孕婦營(yíng)銷案例:數(shù)據(jù)驅(qū)動(dòng)下的精準(zhǔn)零售革命與啟示 在零售行業(yè) “流量紅利見頂” 的當(dāng)下,精準(zhǔn)營(yíng)銷成為企業(yè)突圍的核心方 ...
2025-09-11CDA 數(shù)據(jù)分析師與戰(zhàn)略 / 業(yè)務(wù)數(shù)據(jù)分析:概念辨析與協(xié)同價(jià)值 在數(shù)據(jù)驅(qū)動(dòng)決策的體系中,“戰(zhàn)略數(shù)據(jù)分析”“業(yè)務(wù)數(shù)據(jù)分析” 是企業(yè) ...
2025-09-11Excel 數(shù)據(jù)聚類分析:從操作實(shí)踐到業(yè)務(wù)價(jià)值挖掘 在數(shù)據(jù)分析場(chǎng)景中,聚類分析作為 “無監(jiān)督分組” 的核心工具,能從雜亂數(shù)據(jù)中挖 ...
2025-09-10統(tǒng)計(jì)模型的核心目的:從數(shù)據(jù)解讀到?jīng)Q策支撐的價(jià)值導(dǎo)向 統(tǒng)計(jì)模型作為數(shù)據(jù)分析的核心工具,并非簡(jiǎn)單的 “公式堆砌”,而是圍繞特定 ...
2025-09-10