目标
本章节中
- 我们将实现如何不同图像之间匹配特征.
- 将使用两种匹配器,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()
请看结果如下:
本文暂时没有评论,来添加一个吧(●'◡'●)