【图像处理】——特征匹配(SIFT特征检测器+FLANN特征匹配方法+KNN近邻最优匹配筛选)——cv.xfeatures2d.SIFT_create()sift.detectAndCompute
轉載請注明地址
目錄
1、特征檢測和特征匹配方法
(1)特征檢測算法
(2)特征匹配算法
(3)各種特征檢測算法的比較
2、特征匹配的基本步驟(附帶主要的函數)
(1)圖像預處理——灰度化(模板——查詢集queryImg,待匹配圖像——訓練集trainingImg)
(2)創建特征檢測器——用于檢測模板和圖像上的特征點
(3)利用上述特征檢測器獲得特征點和特征描述符
特征點的解釋:
特征描述符的解釋:
(4)創建特征匹配器——用于對模板和待匹配圖像間進行特征點之間的匹配
匹配的一般思路:
(5)選取優良的匹配點——Lowe's算法+KNN近鄰算法
(6)繪制匹配對間的匹配線
3、Python代碼實現和主要函數分析
(1)導入包
(2)kps,des = sift.detectAndCompute(IMg, None)函數
參數
返回值
(3)cv.FlannBasedMatcher(indexParams,searchParams)
(4)matches=flann.knnMatch(queryDescriptors, trainDescriptors, k, mask=None, compactResult=None)
參數:
返回值:
matches含有三個屬性:queryIdx,trainIdx,distance(特征點描述符的歐式距離)
(5)drawMatchesKnn(img1, keypoints1, img2, keypoints2,**drawparams)
參數:
問題1:怎么提取出kps中關鍵點的坐標?
問題2:matches的shape是怎樣的?
問題3:opencv中match與KnnMatch的區別
4、其他函數
(1)cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
(2)dst = cv2.perspectiveTransform(pts, M)
5、OpenCV仿射變換、投影變換的重要函數
Q&A:
6、實際代碼
(1)KNNMATCH+DRAWKNNMATCH+LOWE'S TEST
(2)match+drawmatch
?
????? 看了很多文章,比較雜亂,打算自己整理一下1特征匹配這方面的知識,主要思路是先拋出一個例子和代碼,然后對代碼逐步解析,和對關鍵代碼的參數以及返回值作解析
1、特征檢測和特征匹配方法
參考:
https://blog.csdn.net/m0_37598482/article/details/78846215
https://www.jianshu.com/p/14b92d3fd6f8
一幅圖像中總存在著其獨特的像素點,這些點我們可以認為就是這幅圖像的特征,成為特征點。計算機視覺領域中的很重要的圖像特征匹配就是一特征點為基礎而進行的,所以,如何定義和找出一幅圖像中的特征點就非常重要。這篇文章我總結了視覺領域最常用的幾種特征點以及特征匹配的方法。
在計算機視覺領域,興趣點(也稱關鍵點或特征點)的概念已經得到了廣泛的應用, 包括目標識別、 圖像配準、 視覺跟蹤、 三維重建等。 這個概念的原理是, 從圖像中選取某些特征點并對圖像進行局部分析,而非觀察整幅圖像。 只要圖像中有足夠多可檢測的興趣點,并且這些興趣點各不相同且特征穩定, 能被精確地定位,上述方法就十分有效。
(1)特征檢測算法
- Harris:該算法用于檢測角點;
- SIFT:該算法用于檢測斑點;https://blog.csdn.net/qq_40369926/article/details/88597406
- SURF:該算法用于檢測角點;
- FAST:該算法用于檢測角點;
- BRIEF:該算法用于檢測斑點;
- ORB:該算法代表帶方向的FAST算法與具有旋轉不變性的BRIEF算法;
(2)特征匹配算法
- 暴力(Brute-Force)匹配法;
- 基于FLANN匹配法;
- 可以采用單應性進行空間驗證。
(3)各種特征檢測算法的比較
參考:https://www.cnblogs.com/jsxyhelu/p/7834416.html
- 計算速度: ORB>>SURF>>SIFT(各差一個量級)
- 旋轉魯棒性: SURF>ORB~SIFT(表示差不多)
- 模糊魯棒性: SURF>ORB~SIFT
- 尺度變換魯棒性: SURF>SIFT>ORB(ORB并不具備尺度變換性)
2、特征匹配的基本步驟(附帶主要的函數)
https://blog.csdn.net/qq_41007606/article/details/81875193
(1)圖像預處理——灰度化(模板——查詢集queryImg,待匹配圖像——訓練集trainingImg)
#1、#讀取要匹配的灰度照片 queryImage=cv.imread("b2_ROI_Template3.jpg",0) trainingImage=cv.imread("b2_target.jpg",0)(2)創建特征檢測器——用于檢測模板和圖像上的特征點
sift=cv.xfeatures2d.SIFT_create()(3)利用上述特征檢測器獲得特征點和特征描述符
kp1, des1 = sift.detectAndCompute(queryImage,None) kp2, des2 = sift.detectAndCompute(trainingImage,None)特征點的解釋:
??????? 具體的概念根據不同的特征檢測算法而異,如果要看確切的關鍵點的概念,可以深入到每一種特征檢測算法當中去
??????? 如FAST特征檢測算法的定義是:
??????? 跟Harris檢測器的情況一樣, FAST算法源于對構成角點的定義。FAST對角點的定義基于候選特征點周圍的圖像強度值。 以某個點為中心作一個圓, 根據圓上的像素值判斷該點是否為關鍵點。 如果存在這樣一段圓弧, 它的連續長度超過周長的3/4, 并且它上面所有像素的強度值都與圓心的強度值明顯不同(全部更黑或更亮) , 那么就認定這是一個關鍵點。
??????? 關鍵點我們可以理解為可以用來代表一個物體的特征的點,如一個長方體,特征點就是8個點,就能夠說明他的大小和形狀了
特征描述符的解釋:
https://blog.csdn.net/shiyongraow/article/details/78347234
???????? 是一種算法和方法,輸入1個圖像,返回多個特征向量(主要用來處理圖像的局部,往往會把多個特征向量組成一個一維的向量)。主要用于圖像匹配(視覺檢測),匹配圖像中的物品。
???????? 通俗一點就是通過一種算法對該特征點一定區域的灰度值或者說特征進行了計算,將計算得到的多個結果組成一個一維數組,而這個數組就可以稱之為該點的特征描述符
??????? 還是以長方體為例:【面積、周長、最小外接球心、最大內接球心...】這一組數據就是該長方體的特征描述符,不同的是,圖像的特征描述符一般具有尺度不變性等,如一個圖片的HU不變矩組合成了一個7元素的一維矩陣,也可以作為描述符
Hu不變矩
(4)創建特征匹配器——用于對模板和待匹配圖像間進行特征點之間的匹配
flann=cv.FlannBasedMatcher(indexParams,searchParams)匹配的一般思路:
從模板中取一個特征點,然后在從待匹配圖像中根據匹配算法(匹配度)尋找最優匹配點,記錄該匹配點對。
但是在lowes大神提出了另一種方法來優化它,即通過優化來選取比較優良的匹配點
(5)選取優良的匹配點——Lowe's算法+KNN近鄰算法
matches=flann.knnMatch(des1,des2,k=2) for i, (m,n) in enumerate(matches):#返回索引號和兩個匹配的信息if m.distance< 0.5*n.distance: #m表示大圖像上最匹配點的距離,n表示次匹配點的距離,若比值小于0.5則舍棄matchesMask[i]=[1,0]?????? 我們需要進一步篩選匹配點,來獲取優秀的匹配點,這就是所謂的“去粗取精”。這里我們采用了Lowe’s算法來進一步獲取優秀匹配點。
??????? 為了排除因為圖像遮擋和背景混亂而產生的無匹配關系的關鍵點,SIFT的作者Lowe提出了比較最近鄰距離與次近鄰距離的SIFT匹配方式:取一幅圖像中的一個SIFT關鍵點,并找出其與另一幅圖像中歐式距離最近的前兩個關鍵點,在這兩個關鍵點中,如果最近的距離除以次近的距離得到的比率ratio少于某個閾值T,則接受這一對匹配點。
??????? 因為對于錯誤匹配,由于特征空間的高維性,相似的距離可能有大量其他的錯誤匹配,從而它的ratio值比較高。顯然降低這個比例閾值T,SIFT匹配點數目會減少,但更加穩定,反之亦然。Lowe推薦ratio的閾值為0.8,但作者對大量任意存在尺度、旋轉和亮度變化的兩幅圖片進行匹配,結果表明ratio取值在0. 4~0. 6 之間最佳,小于0. 4的很少有匹配點,大于0. 6的則存在大量錯誤匹配點,所以建議ratio的取值原則如下:
ratio=0. 4:對于準確度要求高的匹配;
ratio=0. 6:對于匹配點數目要求比較多的匹配;
ratio=0. 5:一般情況下。
(6)繪制匹配對間的匹配線
resultimage=cv.drawMatchesKnn(queryImage,kp1,trainingImage,kp2,matches,None,**drawParams)3、Python代碼實現和主要函數分析
(1)導入包
import cv2 as cv from matplotlib import pyplot as plt還需要安裝這個包:pip install opencv-contrib-python==3.4.2.16
(2)kps,des = sift.detectAndCompute(IMg, None)函數
參數
img:需要提取特征點的灰度圖
None:照寫即可
返回值
kps:返回的是特征點所包含的信息,是一個Dmatch數據類型
這里的kps就是關鍵點。它所包含的信息有:
angle:角度,表示關鍵點的方向,通過Lowe大神的論文可以知道,為了保證方向不變形,SIFT算法通過對關鍵點周圍鄰域進行梯度運算,求得該點方向。-1為初值。
class_id——當要對圖片進行分類時,我們可以用class_id對每個特征點進行區分,未設定時為-1,需要靠自己設定
octave——代表是從金字塔哪一層提取的得到的數據。
pt——關鍵點點的坐標
response——響應程度,代表該點強壯大小,更確切的說,是該點角點的程度。
size——該點直徑的大小
des:返回特征點的特征描述符,是一個一維列表,列表元素為Dmatch類型
(3)cv.FlannBasedMatcher(indexParams,searchParams)
根據設置好的參數返回一個特征匹配器,參數是通過字典的方式傳送給函數的
FLANN_INDEX_KDTREE=0 indexParams=dict(algorithm=FLANN_INDEX_KDTREE,trees=5)#指定匹配的算法和kd樹的層數 searchParams= dict(checks=50)#指定返回的個數#4、根據設置的參數創建特征匹配器 flann=cv.FlannBasedMatcher(indexParams,searchParams)(4)matches=flann.knnMatch(queryDescriptors, trainDescriptors, k, mask=None, compactResult=None)
參數:
opencv中match與KnnMatch的區別(看問題3)
queryDescriptors:查詢集的特征描述符,即模板
trainDescriptors:訓練集的特征描述符,即待匹配圖像
k:根據KNN近鄰算法來返回最匹配的前K個匹配點,默認為1
返回值:
matches:返回的是最匹配的兩個特征點的信息,返回的類型是一個列表,列表元素的類型是Dmatch數據類型,每一個列表元素又是一個列表,這個列表元素的個數和k一樣,因為封裝的就是匹配前k個點的信息matches含有三個屬性:queryIdx,trainIdx,distance(特征點描述符的歐式距離)
參考:https://blog.csdn.net/weixin_44072651/article/details/89262277
queryIdx:測試圖像的特征點描述符的下標(第幾個特征點描述符),同時也是描述符對應特征點的下標。
trainIdx:樣本圖像的特征點描述符下標,同時也是描述符對應特征點的下標。
distance:代表這怡翠匹配的特征點描述符的歐式距離,數值越小也就說明倆個特征點越相近。
注意:這里的遍歷matches時,需要理解matches一次返回的是幾個點,即k=幾得清楚
bf = cv.BFMatcher_create() matches = bf.match(des1, des2) for matche in matches:print(matche)print(matche.queryIdx)print(matche.trainIdx)print(matche.distance)(5)drawMatchesKnn(img1, keypoints1, img2, keypoints2,**drawparams)
#繪制的參數,匹配連線的顏色,特征點的顏色,需要畫哪些匹配,flags=0繪制點和線,=2不畫特征點 drawParams=dict(matchColor=(0,0,255),singlePointColor=(255,0,0),matchesMask=matchesMask,flags=0) #給特征點和匹配的線定義顏色 resultimage=cv.drawMatchesKnn(queryImage,kp1,trainingImage,kp2,matches,None,**drawParams) #畫出匹配的結果參數:
img1:模板灰度圖像
kp1:模板特征點信息
img2:待匹配灰度圖像
kp2:待匹配圖像特征點信息
**drawparam:繪制特征點匹配時的參數,用字典的形式傳入,含有matchColor、singlePointColor、matchesMask、flags=0等參數,其中matchColor表示匹配連線顏色,singlePointColor表示特征點顏色,matchesMask表示畫哪些匹配,flags=0表示畫特征點和連線,flags=2表示不畫特征點
問題1:怎么提取出kps中關鍵點的坐標?
答:通過特征描述符的索引下表和特征點的.pt屬性獲取
# 獲取關鍵點的坐標src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)print(src_pts)dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)問題2:matches的shape是怎樣的?
答:假設取前k個匹配點,查詢機即模板含有m個關鍵點,則matches.shape = (1,m,k)
問題3:opencv中match與KnnMatch的區別
答:區別主要在于前面一個返回的是一個特征點的信息,后面返回的是多個特征點的信息,即前面的是返回最優匹配的點,而后面這個則是返回最優和次優匹配兩個點
參考:https://blog.csdn.net/weixin_44072651/article/details/89262277
4、其他函數
(1)cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
參考:https://blog.csdn.net/martinkeith/article/details/104995093
https://blog.csdn.net/ei1990/article/details/78338928
計算多個二維點對之間的最優單映射變換矩陣 H(3行x3列) ,使用最小均方誤差或者RANSAC方法
(2)dst = cv2.perspectiveTransform(pts, M)
理論參考:https://blog.csdn.net/oppo62258801/article/details/78642218
對二維或者三維矢量進行透射變換,也就是對輸入二維坐標點或者三維坐標點進行投射變換
5、OpenCV仿射變換、投影變換的重要函數
estimateRigidTransform():計算多個二維點對或者圖像之間的最優仿射變換矩陣 (2行x3列),H可以是部分自由度,比如各向一致的切變。
getAffineTransform():計算3個二維點對之間的仿射變換矩陣H(2行x3列),自由度為6.
warpAffine():對輸入圖像進行仿射變換
findHomography: 計算多個二維點對之間的最優單映射變換矩陣 H(3行x3列) ,使用最小均方誤差或者RANSAC方法 。
getPerspectiveTransform():計算4個二維點對之間的透射變換矩陣 H(3行x3列)
warpPerspective(): 對輸入圖像進行透射變換
perspectiveTransform():對二維或者三維矢量進行透射變換,也就是對輸入二維坐標點或者三維坐標點進行投射變換。
estimateAffine3D:計算多個三維點對之間的最優三維仿射變換矩陣H (3行x4列)
transform():對輸入的N維矢量進行變換,可用于進行仿射變換、圖像色彩變換.
findFundamentalMat:計算多個點對之間的基矩陣H。
?
cvStereoCalibrate():中T類型要求了3*1,對與其他形參float和double都支持
cvStereoRectigy():只支持double類型
cvStereoRectifyUncalibrated():立體校正算法Hartley算法效果和F矩陣及圖像數量有關,
ps:
【如果用cvStereoCalibrate()函數計算處理的F矩陣效果和Bouguet算法(cvStereoRectigy())效果一樣】
【如果用cvFindFundamentalMat()函數計算F矩陣,沒有Bougut算法好】
【用Hartley算法(cvStereoRectifyUncalibrated())校正時,別忘了實現要用cvUndistortPoints()去除相機畸變,Bouguet算法(cvStereoRectigy())沒有這個要求,實際上它在函數內部校正了相機的畸變?!?/p>
?
Q&A:
問題1:如何計算3個二維點對之間的仿射變換矩陣?
答:使用getAffineTransform()。
問題2:如何計算多個二維點對之間的仿射變換矩陣(使用誤差最小準則 )?
答:使用estimateRigidTransform()。
問題3:如何計算多個二維點對之間的投影變換矩陣(使用誤差最小準則 )
答:findHomography()。
問題4:如何計算4個二維點對之間的透射變換?
答:使用getPerspectiveTransform()。
問題5:如何計算多個三維點對之間的仿射變換?
答:使用estimateAffine3D。
問題6:如何對輸入圖像進行仿射變換?
答:使用warpAffine()。
問題7:如何對輸入圖像進行透射變換?
答:使用perspectiveTransform()。
問題8:如何對輸入的二維點對進行仿射變換?
答:使用transform()。
問題9:如何對輸入的三維點對進行投影變換?
答:使用perspectiveTransform()。
6、實際代碼
代碼參考:https://blog.csdn.net/zhuisui_woxin/article/details/84400439
(1)KNNMATCH+DRAWKNNMATCH+LOWE'S TEST
# ''' 基于FLANN的匹配器(FLANN based Matcher) 1.FLANN代表近似最近鄰居的快速庫。它代表一組經過優化的算法,用于大數據集中的快速最近鄰搜索以及高維特征。 2.對于大型數據集,它的工作速度比BFMatcher快。 3.需要傳遞兩個字典來指定要使用的算法及其相關參數等 對于SIFT或SURF等算法,可以用以下方法: index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5) 對于ORB,可以使用以下參數: index_params= dict(algorithm = FLANN_INDEX_LSH,table_number = 6, # 12 這個參數是searchParam,指定了索引中的樹應該遞歸遍歷的次數。值越高精度越高key_size = 12, # 20multi_probe_level = 1) #2 ''' import cv2 as cv from matplotlib import pyplot as plt#1、#讀取要匹配的灰度照片 queryImage=cv.imread("b2_ROI_Template3.jpg",0) trainingImage=cv.imread("b2_target.jpg",0)#2、#創建sift檢測器,這個sift檢測器主要是用于檢測模板和待匹配圖像的特征關鍵點點, sift=cv.xfeatures2d.SIFT_create() #利用創建好的特征點檢測器去檢測兩幅圖像的特征關鍵點, # 其中kp含有角度、關鍵點坐標等多個信息,具體怎么提取出坐標點的坐標不清楚, # des是特征描述符,每一個特征點對應了一個特征描述符,由一維特征向量構成 kp1, des1 = sift.detectAndCompute(queryImage,None) kp2, des2 = sift.detectAndCompute(trainingImage,None)#3、設置Flannde參數,這里是為了下一步匹配做準備 FLANN_INDEX_KDTREE=0 indexParams=dict(algorithm=FLANN_INDEX_KDTREE,trees=5)#指定匹配的算法和kd樹的層數 searchParams= dict(checks=50)#指定返回的個數#4、根據設置的參數創建特征匹配器 flann=cv.FlannBasedMatcher(indexParams,searchParams) #利用創建好的特征匹配器利用k近鄰算法來用模板的特征描述符去匹配圖像的特征描述符,k指的是返回前k個最匹配的特征區域 matches=flann.knnMatch(des1,des2,k=2)#返回的是最匹配的兩個特征點的信息,返回的類型是一個列表,列表元素的類型是Dmatch數據類型,具體是什么我也不知道 #設置好初始匹配值,用來存放特征點 matchesMask=[[0,0] for i in range (len(matches))]#[[0, 0], [0, 0]... [0, 0]]個數為len(matches) for i, (m,n) in enumerate(matches):#返回索引號和兩個匹配的信息'''比較最近鄰距離與次近鄰距離的SIFT匹配方式:取一幅圖像中的一個SIFT關鍵點,并找出其與另一幅圖像中歐式距離最近的前兩個關鍵點,在這兩個關鍵點中,如果最近的距離除以次近的距離得到的比率ratio少于某個閾值T,則接受這一對匹配點。因為對于錯誤匹配,由于特征空間的高維性,相似的距離可能有大量其他的錯誤匹配,從而它的ratio值比較高。顯然降低這個比例閾值T,SIFT匹配點數目會減少,但更加穩定,反之亦然。Lowe推薦ratio的閾值為0.8,但作者對大量任意存在尺度、旋轉和亮度變化的兩幅圖片進行匹配,結果表明ratio取值在0. 4~0. 6 之間最佳,小于0. 4的很少有匹配點,大于0. 6的則存在大量錯誤匹配點,所以建議ratio的取值原則如下:ratio=0. 4:對于準確度要求高的匹配;ratio=0. 6:對于匹配點數目要求比較多的匹配;ratio=0. 5:一般情況下。'''if m.distance< 0.5*n.distance: #m表示大圖像上最匹配點的距離,n表示次匹配點的距離,若比值小于0.5則舍棄matchesMask[i]=[1,0] #繪制的參數,匹配連線的顏色,特征點的顏色,需要畫哪些匹配,flags=0繪制點和線,=2不畫特征點 drawParams=dict(matchColor=(0,0,255),singlePointColor=(255,0,0),matchesMask=matchesMask,flags=0) #給特征點和匹配的線定義顏色 resultimage=cv.drawMatchesKnn(queryImage,kp1,trainingImage,kp2,matches,None,**drawParams) #畫出匹配的結果 plt.imshow(resultimage,),plt.show()繪制出了所有的特征點,以及相互匹配的特征點的匹配線
?
(2)match+drawmatch
# 基于FLANN的匹配器(FLANN based Matcher)定位圖片 import numpy as np import cv2 from matplotlib import pyplot as pltMIN_MATCH_COUNT = 5 # 設置最低特征點匹配數量為10 template = cv2.imread('h3_target_Template.jpg', 0) # queryImage target = cv2.imread('h3_target.jpg', 0) # trainImage # Initiate SIFT detector創建sift檢測器 sift = cv2.xfeatures2d.SIFT_create() # find the keypoints and descriptors with SIFT kp1, des1 = sift.detectAndCompute(template, None) kp2, des2 = sift.detectAndCompute(target, None) # 創建設置FLANN匹配 FLANN_INDEX_KDTREE = 0 index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5) search_params = dict(checks=50) flann = cv2.FlannBasedMatcher(index_params, search_params) matches = flann.knnMatch(des1, des2, k=2) # store all the good matches as per Lowe's ratio test. good = [] # 舍棄大于0.7的匹配 for m, n in matches:if m.distance < 0.7 * n.distance:good.append(m) print(good) if len(good) >= MIN_MATCH_COUNT:# 獲取關鍵點的坐標src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)print(src_pts)dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)# 計算變換矩陣和MASK# 計算多個二維點對之間的最優單映射變換矩陣 H(3行x3列) ,使用最小均方誤差或者RANSAC方法M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)matchesMask = mask.ravel().tolist()#先將mask變成一維,再將矩陣轉化為列表h, w = template.shape# 使用得到的變換矩陣對原圖像的四個角進行變換,獲得在目標圖像上對應的坐標pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)dst = cv2.perspectiveTransform(pts, M)cv2.polylines(target, [np.int32(dst)], True, 0, 2, cv2.LINE_AA) else:print("Not enough matches are found - %d/%d" % (len(good), MIN_MATCH_COUNT))matchesMask = None draw_params = dict(matchColor=(0, 255, 0),singlePointColor=None,matchesMask=matchesMask,flags=2) result = cv2.drawMatches(template, kp1, target, kp2, good, None, **draw_params) plt.imshow(result, 'gray') plt.show()總結
以上是生活随笔為你收集整理的【图像处理】——特征匹配(SIFT特征检测器+FLANN特征匹配方法+KNN近邻最优匹配筛选)——cv.xfeatures2d.SIFT_create()sift.detectAndCompute的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【图像处理】——改变图像的大小(降采样重
- 下一篇: idea ssm项目移包报错问题