目標
本章節中
- 我們將實現如何不同圖像之間匹配特徵.
- 將使用兩種匹配器,openCV提供了Brute-Force 和 FLANN
Brute-Force 匹配器的基本原理
Brute-Force匹配器很簡單。它採用第一個集合中一個特徵的描述符,並通過距離計算與第二個集合中的所有其他特徵匹配,返回最近的一個。對於BF匹配器,首先必須使用cv.BFMatcher()創建BFMatcher對象。它需要兩個可選參數。第一個是normType。它指定要使用的距離度量。默認情況下,它是cv.NORM_L2。適用於SIFT、SURF等。cv.NORM_L1也在那裡)。對於基於二進制字符串的描述符,如ORB、BRIEF、lively等,使用cv.NORM_HAMMING,它使用Hamming distance作為度量。如果ORB使用WTA_K == 3或4,則使用cv.NORM_HAMMING2。
第二個參數是布爾變量crossCheck,默認為false。如果為真,Matcher只返回值為(i,j)的匹配項,以便集合A中的第i個描述符與集合B中的第j個描述符具有最佳匹配,反之亦然。也就是說,兩個集合中的兩個特性應該相互匹配。它可以得到可靠的結果,是D.Lowe的論文中提出的一個比值檢驗很好的替代法。創建之後,兩個重要的方法是BFMatcher.match()和BFMatcher.knnMatch()。第一個返回最佳匹配。第二個方法返回由用戶指定的k的最佳匹配。當我們需要在這方面做額外的工作時,它可能是有用的。
就像我們使用了cv.drawKeypoints()來繪製關鍵點一樣,cv.drawMatches()幫助我們繪製匹配項。它水平地堆疊兩個圖像,並從第一個圖像到第二個圖像繪製線,以顯示最佳匹配。還有cv.drawMatchesKnn可以畫出所有k個最佳匹配項。如果k=2,它將為每個關鍵點繪製兩條匹配線。所以我們必須傳遞一個蒙版如果我們想有選擇地畫它。讓我們來分別看看用SURF和ORB的示例 (它們都使用不同的距離測量)。
BF 匹配 ORB 描述子
在這裡,我們將看到一個關於如何在兩個圖像之間匹配特性的簡單示例。在本例中,我有一個queryImage和一個trainImage。我們將嘗試使用特徵匹配在trainImage中找到queryImage。
( 實例圖像 /samples/c/box.png and /samples/c/box_in_scene.png)
我們使用ORB描述符來匹配特性。讓我們從加載圖像開始,尋找描述符等等。
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img1 = cv.imread('box.png',0) # queryImage
img2 = cv.imread('box_in_scene.png',0) # trainImage
# Initiate ORB detector
orb = cv.ORB_create()
# find the keypoints and descriptors with ORB
kp1, des1 = orb.detectAndCompute(img1,None)
kp2, des2 = orb.detectAndCompute(img2,None)
接下來,我們創建一個帶有距離測量的BFMatcher對象。cv.NORM_HAMMING(因為我們使用ORB)和crossCheck被打開以獲得更好的結果。然後我們使用Matcher.match()方法在兩個圖像中獲得最佳匹配。我們按照它們之間距離的升序對它們進行排序,以便最好的匹配(低距離)出現在前面。然後我們只繪製前10個匹配項(只是為了便於查看)。你可以隨意增加)
# create BFMatcher object
bf = cv.BFMatcher(cv.NORM_HAMMING, crossCheck=True)
# Match descriptors.
matches = bf.match(des1,des2)
# Sort them in the order of their distance.
matches = sorted(matches, key = lambda x:x.distance)
# Draw first 10 matches.
img3 = cv.drawMatches(img1,kp1,img2,kp2,matches[:10], flags=2)
plt.imshow(img3),plt.show()
以下就是我們得到的結果:
什麼是匹配器對象?
代碼行中 matches = bf.match(des1,des2) 返回的結果是 DMatch 對象的列表. 這個DMatch 對象有以下屬性:
- DMatch.distance - 描述子之間的距離. 越小越好.
- DMatch.trainIdx - 訓練集中的描述子的索引
- DMatch.queryIdx - 檢索集中的描述子的索引
- DMatch.imgIdx - 訓練集圖像的索引.
BF 匹配 SIFT 描述子和比值檢測
這次,我們將使用BFMatcher.knnMatch()獲得k個最佳匹配。在這個例子中,我們取k=2,這樣我們就可以應用D.Lowe在論文中解釋的比值檢驗。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img1 = cv.imread('box.png',0) # queryImage
img2 = cv.imread('box_in_scene.png',0) # trainImage
# Initiate SIFT detector
sift = cv.SIFT()
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)
# BFMatcher with default params
bf = cv.BFMatcher()
matches = bf.knnMatch(des1,des2, k=2)
# Apply ratio test
good = []
for m,n in matches:
if m.distance < 0.75*n.distance:
good.append([m])
# cv.drawMatchesKnn expects list of lists as matches.
img3 = cv.drawMatchesKnn(img1,kp1,img2,kp2,good,flags=2)
plt.imshow(img3),plt.show()
以下得到結果:
基於FLANN 匹配器
FLANN是Fast Library for Approximate Nearest Neighbors的縮寫。它包含一組優化算法,用於在大型數據集中快速搜索最近鄰和高維特性。對於大型數據集,它比BFMatcher工作得更快。我們將看到基於FLANN的匹配器的第二個例子。
對於基於FLANN的匹配器,我們需要通過兩個字典來指定要使用的算法及其相關參數等。第一個是IndexParams。對於各種算法,要傳遞的信息在FLANN文檔中進行了解釋。作為總結,對於SIFT、SURF等算法,您可以通過以下步驟:
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
在使用ORB時,您可以傳遞以下內容。註釋值是根據文檔推薦的,但在某些情況下它沒有提供所需的結果。其他的值可能會更好:
FLANN_INDEX_LSH = 6
index_params= dict(algorithm = FLANN_INDEX_LSH,
table_number = 6, # 12
key_size = 12, # 20
multi_probe_level = 1) #2
第二個字典是SearchParams。它指定應該遞歸遍歷索引中的樹的次數。數值越大,精度越高,但也需要更多的時間。如果要更改值,請傳遞search_params = dict(check =100)。有了這些信息,我們可以開始了。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img1 = cv.imread('box.png',0) # queryImage
img2 = cv.imread('box_in_scene.png',0) # trainImage
# Initiate SIFT detector
sift = cv.SIFT()
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)
# FLANN parameters
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50) # or pass empty dictionary
flann = cv.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)
# Need to draw only good matches, so create a mask
matchesMask = [[0,0] for i in xrange(len(matches))]
# ratio test as per Lowe's paper
for i,(m,n) in enumerate(matches):
if m.distance < 0.7*n.distance:
matchesMask[i]=[1,0]
draw_params = dict(matchColor = (0,255,0),
singlePointColor = (255,0,0),
matchesMask = matchesMask,
flags = 0)
img3 = cv.drawMatchesKnn(img1,kp1,img2,kp2,matches,None,**draw_params)
plt.imshow(img3,),plt.show()
請看結果如下:
閱讀更多 閒著學點什麼 的文章