cv2-特征点匹配(bf、FLANN)

news/2024/11/27 4:53:26/

cv2-特征点匹配(bf、KNN、FLANN)

文章目录

  • cv2-特征点匹配(bf、KNN、FLANN)
    • 1. 暴力匹配法(bf)
      • 1.1 bf.match()
      • 1.2 bf.knnMatch()
    • 3. FLANN匹配法
    • 4. 总结

1. 暴力匹配法(bf)

(又称作:交叉匹配)交叉过滤的思想很简单,再进行一次匹配,反过来使用被匹配到的点进行匹配,如果匹配到的仍然是第一次匹配的点的话,就认为这是一个正确的匹配。

eg:假如第一次特征点A使用暴力匹配的方法,匹配到的特征点是特征点B;反过来,使用特征点B进行匹配,如果匹配到的仍然是特征点A,则就认为这是一个正确的匹配,否则就是一个错误的匹配。

cv2.BFMatcher() 创建一个 BFMatcher 对象。它有两个可选参数。

  • 第一个是normType。它是用来指定要使用的距离测试类型。默认值为cv2.Norm_L2。这很适合SIFT和SURF等(c2.NORM_L1 也可以)。对于使用二进制描述符的 ORB,BRIEF,BRISK算法等,要使用 cv2.NORM_HAMMING,这样就会返回两个测试对象之间的汉明距离。如果 ORB 算法的参数设置为 V T A_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,就会为每个关键点绘制两条最佳匹配直线。如果我们要选择性绘制话就要给函数传入一个掩模。

注意,

  • cv2.drawMatches()使用的点对集keypoint是一维的,(N,);
    matchesMask是计算转换H矩阵时生成的掩模,inliers的点,最后只画出符合条件的inliers点对。
  • cv2.drawMatchesKnn()使用的点对集keypoint是一维的,(N,1);
    画出keypoin中前几个点对连线。

1.1 bf.match()

matches = bf.match(des1, des2) 返回值是一个 DMatch 对象列表。这个DMatch 对象具有下列属性:

  • DMatch.distance:描述符之间的距离。越小越好。
  • DMatch.trainIdx : 目标图像中描述符的索引。
  • DMatch.queryIdx :查询图像中描述符的索引。
  • DMatch.imgIdx :目标图像的索引。
detector = cv2.ORB_create()  # Oriented FAST and Rotated BRIEFkp1 = detector.detect(img1, None)
kp2 = detector.detect(img2, None)
kp1, des1 = detector.compute(img1, kp1) # keypoint 是关键点的列表,desc 检测到的特征的局部图的列表
kp2, des2 = detector.compute(img2, kp2)
# 获得一个暴力匹配器的对象
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)# 利用匹配器 匹配两个描述符的相近程度
'''1. 暴力法'''
matches = bf.match(des1, des2)
# 按照相近程度 进行排序
matches = sorted(matches, key=lambda x: x.distance)
# 画出匹配项
img31 = cv2.drawMatches(img1, kp1, img2, kp2, matches[: 50], img2, flags=2)"""2. knn法"""
matches = bf.knnMatch(des1, des2, k=1)  # knn 匹配可以返回k个最佳的匹配项、bf返回所有的匹配项
img32 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, matches[: 50], img2, flags=2)
  • 优化:对 matches 匹配点进行排序,提取匹配效果最优的50个点。过滤掉相似性低的特征点。

1.2 bf.knnMatch()

返回对象,同bf.Match()。

K近邻匹配,在匹配的时候选择K个和特征点最相似的点,如果这K个点之间的区别足够大,则选择最相似的那个点作为匹配点,通常选择K = 2,也就是最近邻匹配。对每个匹配返回两个最近邻的匹配,如果第一匹配和第二匹配距离比率足够大(向量距离足够远),则认为这是一个正确的匹配,比率的阈值通常在2左右。

knn匹配过程中很可能发生错误的匹配,错误的匹配主要有两种:

  • 匹配的特征点事错误的
  • 图像上的特征点无法匹配。

KNNMatch():暴力法的基础上添加比率测试。

  • 1)交叉过滤 (暴力法)
    如果第一幅图像的一个特征点和第二幅图像的一个特征点相匹配,则进行一个相反的检查,即将第二幅图像上的特征点与第一幅图像上相应特征点进行匹配,如果匹配成功,则认为这对匹配是正确的。
     
    OpenCV中的BFMatcher已经包含了这种过滤 BFMatcher matcher(NORM_L2,true),在构造BFMatcher是将第二个参数设置为true。

  • 2)比率测试 (knn)
    KNNMatch,可设置K = 2 ,即对每个匹配返回两个最近邻描述符,仅当第一个匹配与第二个匹配之间的距离足够小时,才认为这是一个匹配。

3. FLANN匹配法

FLANN是快速最近邻搜索算法寻找的简称(用快速的第三方库近似最近邻搜索算法)。它包含一组算法,这些算法针对大型数据集中的快速最近邻搜索和高维特征进行了优化。对于大型数据集,它的运行速度比BFMatcher快。

使用 FLANN 匹配,我们需要传入两个字典作为参数。这两个用来确定要使用的算法和其他相关参数等。

  • 第一个是 IndexParams。各种不同算法的信息可以在 FLANN 文档中找到。这里我们总结一下,对于 SIFT 和 SURF 等,我们可以传入的参数是:
    FLANN_INDEX_KDTREE = 1index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)。

  • 第二个字典是 SearchParams。用它来指定递归遍历的次数。值越高结果越准确,但是消耗的时间也越多。如果你想修改这个值,传入参数:searchparams = dict(checks = 100)。

# 创建sift检测器
sift = cv2.xfeatures2d.SIFT_create()
# 查找监测点和匹配符
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
"""
keypoint是检测到的特征点的列表
descriptor是检测到特征的局部图像的列表
"""# 获取flann匹配器
FLANN_INDEX_KDTREE = 0
# 参数1:indexParams
#    对于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)。
indexParams = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)# 参数2:searchParams 指定递归遍历的次数,值越高结果越准确,但是消耗的时间也越多。
searchParams = dict(checks=50)# 使用FlannBasedMatcher 寻找最近邻近似匹配
flann = cv2.FlannBasedMatcher(indexParams, searchParams)
# 使用knnMatch匹配处理,并返回匹配matches
matches = flann.knnMatch(des1, des2, k=2)
# 通过掩码方式计算有用的点
matchesMask = [[0, 0] for i in range(len(matches))]# 通过描述符的距离进行选择需要的点
for i, (m, n) in enumerate(matches):if m.distance < 0.7*n.distance: # 通过0.7系数来决定匹配的有效关键点数量matchesMask[i] = [1, 0]drawPrams = dict(matchColor=(0, 255, 0),singlePointColor=(255, 0, 0),matchesMask=matchesMask,flags=0)
# 匹配结果图片
img33 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, matches, None, **drawPrams)

4. 总结

特征匹配:FLANN效果更好一些。


http://www.ppmy.cn/news/23980.html

相关文章

【C++】C++11 ~ 包装器解析

&#x1f308;欢迎来到C专栏~~包装器解析 (꒪ꇴ꒪(꒪ꇴ꒪ )&#x1f423;,我是Scort目前状态&#xff1a;大三非科班啃C中&#x1f30d;博客主页&#xff1a;张小姐的猫~江湖背景快上车&#x1f698;&#xff0c;握好方向盘跟我有一起打天下嘞&#xff01;送给自己的一句鸡汤&a…

datetime日期和时间管理

datetime日期和时间管理 文章目录datetime日期和时间管理1.概述2.时间time2.1.time类基本使用1.获取时间2.获取一天中合法时间范围3.time微妙是整数3.日期date3.1.date基本操作1.查看date获取日历信息2.时间戳转为日期格式3.日期合法范围4.replace创建日期5.date属性和方法4.时…

MES系统智能工厂,搭上中国制造2025顺风车

MES在电子制造业中的应用日益广泛&#xff0c;越来越多的厂商已经购置或自行开发了MES&#xff0c;并将其作为“智能化工厂”。国内大大小小、各行各业都有上百个MES系统&#xff0c;还有很多的国外MES系统&#xff0c;怎么才能在MES系统公司中找到适合自己的MES&#xff1f;希…

单片机开发---ESP32S3移植NES模拟器(二)

书接上文 《单片机开发—ESP32-S3模块上手》 《单片机开发—ESP32S3移植lvgl触摸屏》 《单片机开发—ESP32S3移植NES模拟器&#xff08;一&#xff09;》 暖场视频&#xff0c;小时候称这个为—超级曲线射门&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&am…

单链表--C语言版(从0开始,超详细解析,小白一看就会)

目录 一、前言 &#x1f34e; 为什么要学习链表 &#x1f4a6;顺序表有缺陷 &#x1f4a6; 优化方案&#xff1a;链表 二、链表详解 &#x1f350;链表的概念 &#x1f349;链表的结构组成&#xff1a;节点 &#x1f353;链表节点的连接&#xff08;逻辑结构与物理结构的区…

HashMap设计思想学习

HashMap设计思想学习引言树化与退化红黑树的优势索引计算put流程扩容&#xff08;加载&#xff09;因子为何默认是 0.75fhashMap并发丢失数据问题jdk 1.7并发死链问题key 的设计引言 hashmap在jdk 1.7之前是数组链表结构&#xff0c;而jdk1.8之后变为是数组(链表|红黑树) 树化…

【直击招聘C++】2.5 this指针

2.5 this指针一、要点归纳1.什么是this指针2.this指针的深入讨论程序1程序23.类成员函数返回对象和返回对象引用的区别二、面试真题解析面试题1面试题2一、要点归纳 1.什么是this指针 this指针是隐含于每一个类对象的特殊指针&#xff0c;该指针值是一个正在被某个成员函数操作…

C++类和对象(下)

✨个人主页&#xff1a; Yohifo &#x1f389;所属专栏&#xff1a; C修行之路 &#x1f38a;每篇一句&#xff1a; 图片来源 I do not believe in taking the right decision. I take a decision and make it right. 我不相信什么正确的决定。我都是先做决定&#xff0c;然后把…