圖像相似度算法 (電子圍籬、移動偵測、入侵偵測) 1
■ 簡述 (Introduction)
隨者攝影機、照相機的越來越普及,圖片的應用領域也隨之提升。我們可以透過攝影機來辨識是否有任何物體從攝影機前經過,如果有則立即發送警報。這類的圖像相似度匹配技術可以應用於:入侵偵測(管制區、特定區域)、移動偵測、網路圖片侵權檢測 等。以下將介紹SIFT圖像相似度算法與實作。
■ 關鍵點匹配簡述 (Keypoint Matching)
如果我們想要比對影像之間的差異,首先需要照到影像內的特徵點,一張圖像內的某些部分可能包含比其他部位更多的特徵點,如邊緣、角點等。透過這些提出取出來的特徵點,進而與其他圖像內的特徵點進行匹配,即可得出兩張圖像之間的差異性。常見的提取特徵點的算法包含:SIFT,ORB,SURF,GIST,此類算法的準確度較高,但由於計算複雜度較高,因此計算速度就相對較低。
● SIFT 簡介 (Scale-invariant feature transform)
由於提取特徵點算法不少,我在這邊僅介紹較為的算法SIFT。SIFT 全名為Scale-invariant feature transform (尺度不變特徵轉換)。SIFT由1999年David Lowe提出後,就被廣泛應用於圖像處理相關領域,包含:圖像識別、圖像檢測、圖像檢索、3D重建等。SIFT是一種機器視覺的演算法用來偵測與描述影像中的局部性特徵,它在空間尺度中尋找極值點,並提取出其位置、尺度、旋轉不變數。
局部性特徵的描述與偵測可以幫助辨識物體,SIFT 特徵是基於物體上的一些局部外觀的興趣點而與影像的大小和旋轉無關。 對於光線、雜訊、些微視角改變的容忍度也相當高。基於這些特性,它們是高度顯著而且相對容易擷取,在母數龐大的特徵資料庫中,很容易辨識物體而且鮮有誤認。
使用 SIFT 特徵描述對於部分物體遮蔽的偵測率也相當高,甚至只需要3個以上的 SIFT 物體特徵就足以計算出位置與方位。在現今的電腦硬體速度下和小型的特徵資料庫條件下,辨識速度可接近即時運算。SIFT 特徵的信息量大,適合在大量資料庫中快速準確匹配。
● SIFT 簡單實作
我們今天假設在家中裝了一台網路攝影機,當外出時可幫我們全天候的監控家中狀況。有人突然出現於畫面中時,我們可以快速得知家中被人入侵了,進而做出適當的處置。
以下我們需要先準備兩張照片,一張為家中客廳的照片,另一張則為家中被人闖入時的照片。
我們須先需要先安裝下列套件 與 進行import。建議可以透過Anaconda建立一個新環境後,再安裝套件,比較不會跟現有環境互相衝突,Python版本則建議為3.6.8。當初安裝CV2套件時,僅安裝opencv-python,但執行時會Error,因此需額外再安裝opencv-contrib-python即可。
import cv2
from matplotlib import pyplot as plt
import numpy as np
import os
import math
環境方面都準備就緒後,即可進行實作,首先我們會先建立一個SIFT特徵提取器 與 建立FLANN匹配對象。
#建立SIFT特徵提取器
sift = cv2.xfeatures2d.SIFT_create()
#建立FLANN匹配對象
FLANN_INDEX_KDTREE=0
indexParams=dict(algorithm=FLANN_INDEX_KDTREE,trees=5)
searchParams=dict(checks=50)
flann=cv2.FlannBasedMatcher(indexParams,searchParams)
透過cv2讀取我們的樣本圖片(原始客廳照片),並透過日前建立的SIFT特徵提取器去提取圖片的特徵值 sift.detectAndCompute()。原始圖片的特徵提取完畢後,接者讀取欲比對的圖片特徵(小偷入侵的照片) sift.detectAndCompute(),
sampleImage=cv2.imread(samplePath,0)
kp1, des1 = sift.detectAndCompute(sampleImage, None) #提取圖片特徵
for parent,dirnames,filenames in os.walk(queryPath):
for p in filenames:
p=queryPath+p
queryImage=cv2.imread(p,0)
kp2, des2 = sift.detectAndCompute(queryImage, None) #提取比對的圖片特徵
matches=flann.knnMatch(des1,des2,k=2)
(matchNum,matchesMask)=getMatchNum(matches,0.9) #通過比率條件,計算出匹配程度
matchRatio=matchNum*100/len(matches)
drawParams=dict(matchColor=(0,255,0),
singlePointColor=(255,0,0),
matchesMask=matchesMask,
flags=0)
comparisonImage=cv2.drawMatchesKnn(sampleImage,kp1,queryImage,kp2,matches,None,**drawParams)
comparisonImageList.append((comparisonImage,matchRatio)) #紀錄結果
getMatchNum function,主要做返回特徵點匹配數量和matchesMask
def getMatchNum(matches,ratio):
'''返回特徵點匹配數量和matchesMask'''
matchesMask=[[0,0] for i in range(len(matches))]
matchNum=0
for i,(m,n) in enumerate(matches):
if m.distance<ratio*n.distance: #將距離比率小於ratio的匹配點篩選出來
matchesMask[i]=[1,0]
matchNum+=1
return (matchNum,matchesMask)
當兩張圖象比對完畢後,將兩張圖片匹配點進行連線,可以更清楚看出兩張圖的相似程度。
comparisonImageList.sort(key=lambda x:x[1],reverse=True) #按照匹配度排序
count=len(comparisonImageList)
column=3
row=math.ceil(count/column)
#圖片顯示
figure,ax=plt.subplots(row,column)
for index,(image,ratio) in enumerate(comparisonImageList):
ax[int(index/column)][index%column].set_title('Similiarity %.2f%%' % ratio)
ax[int(index/column)][index%column].imshow(image)
plt.show()
完整程式碼可以參考:一个用SIFT特征比较图像相似度的python小程序
比對結果如下,當沒有任何進入到客廳時,與出門前的照片進行匹配是100%相似(上圖)。但當有人入侵時,匹配程度則下降到了66.95%(下圖)。透過簡單的方法,即可快速比對兩張圖片的差異,並可延伸出更多的應用場景。
但由於SIFT的計算複雜度較高,因此計算速度就相對較慢,以下整理出其他常見的圖像相似度算法,並針對其各項特點進行比較。
透過上述比較後,可以發現如果當應用場景不需要非常精準的匹配結果時,可以採用更快速演算法即可達到相同應用效果。因此下一篇會再為各位介紹另一個圖像相似度算法,雜湊演算法 /哈希演算法 (Hash Algorithm)。
網址連結:圖像相似度算法 (Google以圖搜圖)
■參考文獻 & 銘謝:
- https://zhuanlan.zhihu.com/p/22476595
- https://www.twblogs.net/a/5bca9d5a2b7177735197c936
- https://blog.csdn.net/wangzhenyang2/article/details/85106267
- https://colobu.com/2018/09/07/image-similarity-algorithms-in-go/
- https://kknews.cc/tech/kvgxqbp.html
- https://www.twblogs.net/a/5c0ab562bd9eee6fb37be7d9
- https://en.wikipedia.org/wiki/Scale-invariant_feature_transform
- 圖像相似度算法差異比較來源:網址