OpenCV入门(二十八)快速学会OpenCV 27 图像匹配
OpenCV入门(二十八)快速学会OpenCV 27 图像匹配
作者:Xiou
1.使用基于FAST特征和BRIEF描述符的ORB
如果说SIFT还很年轻,SURF更年轻,那么ORB就还处于婴儿期。ORB首次发布于2011年,作为SIFT和SURF的一个快速代替品。该算法发表在论文“ORB:an efficient alternative to SIFT or SURF”上,可以在
http://www.willowgarage.com/sites/default/files/orb_final.pdf
处找到PDF格式的论文。ORB融合了FAST关键点检测器和BRIEF关键点描述符,所以有必要先了解一下FAST和BRIEF。接下来,我们将讨论蛮力匹配(用于特征匹配的算法)并举一个特征匹配的例子。
1.1 FAST
加速分割测试的特征(Feature from Accelerated Segment Test,FAST)算法是通过分析16个像素的圆形邻域来实现的。
FAST算法把邻域内每个像素标记为比特定阈值更亮或更暗,该阈值是相对于圆心定义的。
如果邻域包含若干标记为更亮或更暗的一系列连续像素,那么这个邻域就被视为角点。FAST还使用了一种高速测试,有时可以通过只检查2个或者4个像素(而不是16个像素)来确定邻域不是角点。
FAST是一个智能算法,但是它并不是没有缺点,为了弥补这些缺点,从事图像分析的开发人员可以实现一种机器学习算法,以便为算法提供一组(与给定应用程序相关的)图像,从而优化阈值等参数。不管开发人员是直接指定参数,还是为机器学习方法提供一个训练集,FAST都是对输入很敏感的一种算法,也许比SIFT更敏感。
1.2 BRIEF
另外,二值鲁棒独立基本特征(Binary Robust Independent Elementary Feature,BRIEF)并非特征检测算法,而是一个描述符。我们来更深入地研究一下描述符的概念,然后再来研究BRIEF。
在前面用SIFT和SURF分析图像时,整个过程的核心是调用detectAndCompute函数。此函数执行两个不同的步骤——检测和计算,它们返回2个不同的结果(耦合到一个元组中)。
检测结果是一组关键点,计算结果是这些关键点的一组描述符。这意味着OpenCV的cv2.SIFT和cv2.SURF类都实现了检测和描述算法。请记住,原始的SIFT和SURF不是特征检测算法。OpenCV的cv2.SIFT实现了DoG特征检测和SIFT描述,而OpenCV的cv2.SURF实现了快速Hessian特征检测和SURF描述。
关键点描述符是图像的一种表示,充当特征匹配的通道,因为你可以比较两幅图像的关键点描述符并发现它们的共性。BRIEF是目前最快的描述符之一。
BRIEF背后的理论相当复杂,但是可以这样说,BRIEF采用一系列优化,使其成为特征匹配的一个非常好的选择。
1.3 蛮力匹配
蛮力匹配器是一个描述符匹配器,它比较两组关键点描述符并生成匹配列表。之所以称为蛮力匹配,是因为在该算法中几乎不涉及优化。对于第一个集合中的每个关键点描述符,匹配器将之与第二个集合中的每个关键点描述符进行比较。每次比较产生一个距离值,并基于最小距离选择最佳匹配。
概括地说,在计算中,“蛮力”一词是指将所有可能组合(例如,破解已知长度密码的所有可能的字符组合)的穷举按优先级排序的方法。
相反,优先考虑速度的算法可能会跳过一些可能性,并试图走一条捷径来找到看似最合理的解决方案。OpenCV提供了一个cv2.BFMatcher类,支持几种蛮力特征匹配的方法。
2.代码实例
ORB的目标是优化和加速操作,包括非常重要的以旋转感知的方式利用BRIEF的步骤,这样匹配就得以改善,即使在训练图像与查询图像有非常不同的旋转状态的情况下也是如此。不过,在这个阶段,你可能已经了解了足够的理论,希望深入研究一些特征匹配,我们来看一些代码。下面的脚本试图将标识中的特征与包含该标识的照片中的特征进行匹配:
import cv2
from matplotlib import pyplot as plt
# Load the images.
img0 = cv2.imread('../images/nasa_logo.png',
cv2.IMREAD_GRAYSCALE)
img1 = cv2.imread('../images/kennedy_space_center.jpg',
cv2.IMREAD_GRAYSCALE)
# Perform ORB feature detection and description.
orb = cv2.ORB_create()
kp0, des0 = orb.detectAndCompute(img0, None)
kp1, des1 = orb.detectAndCompute(img1, None)
# Perform brute-force matching.
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des0, des1)
# Sort the matches by distance.
matches = sorted(matches, key=lambda x:x.distance)
# Draw the best 25 matches.
img_matches = cv2.drawMatches(
img0, kp0, img1, kp1, matches[:25], img1,
flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
# Show the matches.
plt.imshow(img_matches)
plt.show()
查询图像:
输出结果:
与使用SIFT和SURF的方式类似,我们检测并计算这两幅图像的关键点和描述符。从这里开始,概念非常简单:遍历描述符并确定是否匹配,然后计算匹配的质量(距离),并对匹配进行排序,这样就可以在一定程度上显示前n个匹配,它们实际上匹配了两幅图像上的特征。cv2.BFMatcher可以实现这一任务。
3.match vs knnMatch
match 格式:
match = bf.match(des1, des2)
返回值:
querlyIdx: 测试图像的特征点的 index
trainIdx: 测试图像的特征点的 index
distance: 匹配特征点的欧式距离, 数值越小越接近
knnMatch 格式:
match = bf.knnMatch(des1, des2, k)
返回值:
m: 最近的匹配特征点
n: 第二近的匹配点, 如果 m 和 n 的距离足够大, 那么这就是一个正确的匹配
knnMatch返回列表的列表,每个内部列表至少包含一个匹配项,且不超过k个匹配项,各匹配项从最佳(最短距离)到最差依次排序。
代码实例:
import cv2
from matplotlib import pyplot as plt
# Load the images.
img0 = cv2.imread('../images/nasa_logo.png',
cv2.IMREAD_GRAYSCALE)
img1 = cv2.imread('../images/kennedy_space_center.jpg',
cv2.IMREAD_GRAYSCALE)
# Perform ORB feature detection and description.
orb = cv2.ORB_create()
kp0, des0 = orb.detectAndCompute(img0, None)
kp1, des1 = orb.detectAndCompute(img1, None)
# Perform brute-force KNN matching.
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=False)
pairs_of_matches = bf.knnMatch(des0, des1, k=2)
# Sort the pairs of matches by distance.
pairs_of_matches = sorted(pairs_of_matches, key=lambda x:x[0].distance)
# Draw the 25 best pairs of matches.
img_pairs_of_matches = cv2.drawMatchesKnn(
img0, kp0, img1, kp1, pairs_of_matches[:25], img1,
flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
# Show the pairs of matches.
plt.imshow(img_pairs_of_matches)
plt.show()
# Apply the ratio test.
matches = [x[0] for x in pairs_of_matches
if len(x) > 1 and x[0].distance < 0.8 * x[1].distance]
# Draw the best 25 matches.
img_matches = cv2.drawMatches(
img0, kp0, img1, kp1, matches[:25], img1,
flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
# Show the matches.
plt.imshow(img_matches)
plt.show()
输出结果: