目录
1 Brute-Force匹配的基础
2 对ORB描述符进行蛮力匹配
3 对 SIFT 描述符进行蛮力匹配和比值测试
4 FLANN匹配器
蛮力匹配器是很简单的。首先在第一幅图像中选取一个关键点然后依次与第二幅图像的每个关键点进行(描述符)距离测试,最后返回距离最近的关键点。
对于 BF 匹配器,我们首先要使用 cv2.BFMatcher() 创建一个 BFMatcher 对象。它有两个可选参数。第一个是 normType。它是用来指定要 使用的距离测试类型。默认值为 cv2.Norm_L2。这很适合 SIFT 和SURF等(c2.NORM_L1 也可以)。对于使用二进制描述符的 ORB,BRIEF,BRISK 算法等,要使用 cv2.NORM_HAMMING,这样就会返回两个测试对象之 间的汉明距离。如果 ORB 算法的参数设置为 VTA_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,就会为每个 关键点绘制两条最佳匹配直线。如果我们要选择性绘制话就要给函数传入一个 掩模。
让我们分别看一个 ORB 和一个 SURF 的例子吧。(使用不同距离计算方法)。
创建ORB对象,参考https://blog.csdn.net/yukinoai/article/details/89051973
检测特征点和计算描述符:
detector.detectAndCompute(image, mask, keypoints, descriptors, useProvidedKeypoints)
keypoints, descriptors = detector.detectAndCompute(image, mask,None, None, useProvidedKeypoints)
创建蛮力匹配对象BFMatcher:
bf = cv2.BFMatcher_create(, normType, crossCheck)
NORM_L1,对于SIFT和SURF描述符是较好的选择
NORM_L2,对于SIFT和SURF描述符是较好的选择,默认
NORM_HAMMING,应该与ORB,BRISK和BRIEF一起使用
NORM_HAMMING2,应该与当WTA_K==3或4时的ORB使用
输出匹配的结果:
matches = bf.match(queryDescriptors, trainDescriptors, mask)
他有下列属性:
绘制匹配结果:
outImg=cv.drawMatches(img1, keypoints1, img2, keypoints2, matches1to2, None, matchColor, singlePointColor, matchesMask, flags)
cv2.DRAW_MATCHES_FLAGS_DEFAULT,也可以取0:
默认值,只绘制特征点的坐标点,显示在图像上就是一个个小圆点,每个小圆点的圆心坐标都是特征点的坐标。
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS,也可以取4:
绘制特征点的时候绘制的是带有方向的圆,这种方法同时显示图像的坐标,size,和方向,是最能显示特征的一种绘制方式。
cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG,也可以取1:
只绘制特征点的坐标点,显示在图像上就是一个个小圆点,每个小圆点的圆心坐标都是特征点的坐标。
cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINT,也可以取0:
单点的特征点不被绘制
举个例子:
import numpy as np
import cv2
from matplotlib import pyplot as plt
img1 = cv2.imread('test37_0.jpg', 0)
img2 = cv2.imread('test37_1.jpg', 0)
orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)
# 根据距离排序
matches = sorted(matches, key=lambda x: x.distance)
draw_params = dict(matchColor=(0, 255, 0),
singlePointColor=(255, 0, 0),
flags=0)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:20], None, **draw_params)
plt.imshow(img3, cmap='gray'), plt.title('Matched Result'), plt.axis('off')
plt.show()
结果如下,flags=0,所以绘制了特征点,=2则不绘制
k最近邻匹配:
matches = bf.knnMatch(queryDescriptors, trainDescriptors, k, mask, compactResult)
绘制匹配结果
outImg = cv2.drawMatchesKnn(img1, keypoints1, img2, keypoints2, matches1to2, None, matchColor, singlePointColor, matchesMask, flags)
参数含义与上面相同
举个例子:
import numpy as np
import cv2
from matplotlib import pyplot as plt
img1 = cv2.imread('test37_0.jpg', 0) # queryImage
img2 = cv2.imread('test37_1.jpg', 0)
# Initiate SIFT detector
sift = cv2.xfeatures2d.SIFT_create()
# 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)
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[:20], None, flags=2)
plt.imshow(img3, cmap='gray'), plt.title('Matched Result'), plt.axis('off')
plt.show()
结果如下,结果很差
FLANN是快速最近邻搜索包(Fast_Library_for_Approximate_Nearest_Neighbors)的简称。它是一个对大数据集和高维特征进行最近邻搜索的算法的集合,而且 这些算法都已经被优化过了。在面对大数据集时它的效果要好于 BFMatcher。 我们来对第二个例子使用 FLANN 匹配看看它的效果。
flann = cv2.FlannBasedMatcher(index_params, search_params)
每种算法该怎么设置参数可查看官网地址
FLANN_INDEX_LINEAR | LINEAR |
FLANN_INDEX_KDTREE | KDTREE |
FLANN_INDEX_KMEANS | KMEANS |
FLANN_INDEX_COMPOSITE | COMPOSITE |
FLANN_INDEX_KDTREE_SINGLE | KDTREE_SINGLE |
FLANN_INDEX_HIERARCHICAL | SAVED |
FLANN_INDEX_LSH | AUTOTUNED |
FLANN_INDEX_SAVED | |
FLANN_INDEX_AUTOTUNED |
举个例子:
import numpy as np
import cv2
from matplotlib import pyplot as plt
img1 = cv2.imread('test37_0.jpg', 0)
img2 = cv2.imread('test37_1.jpg', 0)
sift = cv2.xfeatures2d.SIFT_create(nfeatures=50)
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
FLANN_INDEX_KDTREE = 0 # kd树
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 range(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, cmap='gray'), plt.title('Matched Result'), plt.axis('off')
plt.show()
结果如下,可以看出匹配结果还是不错的。
结论:可以看出ORB蛮力匹配和FLANN匹配结果都不错,另外对于不同的图片,参数的设置对结果的影响很大。