OpenCV-Python教程:41.特征匹配

Brute-Force匹配器基礎

Brute-Force匹配器很簡單,它取第一個集合里一個特征的描述子并用第二個集合里所有其他的特征和他通過一些距離計算進行匹配。最近的返回。

對于BF匹配器,首先我們得用cv2.BFMatcher()創建BF匹配器對象.它取兩個可選參數,第一個是normType。它指定要使用的距離量度。默認是cv2.NORM_L2。對于SIFT,SURF很好。(還有cv2.NORM_L1)。對于二進制字符串的描述子,比如ORB,BRIEF,BRISK等,應該用cv2.NORM_HAMMING。使用Hamming距離度量,如果ORB使用VTA_K == 3或者4,應該用cv2.NORM_HAMMING2

第二個參數是布爾變量,crossCheck模式是false,如果它是true,匹配器返回那些和(i, j)匹配的,這樣集合A里的第i個描述子和集合B里的第j個描述子最匹配。兩個集合里的兩個特征應該互相匹配,它提供了連續的結果,

當它創建以后,兩個重要的方法是BFMatcher.match()和BFMatcher.knnMatch()。第一個返回最匹配的,第二個方法返回k個最匹配的,k由用戶指定。當我們需要多個的時候很有用。

想我們用cv2.drawKeypoints()來畫關鍵點一樣,cv2.drawMatches()幫我們畫匹配的結果,它把兩個圖像水平堆疊并且從第一個圖像畫線到第二個圖像來顯示匹配。還有一個cv2.drawMatchesKnn來畫k個最匹配的。如果k=2,它會給每個關鍵點畫兩根匹配線。所以我們得傳一個掩圖,如果我們想選擇性的畫的話。

讓我們各看一個SURF和ORB的例子(都使用了不同的距離度量)

使用ORB描述子的Brute-Force匹配

這里我們會看到如何匹配兩個圖片的里的特征。在這個例子里,我有一個查詢圖像和一個訓練圖像,我們用特征匹配來在訓練圖像里找查詢圖像。

我們使用SIFT描述子來匹配特征,所以讓我們先加載圖像,找到描述子。

import numpy as np
import cv2
from matplotlib import pyplot as plt

img1 = cv2.imread('box.png',0)? ? ? ? ? # queryImage
img2 = cv2.imread('box_in_scene.png',0) # trainImage

# Initiate SIFT detector
orb = cv2.ORB()

# find the keypoints and descriptors with SIFT
kp1, des1 = orb.detectAndCompute(img1,None)
kp2, des2 = orb.detectAndCompute(img2,None)

接著我們用距離度量cv2.NORM_HAMMING創建一個BFMatcher對象,crossCheck設為真。然后我們用Matcher.match()方法來獲得兩個圖像里最匹配的。我們按他們距離升序排列,這樣最匹配的(距離最小)在最前面。然后我們畫出最開始的10個匹配(為了好看,你可以增加)

# create BFMatcher object
bf = cv2.BFMatcher(cv2.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 = cv2.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 - 訓練圖像的索引

用SIFT描述子和比率測試的Brute-Force 匹配

這次,我們使用BFMatcher.knnMatch()來獲得k個最匹配的。在這個例子里,我們設置k=2這樣我們可以應用比率檢測。

import numpy as np
import cv2
from matplotlib import pyplot as plt

img1 = cv2.imread('box.png',0)? ? ? ? ? # queryImage
img2 = cv2.imread('box_in_scene.png',0) # trainImage

# Initiate SIFT detector
sift = cv2.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 = cv2.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])

# cv2.drawMatchesKnn expects list of lists as matches.
img3 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,good,flags=2)

plt.imshow(img3),plt.show()

看下面的結果

基于FLANN的匹配器

FLANN是快速估計最近鄰的庫。它包含了一些為大數據集內搜索快速近鄰和高維特征的優化算法。它在大數據集的時候比BFMatcher更快。

對于基于FLANN的匹配器,我們需要傳兩個字典指定要用的算法,他們相關的參數等。第一個是IndexParams。對于更多算法,要傳的信息參看FLANN的文檔。作為總結,對于像SIFT,SURF等的算法,你可以傳下面的:

index_params=dict(algorithm=FLANN_INDEX_KDTREE,trees=5)

當使用ORB,你可以傳下面的。注釋里的值是推薦的值。但是在有些情況下不提供需要的結果。其他的值工作正常。

index_params = dict(algorithm=FLANN_INDEX_LSH,
? ? table_number = 6, ?# 12
? ? key_size = 12, ? ?# 20
? ? multi_probe_level = 1) ?#2

第二個字典是SearchParams,它指定了索引里的樹應該被遞歸遍歷的次數。更高的值帶來更高的準確率。但是也花更多時間,如果你想改變值,search_params = dict(checks=100).

通過這些信息,我們可以開始了:

import numpy as np
import cv2
from matplotlib import pyplot as plt

img1 = cv2.imread('box.png',0)? ? ? ? ? # queryImage
img2 = cv2.imread('box_in_scene.png',0) # trainImage

# Initiate SIFT detector
sift = cv2.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 = 0
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)? # or pass empty dictionary

flann = cv2.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 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,matches,None,**draw_params)

plt.imshow(img3,),plt.show()

結果:

END

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容