OpenCV-Python——第30章:特征匹配

目录

1 Brute-Force匹配的基础

2 对ORB描述符进行蛮力匹配

 3 对 SIFT 描述符进行蛮力匹配和比值测试

4 FLANN匹配器


1 Brute-Force匹配的基础

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

对于 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 的例子吧。(使用不同距离计算方法)。

 

2 对ORB描述符进行蛮力匹配

创建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)

  • image:输入图像
  • mask:掩膜
  • keypoints:输出特征点
  • descriptors:输出描述符
  • useProvidedKeypoints:是否使用提供的点,默认False

创建蛮力匹配对象BFMatcher:

bf = cv2.BFMatcher_create(, normType, crossCheck)

  • normType:取模方法,有四种可选:

             NORM_L1,对于SIFT和SURF描述符是较好的选择

             NORM_L2,对于SIFT和SURF描述符是较好的选择,默认

             NORM_HAMMING,应该与ORB,BRISK和BRIEF一起使用

             NORM_HAMMING2,应该与当WTA_K==3或4时的ORB使用

  • crossCheck:如果为false(默认),当BFMatcher为每个查询描述符找到k最近邻时,这将是默认的BFMatcher行为。如果crossCheck==true,那么k=1的knnMatch()方法只返回对(i,j),这样对于第i个查询描述符,匹配器集合中的第j个描述符是最近的,反之亦然,即BFMatcher只返回一致的对。当有足够的匹配时,这种技术通常用最少的异常值产生最好的结果。

输出匹配的结果:

matches = bf.match(queryDescriptors, trainDescriptors, mask)

他有下列属性:

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

绘制匹配结果:

outImg=cv.drawMatches(img1, keypoints1, img2, keypoints2, matches1to2, None, matchColor, singlePointColor, matchesMask, flags)

  • img1:第一张输入图像
  • keypoints1:第一张输入图像特征点
  • img2:第二张输入图像
  • keypoints2:第二张输入图像的特征点
  • matches1to2:匹配从第一个图像到第二个图像,这意味着keypointts1 [i]在keypointts2 [Matches [i]]中有一个对应的点。
  • outImg:输出图像
  • matchColor:线条颜色,默认随机
  • singlePointColor:特征点的颜色,默认随机
  • matchesMask:掩码决定绘制哪些匹配项。如果掩码为空,则绘制所有匹配项。
  • flags:标志设置绘图功能。可能的标志位值由DrawMatchesFlags定义:

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则不绘制

 

 3 对 SIFT 描述符进行蛮力匹配和比值测试

k最近邻匹配:

matches = bf.knnMatch(queryDescriptors, trainDescriptors, k, mask, compactResult)

  • queryDescriptors:待匹配描述符
  • trainDescriptors:训练描述符
  • k:如果查询描述符总共有少于k个可能的匹配项,则计算每个查询描述符找到的最佳匹配项的数量或更少。
  • mask:掩膜
  • compactResult:当掩码(或多个掩码)不为空时使用的参数。如果compactResult为false(默认),则匹配向量的大小与queryDescriptors行相同。如果compactResult为True,则匹配向量不包含完全屏蔽的查询描述符的匹配。

绘制匹配结果

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()

结果如下,结果很差

 

4 FLANN匹配器

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

flann = cv2.FlannBasedMatcher(index_params, search_params)

  • index_params:字典类型,例如先选择算法:algorithm取值如下,在对该算法进行参数设置,例如选择KDTree算法,则可取dict(algorithm=FLANN_INDEX_KDTREE, trees=5)

每种算法该怎么设置参数可查看官网地址

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  
  • search_params:字典类型,默认dict(checks=32, eps=0, sorted=True)

举个例子:

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匹配结果都不错,另外对于不同的图片,参数的设置对结果的影响很大。

  • 9
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值