本章将学习以下内容:
- 学习如何将一幅图像中的特征与其他图像进行匹配
- 使用OpenCV中的暴力匹配器(Brute-Force Matcher)和快速近似最近邻匹配器(FLANN Matcher)
暴力匹配器基础
暴力匹配器原理简单:它提取第一组特征中某个特征的描述符,并通过距离计算与第二组中的所有特征进行匹配,最终返回最接近的匹配结果。
使用暴力匹配器时,首先需要通过cv2.BFMatcher()
创建BFMatcher对象。该函数接收两个可选参数:
normType
:指定距离度量方式,默认为cv2.NORM_L2
(适用于SIFT、SURF等算法,cv2.NORM_L1
也可用)。 对于基于二进制字符串的描述符(如ORB、BRIEF、BRISK等),应使用cv2.NORM_HAMMING
(以汉明距离为度量标准)。若ORB使用VTA_K
== 3或4,则需改用cv2.NORM_HAMMING2
。crossCheck
:布尔参数,默认为false。若为true,匹配器仅返回满足双向一致性的匹配对——即集合A中的第i个描述符与集合B中的第j个描述符互为最佳匹配。 这种方式能提供稳定的匹配结果,可作为D.Lowe在SIFT论文中提出的比率检验的替代方案。
创建匹配器后,有两个重要方法:BFMatcher.match()
即返回最佳单匹配结果。
BFMatcher.knnMatch()
即返回用户指定的前k个最佳匹配(适用于需进一步处理的场景)。
与绘制关键点的cv2.drawKeypoints()
类似,cv2.drawMatches()
用于绘制匹配结果。
它会水平堆叠两幅图像,并绘制从第一幅图像到第二幅图像的最佳匹配连线。
另有cv2.drawMatchesKnn
可绘制所有前k个匹配(例如k=2时,每个关键点会对应两条匹配线),此时需通过掩码选择性绘制。
接下来我们将分别演示SURF和ORB的匹配示例(两者使用不同的距离度量方式)。
使用ORB描述符的暴力匹配
在此示例中,我们将展示如何在两幅图像之间进行特征匹配。
假设有一张查询图像(queryImage)和一张训练图像(trainImage),
将尝试通过特征匹配在训练图像中定位查询图像(示例图像路径为 /samples/c/box.png
和 /samples/c/box_in_scene.png
)。
我们使用SIFT描述符进行特征匹配。以下是具体步骤: 加载图像;提取特征描述符等。
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img1 = cv.imread('/data/cvdata/box.png',cv.IMREAD_GRAYSCALE) # queryImage
img2 = cv.imread('/data/cvdata/box_in_scene.png',cv.IMREAD_GRAYSCALE) # 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对象,使用汉明距离(cv2.NORM_HAMMING
)作为度量标准(由于我们使用的是ORB描述符),
并启用交叉验证(crossCheck=True)以提高匹配质量。随后,调用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],None,flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
plt.imshow(img3),plt.show()
(<matplotlib.image.AxesImage at 0x7fbf96c36ed0>, None)