在前面的記錄中對使用OpenCV2進行特征匹配的步驟做了一個簡單的介紹,詳見:http://www.reibang.com/p/5083f8d75439斗搞,使用了ORB提取特征點,但是匹配出的結(jié)果是非常粗糙的,結(jié)果顯然包含了很多錯誤的匹配贞谓,這就需要使用一些方法來過濾掉這些錯誤的匹配。因此繼續(xù)在這里記錄對特征點進行進一步篩選的過程熊尉,同時,也對幾個關(guān)鍵的計算流程做個更詳細的說明掌腰,方便將來參考狰住。
特征點匹配
OpenCV里面的二維特征匹配,有兩種常用的方法齿梁,
Brute Force Matcher
Flann based Matcher
繼承于DescriptorMatcher類催植,分別對應(yīng)BFMatcher和FlannBasedMatcher。
二者的區(qū)別: BFMatcher總是嘗試所有可能的匹配勺择,從而使得它總能夠找到最佳匹配创南。這也是Brute Force(暴力法)的原始含義。方法是計算某一個特征點描述子與其他所有特征點描述子之間的距離酵幕,然后將得到的距離進行排序扰藕,取距離最近的一個作為匹配點缓苛。 FlannBasedMatcher中FLANN的含義是Fast Library forApproximate Nearest Neighbors芳撒,它是一種近似法邓深,算法更快但是找到的是最近鄰近似匹配,當我們需要找到一個相對好的匹配但是不需要最佳匹配的時候可以用FlannBasedMatcher笔刹。當然也可以通過調(diào)整FlannBasedMatcher的參數(shù)來提高匹配的精度或者提高算法速度芥备,但是相應(yīng)地算法速度或者算法精度會受到影響。
此外舌菜,使用特征提取過程得到的特征描述符(descriptor)數(shù)據(jù)類型有的是float類型的萌壳,ORB和BRIEF特征描述子只能使用BruteForce匹配法。
具體matching的方法
match: 給定查詢集合中的每個特征描述子日月,尋找最佳匹配袱瓮,返回值按距離升序排列。
knnMatch:給定查詢集合中的每個特征描述子爱咬,尋找k個最佳匹配
radiusMatch:在特定范圍內(nèi)尋找特征描述子尺借,返回特定距離內(nèi)的匹配
特征點篩選
通過上述過程得到了raw_matches之后,接下來對其進行篩選精拟。
- 交叉驗證(Cross-match filter)
這種方法只針對BFMatcher, 就是將BFMatcher的最后一個參數(shù)燎斩,交叉驗證聲明為true,即matcher = cv2.BFMatcher(cv2.NORM_HAMMING,crossCheck=True)蜂绎,如果圖1的一個特征點和圖2的一個特征點相匹配栅表,則進行一個相反的檢查,即從圖2上的特征點進行匹配圖1的特征點师枣,如果相互之間都匹配成功怪瓶,才認為是一個好的匹配。
cross-match filter 結(jié)果如圖(似乎還不夠好):
- knnMatch
knnMatch返回K個好的匹配践美,k可以自行指定劳殖。這里指定k=2,raw_matches = matcher.knnMatch(desc1, desc2,2) 拨脉,然后每個match得到兩個最接近的descriptor哆姻,再計算最接近距離和次接近距離之間的比值,當比值大于某個設(shè)定的值時玫膀,才作為最終的match矛缨。
knnMatch結(jié)果如圖:
- RANSAC
為了進一步提升精度,還可以采用隨機采樣一致性(RANSAC)來過濾錯誤的匹配帖旨,該方法是利用匹配點計算兩圖像之間的單應(yīng)矩陣箕昭,然后利用重投影誤差來判定某一個匹配是否是正確的匹配。OpenCV中封裝了求解單應(yīng)矩陣的方法 findHomography ,可以為該方法設(shè)定一個重投影誤差的閾值解阅,可以得到一個向量mask來指定那些是符合重投影誤差的匹配點對落竹,用來過濾掉錯誤的匹配。
RANSAC結(jié)果如圖货抄,左邊是ORB后未篩選的結(jié)果述召,右邊是篩選優(yōu)化后的:
可以看出朱转,優(yōu)化的作用還是很明顯的。最后积暖,貼項目代碼如下:
"""
Created on Fri Jan 12 17:59:06 2018
Ransac test
@author:wanqingfeng
"""
import numpy as np
import cv2
img1 = cv2.imread("pic/face1.jpg",0)
img2 = cv2.imread("pic/face2.jpg",0)
detector = cv2.ORB_create()
flann_params= dict(algorithm = 6,
table_number = 6, # 12
key_size = 12, # 20
multi_probe_level = 1) #2
matcher = cv2.BFMatcher(cv2.NORM_HAMMING,crossCheck=False)
kp1,desc1 = detector.detectAndCompute(img1,None)
kp2,desc2 = detector.detectAndCompute(img2,None)
raw_matches = matcher.knnMatch(desc1, desc2,2) #2
good = []
for m,n in raw_matches:
if m.distance < 0.7*n.distance:
good.append(m)
if len(good)>10:
src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2)
dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2)
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)
matchesMask = mask.ravel().tolist()
else:
print("Not enough matches are found - %d/%d" % (len(good),10))
matchesMask = None
draw_params = dict(matchColor = (0,255,0), # draw matches in green color
singlePointColor = (0,0,255),
matchesMask = matchesMask,
flags = 2)# draw only inliers
vis = cv2.drawMatches(img1,kp1,img2,kp2,good,None,**draw_params)
cv2.imshow("", vis)
cv2.imwrite("pic/face_brisk_bf_ransac_1519.jpg", vis)
cv2.waitKey()
cv2.destroyAllWindows()
?