ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

第240篇 视觉SLAM前端之特征匹配——BF/FLANN和误匹配剔除

第240篇 视觉SLAM前端之特征匹配——BF/FLANN和误匹配剔除 上一篇讲了特征提取这篇讲匹配——提取出特征之后怎么在不同图像之间找到对应的点以及怎么把错误的匹配剔除掉。特征匹配是视觉SLAM前端的核心环节。匹配对了后面的位姿估计就准匹配错了位姿估计就会跑偏误差会一步步累积。而且匹配环节产生的误匹配如果不剔除干净会严重影响系统的鲁棒性。面试的时候特征匹配的细节经常被追问你得把BF、FLANN的原理和误匹配剔除的策略讲清楚。暴力匹配BF暴力匹配的思路最直白对于图像A里的每个特征点遍历图像B里的所有特征点找描述子距离最近的那个。描述子的距离怎么算取决于描述子的类型。ORB用的是二进制描述子距离用汉明距离两个二进制串做XOR数有多少位不同。SIFT用的是浮点描述子距离用欧氏距离。import cv2 # 暴力匹配汉明距离适用于ORB bf cv2.BFMatcher(cv2.NORM_HAMMING, crossCheckTrue) matches bf.match(desc1, desc2) # matches: 每个desc1中的描述子对应desc2中最近的匹配暴力匹配的复杂度是O(N×M)N和M分别是两幅图像的特征点数。特征点多的时候会很慢比如两幅图各有2000个特征点就需要做400万次距离计算。但暴力匹配有个好处它一定能找到全局最优的匹配不存在近似误差。crossCheck参数很关键是减少误匹配的第一道防线。设为True的时候A匹配到B的最近点和B匹配到A的最近点必须是同一个点否则这个匹配就被丢弃。这个交叉验证能大幅减少误匹配。FLANN匹配FLANNFast Library for Approximate Nearest Neighbors不是单一算法而是一个库。它提供多种近似最近邻搜索的算法最常用的是随机kd树Randomized KD-Tree。FLANN的思路是不找精确的最近邻而是找一个足够近的近似最近邻。通过牺牲一点精度通常不到1%的损失换取大幅的速度提升在特征点多的时候效果特别好。# FLANN匹配适用于SIFT等浮点描述子 index_params dict(algorithm1, trees4) # 1FLANN_INDEX_KDTREE search_params dict(checks50) flann cv2.FlannBasedMatcher(index_params, search_params) matches flann.knnMatch(desc1, desc2, k2)对于二进制描述子如ORBFLANN需要用特殊的距离类型。OpenCV提供了对应的配置。FLANN在特征点数量大的时候优势明显。几千个特征点的暴力匹配可能要几十毫秒FLANN只需要几毫秒。但特征点少的时候几百个暴力匹配反而更快因为FLANN有建索引的开销。Lowes Ratio Test误匹配剔除的利器匹配完之后最关键的一步是剔除误匹配。Lowe提出的Ratio Test是经典方法。思路是这样的对于A中的每个特征点在B中找最近和次近的两个匹配。如果最近的距离远小于次近的距离比值小于某个阈值通常0.75说明这个匹配是可靠的。如果两个距离差不多说明这个特征点在B中有多个相似的候选匹配不确定应该丢弃。# Lowes Ratio Test good_matches [] for m, n in matches: if m.distance 0.75 * n.distance: good_matches.append(m)Ratio Test的原理是正确的匹配最近邻的距离会明显小于次近邻而错误的匹配最近邻和次近邻的距离往往差不多。这个简单的测试能剔除掉90%以上的误匹配。几何验证用对极几何过滤Ratio Test之后通常还要做几何验证。因为描述子匹配只看外观相似度不考虑空间关系。两个在图像中距离很远的点描述子可能很相似但从几何上看不可能同时匹配。几何验证的思路是用对极约束本质矩阵或基础矩阵来检查匹配点是否满足几何关系。不满足的点就是误匹配。# 用RANSAC做几何验证 pts1 np.float32([kpts1[m.queryIdx].pt for m in good]) pts2 np.float32([kpts2[m.trainIdx].pt for m in good]) E, mask cv2.findEssentialMat(pts1, pts2, methodcv2.RANSAC) # mask: 1内点通过几何验证0外点误匹配RANSAC会反复采样计算本质矩阵然后用内点数量来评估模型质量。最终保留内点对应的匹配剔除所有外点。匹配策略的工程选择实际做SLAM的时候匹配策略要根据场景来选。相邻帧匹配跟踪模式前后两帧的位姿变化不大特征点移动范围有限。这时候可以用光流法做跟踪不需要做全图匹配。光流法的速度快而且天然避免了大部分误匹配。ORB-SLAM的跟踪线程用的就是这个思路。回环检测匹配需要和所有历史关键帧做匹配特征点数量大。这时候FLANN是更好的选择暴力匹配太慢。同时Ratio Test和几何验证都不能少。重定位匹配机器人在已知地图中重新定位需要和地图中的所有路标点匹配。这时候匹配量大误匹配也多需要更严格的验证策略。光流跟踪匹配的另一种思路其实相邻帧之间的特征对应关系不一定要通过描述子匹配来找。光流跟踪是另一种方案。光流法的核心假设是相邻帧之间同一个特征点的像素亮度不变。基于这个假设可以通过迭代的方式精确地找到特征点在下一帧中的位置。OpenCV里的LK光流法Lucas-Kanade就是这个思路。# LK光流跟踪 next_pts, status, err cv2.calcOpticalFlowPyrLK( prev_gray, curr_gray, prev_pts, None) # status: 1跟踪成功0跟踪失败光流法的优势是速度快不需要提取描述子和做匹配而且匹配精度高亚像素级别。缺点是只能跟踪相邻帧不能跳帧匹配。而且如果运动太大帧间位移超过几个像素光流会跟踪失败。ORB-SLAM的跟踪线程用的就是光流法来维持特征追踪。只有当跟踪失败比如快速转动导致大量特征丢失的时候才回退到描述子匹配做重定位。面试中怎么聊特征匹配面试官问怎么剔除误匹配你要说出两层过滤。第一层是描述子层面的Ratio Test第二层是几何层面的RANSAC验证。两层过滤之后匹配质量通常就可靠了。面试官问BF和FLANN怎么选关键看特征点数量。几百个点用BF几千个点用FLANN。SLAM的回环检测模块通常用FLANN因为要匹配的特征点很多。相邻帧跟踪用光流法根本不走匹配流程。Ratio Test的阈值怎么定 0.75是Lowe推荐的默认值适用于大多数场景。阈值越小剔除越严格保留的匹配越少但越可靠。在纹理重复的场景比如看一排排窗户可能需要把阈值调小到0.6甚至0.5。面试官还可能问RANSAC的原理是什么 RANSAC的核心思想是随机采样假设验证。每次随机取最小样本集本质矩阵需要5对点计算模型然后数有多少内点。反复迭代取内点最多的那次作为最终结果。迭代次数取决于你想要的置信度和预期的外点比例。匹配点太少怎么办 可能是场景纹理不够或者帧间运动太大。解决思路降低特征提取的阈值以提取更多特征点用金字塔光流来应对大运动或者降低帧率让相邻帧之间的位移变小。特征匹配的质量直接决定了视觉SLAM前端的可靠性。好的匹配策略能把误匹配率控制在1%以下差的策略可能10%的匹配都是错的系统直接跑飞。上一篇第239篇 视觉SLAM前端之特征提取——ORB/SIFT/SuperPoint对比下一篇我们聊对极几何——本质矩阵和基础矩阵这是从匹配点中恢复相机运动的数学基础。理解了这两个矩阵你就能明白SLAM是怎么从二维图像中推算出三维运动的。如果这篇文章对你有帮助欢迎点赞支持一下你的鼓励是我持续更新的动力
返回列表