OpenCV系列之特徵匹配 | 四十四

目標
在本章中,算法

  • 咱們將看到如何將一個圖像中的特徵與其餘圖像進行匹配。
  • 咱們將在OpenCV中使用Brute-Force匹配器和FLANN匹配器
    Brute-Force匹配器的基礎
    蠻力匹配器很簡單。它使用第一組中一個特徵的描述符,並使用一些距離計算將其與第二組中的全部其餘特徵匹配。並返回最接近的一個。
    對於BF匹配器,首先咱們必須使用cv.BFMatcher()建立BFMatcher對象。
    它須要兩個可選參數。第一個是normType,它指定要使用的距離測量。默認狀況下爲cv.NORM_L2。對於SIFT,SURF等(也有cv.NORM_L1)頗有用。
    對於基於二進制字符串的描述符,例如ORB,BRIEF,BRISK等,應使用cv.NORM_HAMMING,該函數使用漢明距離做爲度量。若是ORB使用WTA_K == 3或4,則應使用cv.NORM_HAMMING2。
    第二個參數是布爾變量,即crossCheck,默認狀況下爲false。若是爲true,則Matcher僅返回具備值(i,j)的那些匹配項,以使集合A中的第i個描述符具備集合B中的第j個描述符爲最佳匹配,反之亦然。即兩組中的兩個特徵應彼此匹配。它提供了一致的結果,而且是D.Lowe在SIFT論文中提出的比率測試的良好替代方案。
    建立以後,兩個重要的方法是BFMatcher.match()和BFMatcher.knnMatch()。第一個返回最佳匹配。第二種方法返回k個最佳匹配,其中k由用戶指定。當咱們須要對此作其餘工做時,它可能會頗有用。
    就像咱們使用cv.drawKeypoints()繪製關鍵點同樣,cv.drawMatches()能夠幫助咱們繪製匹配項。它水平堆疊兩張圖像,並繪製從第一張圖像到第二張圖像的線,以顯示最佳匹配。還有cv.drawMatchesKnn繪製全部k個最佳匹配。若是k=2,它將爲每一個關鍵點繪製兩條匹配線。所以,若是要選擇性地繪製,則必須經過掩碼。
    讓咱們來看一個SIFT和ORB的示例(二者都使用不一樣的距離測量)。
    使用ORB描述符進行Brute-Force匹配
    在這裏,咱們將看到一個有關如何在兩個圖像之間匹配特徵的簡單示例。在這種狀況下,我有一個queryImage和trainImage。咱們將嘗試使用特徵匹配在trainImage中找到queryImage。(圖像是/samples/data/box.png和/samples/data/box_in_scene.png)
    咱們正在使用ORB描述符來匹配特徵。所以,讓咱們從加載圖像,查找描述符等開始。
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img1 = cv.imread('box.png',cv.IMREAD_GRAYSCALE)          # 索引圖像
img2 = cv.imread('box_in_scene.png',cv.IMREAD_GRAYSCALE) # 訓練圖像
# 初始化ORB檢測器
orb = cv.ORB_create()
# 基於ORB找到關鍵點和檢測器
kp1, des1 = orb.detectAndCompute(img1,None)
kp2, des2 = orb.detectAndCompute(img2,None)

接下來,咱們建立一個距離測量值爲cv.NORM_HAMMING的BFMatcher對象(由於咱們使用的是ORB),而且啓用了CrossCheck以得到更好的結果。而後,咱們使用Matcher.match()方法來獲取兩個圖像中的最佳匹配。咱們按照距離的升序對它們進行排序,以使最佳匹配(低距離)排在前面。而後咱們只抽出前10的匹配(只是爲了提升可見度。您能夠根據須要增長它)app

# 建立BF匹配器的對象
bf = cv.BFMatcher(cv.NORM_HAMMING, crossCheck=True) # 匹配描述符.
matches = bf.match(des1,des2) # 根據距離排序
matches = sorted(matches, key = lambda x:x.distance) # 繪製前10的匹配項
img3 = cv.drawMatches(img1,kp1,img2,kp2,matches[:10],None,flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS) plt.imshow(img3),plt.show()

將得到如下的結果:
OpenCV系列之特徵匹配 | 四十四
什麼是Matcher對象?
matchs = bf.match(des1,des2)行的結果是DMatch對象的列表。該DMatch對象具備如下屬性:ide

  • DMatch.distance-描述符之間的距離。越低越好。
  • DMatch.trainIdx-火車描述符中的描述符索引
  • DMatch.queryIdx-查詢描述符中的描述符索引
  • DMatch.imgIdx-火車圖像的索引。
    帶有SIFT描述符和比例測試的Brute-Force匹配
    此次,咱們將使用BFMatcher.knnMatch()得到k個最佳匹配。在此示例中,咱們將k = 2,以即可以應用D.Lowe在他的論文中闡述的比例測試。

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img1 = cv.imread('box.png',cv.IMREAD_GRAYSCALE) # 索引圖像
img2 = cv.imread('box_in_scene.png',cv.IMREAD_GRAYSCALE) # 訓練圖像函數

初始化SIFT描述符

sift = cv.xfeatures2d.SIFT_create()測試

基於SIFT找到關鍵點和描述符

kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)優化

默認參數初始化BF匹配器

bf = cv.BFMatcher()
matches = bf.knnMatch(des1,des2,k=2)3d

應用比例測試

good = []
for m,n in matches:
if m.distance < 0.75*n.distance:
good.append([m])code

cv.drawMatchesKnn將列表做爲匹配項。

img3 = cv.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
plt.imshow(img3),plt.show()orm

查看如下結果:
![](https://s4.51cto.com/images/blog/202101/06/b770b42c2fd4ffecbf7ad3cee26a801a.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)
**基於匹配器的FLANN**
FLANN是近似最近鄰的快速庫。它包含一組算法,這些算法針對大型數據集中的快速最近鄰搜索和高維特徵進行了優化。對於大型數據集,它的運行速度比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(checks = 100)
有了這些信息,咱們就很容易了。

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img1 = cv.imread('box.png',cv.IMREAD_GRAYSCALE) # 索引圖像
img2 = cv.imread('box_in_scene.png',cv.IMREAD_GRAYSCALE) # 訓練圖像

初始化SIFT描述符

sift = cv.xfeatures2d.SIFT_create()

基於SIFT找到關鍵點和描述符

kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)

FLANN的參數

FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50) # 或傳遞一個空字典
flann = cv.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)

只須要繪製好匹配項,所以建立一個掩碼

matchesMask = [[0,0] for i in range(len(matches))]

根據Lowe的論文進行比例測試

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 = cv.DrawMatchesFlags_DEFAULT)
img3 = cv.drawMatchesKnn(img1,kp1,img2,kp2,matches,None,**draw_params)
plt.imshow(img3,),plt.show()

查看如下結果:
![](https://s4.51cto.com/images/blog/202101/06/69da30d86448f394c82ff82da606b764.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)
相關文章
相關標籤/搜索