作者:RayChiu_Labloy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
目录
BFMatcher对象的两个匹配方法->bf.match()和bf.knnMatch()
绘制匹配的点的函数drawMatches()和drawMatchsKnn()
匹配器生成方法FlannBasedMatcher()及两个入参参数解释
第二个指定了索引里的树应该被递归遍历的次数(更高的值带来更高的准确率。但是也花更多时间,如果你想改变值)。
之前几篇文章我们讨论过了众多的特征检测算法,这次我们来讨论如何运用相关的方法进行特征匹配 。
Brute-Force 蛮力匹配器:
原理和使用过程
使用的方式就是从图片中利用ORB、SIFT、SURF等特征描述算法提取关键点图像特征生成特征描述后用蛮力算法进行比对两幅图像的相似度距离,最后返回距离最近的关键点。
相关函数:
创建匹配器->BFMatcher()及其参数解释
BFMatcher()创建一个BFMatcher对象,它有两个可选参数分别为:
- normType
normType这个参数用来指定要使用的距离测试类型。默认值为cv.NORM_L2,这个类型很适合SIFT和SURF等(cv.NORM_L1也可以)。对于使用二进制描述符的ORB,BRIEF,BRISK算法等,要使用cv.NORM_HAMMING,这样就会返回两个测试对象间的汉明距离。如果ORB算法的参数设置为MTA_K==3或4,normType就应该设置为cv.NORM_HAMMING2。
- corssCheck
布尔变量corssCheck,默认值为False。如果设置为True,匹配条件就会更加严格,只有到A中的第i个特征点与B中的第j个特征点距离最近,并且B中的第j个特征点到A中的第i个特征点也是最近(A中没有其他的点到j的距离最近)时才会返回最佳匹配(i, j)。也就是这两个特征点要互相匹配才行。这样就能提供统一的结果,这可以用来代替D.Lowe在SIFT文章中提出的比值测试方法。
BFMatcher对象的两个匹配方法->bf.match()和bf.knnMatch()
前者返回最佳匹配,后者为每个关键点返回k个匹配(降序排列之后取前K个),其中K是由用户设定的。
绘制匹配的点的函数drawMatches()和drawMatchsKnn()
前者会将两幅图像水平排列,然后在最近匹配的点之间绘制直线(从原图像到目标图像)。
后者事前面使用的匹配方法是bf.knnMatch()的话,就使用函数drawMatchsKnn()为每个关键点和它的K个最佳匹配点绘制匹配线。如果k等于2,就会为每个关键点绘制2条最佳匹配直线。
看一个SURF和ORB的例子 (不同匹配方式)
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img1 = cv.imread('img/box.png', 0)
img2 = cv.imread('img/box_in_scene.png', 0)
def orb_bf_demo():
#创建ORB对象
orb = cv.ORB_create()
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
#创建bf对象,并设定初始值
bf = cv.BFMatcher(cv.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)
#将匹配结果按特征点之间的距离进行降序排列
matches = sorted(matches, key= lambda x:x.distance)
#前10个匹配
img3 = cv.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flags=2)
cv.namedWindow('img',cv.WINDOW_AUTOSIZE)
cv.imshow('orb_img',img3)
def sift_bf_demo():
#创建SIFT对象
sift = cv.xfeatures2d.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
#使用默认参数 cv.Norm_L2 ,crossCheck=False
bf = cv.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2)
#比值测试,首先获取与A距离最近的点B(最近)和C(次近),只有当B/C小于阈值时(0.75)才被认为是匹配
#因为假设匹配是一一对应的,真正的匹配的理想距离是0
good = []
for m, n in matches:
if m.distance < 0.75*n.distance:
good.append([m])
img3 = cv.drawMatchesKnn(img1, kp1, img2, kp2, good, None, flags=2)
cv.namedWindow('img', cv.WINDOW_AUTOSIZE)
cv.imshow('sift_img', img3)
orb_bf_demo()
sift_bf_demo()
cv.waitKey(0)
cv.destroyAllWindows()
效果:
ORB的效果
SIFT的效果
FLANN基于最近邻的匹配器
定义和优点:
FLANN是快速最近邻搜索包(Fast_Library_for_Approximate_Nearest_Neighbors)的简称,它包含了一些为大数据集内搜索快速近邻和高维特征的优化算法。它在大数据集的时候比BFMatcher更快。
FLANN 属于单应性匹配,单应性指的是图像在投影发生了畸变后仍然能够有更高的检测和匹配准确率,它可以大概率上避免旋转和放缩带来的影响。
关于单应性匹配:
定义:
FLANN算法就属于单应性算法,先解释一下单应性,在计算机视觉中:平面的单应性被定义为从一个平面到另一个平面的投影映射。比如,一个二维平面上的点映射到摄像机成像仪上的映射就是平面单应性的例子。
我们针对一个图像,在其中找到一些 特征点(关键点),我们又在另一幅图像中也找到了一些特征点,最后对这两幅图像之间的特征点进行匹配。简单来说就是:我们在一张杂乱的图像中找到了 一个对象(的某些部分)的位置。这些信息足以帮助我们在目标图像中准确的 找到(查询图像)对象。
为了达到这个目的我们可以使用calib3d模块中的CV2.findHomography()函数。如果将这两幅图像中的特征点集传给这个函数,他就会找到这个对象的透视图变换。然后我们就可以使用函数 CV2.perspectiveTransform() 找到这个对象了,至少要 4 个正确的点才能找到这种变换。
我们已经知道在匹配过程可能会有一些错误,而这些错误会影响最终结果。为了解决这个问题,算法使用 最小二乘法、RANSAC 、 LMEDS及PROSAC(可以通过参数来设定)。所以好的匹配提供的正确的估计被称为 inliers,剩下的被称为 outliers。CV2.findHomography() 返回一个掩模,这个掩模确定了 inlier 和 outlier 点。
最小二乘法:
残差平方和最小
RANSAC:
通俗的来讲,普通最小二乘是保守派:在现有数据下,如何实现最优。是从一个整体误差最小的角度去考虑,尽量谁也不得罪。
RANSAC是改革派:首先假设数据具有某种特性(目的),为了达到目的,适当割舍一些现有的数据。
简化版思路说明一下求解过程(针对二维讲解):
- 第一步随机取点两个连线(模型拟合)
- 第二步设定容差,模型线附近自定义容错距离,统计点数
- 第三步重新随机取点重复一二步
- 最终找到点数最多的那个线,也就是最后要的模型
LMEDS
LMEDS又称最小中值平方法,LMedS的经典步骤是:
1. 随机采样
2. 计算模型参数
3. 计算相对模型的点集偏差err,并求出偏差中值Med(err)
4. 迭代2. 3.步直至获得符合阈值的最优解:Med(err)最小
5. 精确优化模型参数(LM算法迭代优化)
LMedS的函数接口 参照OpenCV来说主要需要2-3个参数(第三个不是必须的)
1. 置信度confidence:设置之后代表RANSAC采样n次过程中会出现(至少一次)采样点数据集中的点都为内点的概率,这个值设置的太大,会增加采样次数。太小,会使结果不太理想。一般取0.95-0.99
2. 最大采样迭代次数maxIters:为了防止一直在采样计
3.最大精细迭代次数refineIters:在采样之后,选取最优解。可以增加精确优化,比如使用LM算法获得更优解。
RANSAC的阈值在具有物理意义或者几何意义的时候比较容易确定,但是当阈值不具有这些特征的时候,就成了一个不太好调整的参数了。这时LMedS可以自适应迭代获得最优解。
PROSAC
渐进一致采样法(PROSAC) 是对经典的 RANSAC中采样的一种优化。相比经典的 RANSAC 方法均匀地从整个集合中采样,PROSAC 方法是从不断增大的最佳对应点集合中进行采样的。所以这种方法可以节省计算量,提高运行速度
匹配器生成方法FlannBasedMatcher()及两个入参参数解释
对于基于FLANN的匹配器生成函数FlannBasedMatcher(),我们需要传两个参数。
第一个指定要用的最近邻算法
可选的三种算法:随机K-D树算法、优先搜索k-means树、层次聚类树
基于SIFT、SURF特征描述算法的的这样准备第一个参数:
index_params=dict(algorithm=FLANN_INDEX_KDTREE,trees=5)
基于ORB的可以这样准备第一个参数:
index_params = dict(algorithm=FLANN_INDEX_LSH, table_number = 6, key_size = 12, multi_probe_level = 1)
第二个指定了索引里的树应该被递归遍历的次数(更高的值带来更高的准确率。
但是也花更多时间,如果你想改变值)。
search_params = dict(checks=100).
整合一下:
import numpy as np
import cv2
from matplotlib import pyplot as plt
img1 = cv2.imread('box.png',0) # queryImage
img2 = cv2.imread('box_in_scene.png',0) # trainImage
# Initiate SIFT detector
sift = cv2.SIFT()
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)
# FLANN parameters
FLANN_INDEX_KDTREE = 0
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 maskmatches
Mask = [[0,0] for i in xrange(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,),
plt.show()
【如果对您有帮助,交个朋友给个一键三连吧,您的肯定是我博客高质量维护的动力!!!】