1.Brute-Force匹配

蛮力匹配器是很简单的。首先在第一幅图像中选取一个关键点然后依次与第二幅图像的每个关键点进行(描述符)距离测试,最后返回距离最近的关键点。

对于 BF 匹配器,我们首先要使用cv2.BFMatcher() 创建一个 BFMatcher 对象。它有两个可选参数。第一个是normType。它是用来指定要使用的距离测试类型。默认值为cv2.Norm_L2。这很适合SIFT和SURF等(c2.NORM_L1 也可以)。对于使用二进制描述符的 ORB,BRIEF,BRISK算法等,要使用 cv2.NORM_HAMMING,这样就会返回两个测试对象之间的汉明距离。如果 ORB 算法的参数设置为 V T A_K==3 或 4,normType就应该设置成 cv2.NORM_HAMMING2。

第二个参数是布尔变量 crossCheck,默认值为False。如果设置为True,匹配条件就会更加严格,只有到 A 中的第 i 个特征点与 B 中的第 j 个特征点距离最近,并且 B 中的第 j 个特征点到 A 中的第 i 个特征点也是最近(A 中没有其他点到 j 的距离更近)时才会返回最佳匹配(i,j)。也就是这两个特征点要互相匹配才行。这样就能提供统一的结果,这可以用来替代 D.Lowe在 SIFT 文章中提出的比值测试方法。

BFMatcher 对象具有两个方法,BFMatcher.match() 和BFMatcher.knnMatch()。第一个方法会返回最佳匹配。第二个方法为每个关键点返回 k 个最佳匹配(降序排列之后取前 k 个),其中 k 是由用户设定的。如果除了匹配之外还要做其他事情的话可能会用上(比如进行比值测试)。

就像使用 cv2.drawKeypoints() 绘制关键点一样,我们可以使用cv2.drawMatches() 来绘制匹配的点。它会将这两幅图像先水平排列,然后在最佳匹配的点之间绘制直线(从原图像到目标图像)。如果前面使用的是 BFMatcher.knnMatch(),现在我们可以使用函数 cv2.drawMatchsKnn为每个关键点和它的 k 个最佳匹配点绘制匹配线。如果 k 等于 2,就会为每个关键点绘制两条最佳匹配直线。如果我们要选择性绘制话就要给函数传入一个掩模。

1.1 对ORB描述符进行BF匹配

代码演示:

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

img1 = cv2.imread('pizza1.jpg')
img2 = cv2.imread('pizza2.jpg')

# Initiate ORB detector
orb = cv2.ORB_create()
# find the kp and descriptors with ORB
kp1,des1 = orb.detectAndCompute(img1,None)
kp2,des2 = orb.detectAndCompute(img2,None)
# 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],None,flags=2)
plt.imshow(cv2.cvtColor(img3, cv2.COLOR_BGR2RGB))
plt.show()

创建一个 BFMatcher 对象,并将距离计算设置为cv2.NORM_HAMMING(因为我们使用的是 ORB),并将 crossCheck 设置为 True。然后使用 Matcher.match()方法获得两幅图像的最佳匹配。然后将匹配结果按特征点之间的距离进行降序排列,这样最佳匹配就会排在前面了。最后我们只将前 10 个匹配绘制出来。

Python如何获得特征点配准的正确率 python opencv 特征匹配_搜索

matches = bf.match(des1, des2) 返回值是一个 DMatch 对象列表。这个DMatch 对象具有下列属性:

  • DMatch.distance:描述符之间的距离。越小越好。
  • DMatch.trainIdx : 目标图像中描述符的索引。
  • DMatch.queryIdx :查询图像中描述符的索引。
  • DMatch.imgIdx :目标图像的索引。

1.2 对SIFT描述符进行BF匹配和比值测试

现在使用 BFMatcher.knnMatch() 来获得 k 对最佳匹配。在本例中设置 k = 2,代码演示:

import cv2
import numpy as np
from matplotlib import pyplot as plt
img1 = cv2.imread('pizza_part.jpg')
img2 = cv2.imread('pizza2.jpg')
# Initiate ORB detector
sift = cv2.SIFT_create()
# find the kp and descriptors with ORB
kp1,des1 = sift.detectAndCompute(img1,None)
kp2,des2 = sift.detectAndCompute(img2,None)
# create BFMatcher object
bf = cv2.BFMatcher()
# Match descriptors
matches = bf.knnMatch(des1,des2,k=2)
# Apply ratio test
# 比值测试,首先获取与 A 距离最近的点 B(最近)和 C(次近),只有当 B/C
# 小于阈值时(0.2)才被认为是匹配,因为假设匹配是一一对应的,真正的匹配的理想距离为 0
good = []
for m,n in matches:
    if m.distance < 0.2*n.distance:
        good.append([m])
# cv2.drawMatchesKnn expects list of lists as matches.
img3 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=2)
plt.imshow(cv2.cvtColor(img3, cv2.COLOR_BGR2RGB))
plt.show()

Python如何获得特征点配准的正确率 python opencv 特征匹配_描述符_02

2 FLANN匹配器

FLANN 是快速最近邻搜索包(Fast_Library_for_Approximate_Nearest_Neighbors)的简称。它是一个对大数据集和高维特征进行最近邻搜索的算法的集合,而且这些算法都已经被优化过了。在面对大数据集时它的效果要好于BFMatcher。

使用 FLANN 匹配,我们需要传入两个字典作为参数。这两个用来确定要使用的算法和其他相关参数等。第一个是 IndexParams。各种不同算法的信息可以在 FLANN 文档中找到。这里我们总结一下,对于 SIFT 和 SURF 等,我们可以传入的参数是:
FLANN_INDEX_KDTREE = 1index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)。

第二个字典是 SearchParams。用它来指定递归遍历的次数。值越高结果越准确,但是消耗的时间也越多。如果你想修改这个值,传入参数:searchparams = dict(checks = 100)。

代码演示:

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

img1 = cv2.imread('pizza_part.jpg')
img2 = cv2.imread('pizza2.jpg')

# Initiate SIFT detector
sift = cv2.SIFT_create()
# find the keypoints and descriptors with SIF
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)
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 range(len(matches))]

# ratio test as per Lowe's paper
for i,(m,n) in enumerate(matches):
    if m.distance < 0.2*n.distance:
        matchesMask[i] = [1,0]       
        
draw_params = dict(matchColor = (0,255,0),
                  singlePointColor = (255,0,0),
                  matchesMask = matchesMask,
                  flags = cv2.DrawMatchesFlags_DEFAULT)
                  
img3 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,matches,None,**draw_params)
plt.imshow(cv2.cvtColor(img3, cv2.COLOR_BGR2RGB))
plt.show()

Python如何获得特征点配准的正确率 python opencv 特征匹配_搜索_03