当前位置: 首页 > news >正文

ORB-SLAM3 SearchByBoW 2

以下是ORB-SLAM3中ORBmatcher::SearchByBoW函数的完整代码及详细解读。


📄 完整代码

cpp

int ORBmatcher::SearchByBoW(KeyFrame* pKF, Frame &F, vector<MapPoint*> &vpMapPointMatches) { // 1. 获取关键帧的地图点 const vector<MapPoint*> vpMapPointsKF = pKF->GetMapPointMatches(); // 2. 初始化当前帧的匹配结果容器(全部置空) vpMapPointMatches = vector<MapPoint*>(F.N, static_cast<MapPoint*>(NULL)); // 3. 获取关键帧的特征向量(FeatureVector) const DBoW2::FeatureVector &vFeatVecKF = pKF->mFeatVec; int nmatches = 0; // 4. 初始化方向直方图(用于旋转一致性检验) vector<int> rotHist[HISTO_LENGTH]; for(int i=0; i<HISTO_LENGTH; i++) rotHist[i].reserve(500); // 5. 角度到直方图bin的转换因子 const float factor = HISTO_LENGTH / 360.0f; // 6. 同步遍历两个特征向量(只比较相同节点ID下的特征点) DBoW2::FeatureVector::const_iterator KFit = vFeatVecKF.begin(); DBoW2::FeatureVector::const_iterator Fit = F.mFeatVec.begin(); DBoW2::FeatureVector::const_iterator KFend = vFeatVecKF.end(); DBoW2::FeatureVector::const_iterator Fend = F.mFeatVec.end(); while(KFit != KFend && Fit != Fend) { // 6.1 只处理属于同一节点的特征点 if(KFit->first == Fit->first) { // 获取该节点下两帧的特征点索引列表 const vector<unsigned int> vIndicesKF = KFit->second; const vector<unsigned int> vIndicesF = Fit->second; // 6.2 遍历关键帧中该节点的所有特征点 for(size_t iKF=0; iKF<vIndicesKF.size(); iKF++) { const unsigned int realIdxKF = vIndicesKF[iKF]; // 获取该特征点对应的地图点 MapPoint* pMP = vpMapPointsKF[realIdxKF]; if(!pMP || pMP->isBad()) continue; // 获取关键帧中该特征点的描述子 const cv::Mat &dKF = pKF->mDescriptors.row(realIdxKF); int bestDist1 = 256; // 最佳距离(最小汉明距离) int bestIdxF = -1; // 最佳匹配点在当前帧中的索引 int bestDist2 = 256; // 次佳距离 // 6.3 遍历当前帧中该节点的所有特征点,寻找最佳匹配 for(size_t iF=0; iF<vIndicesF.size(); iF++) { const unsigned int realIdxF = vIndicesF[iF]; // 如果该点已经被匹配,跳过 // 当前帧中的这个ORB特征点已经有匹配地图点了MapPoint if(vpMapPointMatches[realIdxF]) continue; const cv::Mat &dF = F.mDescriptors.row(realIdxF); const int dist = DescriptorDistance(dKF, dF); // 更新最佳距离和次佳距离 if(dist < bestDist1) { bestDist2 = bestDist1; bestDist1 = dist; bestIdxF = realIdxF; } else if(dist < bestDist2) { bestDist2 = dist; } } // 6.4 双重检验:距离阈值 + 最近邻比率 if(bestDist1 <= TH_LOW) // 阈值检验 { if(static_cast<float>(bestDist1) < mfNNratio * static_cast<float>(bestDist2)) // 比率检验 { // 接受匹配:当前帧的特征点 → 关键帧的地图点 vpMapPointMatches[bestIdxF] = pMP; nmatches++; // 6.5 计算旋转角度差,存入直方图 if(mbCheckOrientation) { const cv::KeyPoint &kp = pKF->mvKeysUn[realIdxKF]; float rot = kp.angle - F.mvKeys[bestIdxF].angle; if(rot < 0.0) rot += 360.0f; int bin = round(rot * factor); if(bin == HISTO_LENGTH) bin = 0; assert(bin >= 0 && bin < HISTO_LENGTH); rotHist[bin].push_back(bestIdxF); } } } } KFit++; Fit++; } else if(KFit->first < Fit->first) { // 对齐迭代器 KFit = vFeatVecKF.lower_bound(Fit->first); } else { Fit = F.mFeatVec.lower_bound(KFit->first); } } // 7. 旋转一致性检验:剔除不符合主流旋转方向的匹配 if(mbCheckOrientation) { int ind1=-1, ind2=-1, ind3=-1; ComputeThreeMaxima(rotHist, HISTO_LENGTH, ind1, ind2, ind3); for(int i=0; i<HISTO_LENGTH; i++) { // 只保留数量最多的三个bin中的匹配 if(i == ind1 || i == ind2 || i == ind3) continue; // 剔除其他bin中的匹配 for(size_t j=0, jend=rotHist[i].size(); j<jend; j++) { vpMapPointMatches[rotHist[i][j]] = static_cast<MapPoint*>(NULL); nmatches--; } } } return nmatches; }

📖 详细解读

🎯 函数作用

SearchByBoW利用词袋模型(Bag-of-Words)加速两帧(关键帧与普通帧,或两个关键帧)之间的特征匹配。它只比较属于同一词汇树节点的特征点,避免了全局暴力搜索,大幅提升匹配效率。该函数广泛应用于跟踪参考关键帧重定位闭环检测等场景。

📥 输入参数

参数类型说明
pKFKeyFrame*参考关键帧,提供地图点
FFrame&当前帧,待匹配的特征点
vpMapPointMatchesvector<MapPoint*>&输出:当前帧每个特征点匹配到的地图点

📤 返回值

返回成功匹配的数量(int)。

⚙️ 核心原理:五步匹配流程

步骤1:数据准备

cpp

const vector<MapPoint*> vpMapPointsKF = pKF->GetMapPointMatches(); vpMapPointMatches = vector<MapPoint*>(F.N, static_cast<MapPoint*>(NULL)); const DBoW2::FeatureVector &vFeatVecKF = pKF->mFeatVec;
  • 获取关键帧中每个特征点对应的地图点

  • 初始化当前帧的匹配结果(全部置空)

  • 获取关键帧的特征向量(mFeatVec—— 一个按节点ID索引特征点索引列表的映射表

步骤2:同步遍历 FeatureVector

cpp

DBoW2::FeatureVector::const_iterator KFit = vFeatVecKF.begin(); DBoW2::FeatureVector::const_iterator Fit = F.mFeatVec.begin(); while(KFit != KFend && Fit != Fend) { if(KFit->first == Fit->first) { /* 匹配 */ } else if(KFit->first < Fit->first) { KFit = vFeatVecKF.lower_bound(Fit->first); } else { Fit = F.mFeatVec.lower_bound(KFit->first); } }

FeatureVector的本质是std::map<NodeId, vector<unsigned int>>

  • Key:词汇树节点ID(NodeId)

  • Value:属于该节点的特征点索引列表

同步遍历确保只比较两帧中属于同一节点的特征点。这利用了词袋模型的核心思想:属于同一节点的特征点在视觉上相似,最可能产生正确匹配。

步骤3:节点内暴力匹配

对于关键帧中一个有效特征点(有对应地图点),在当前帧同一节点的所有特征点中计算汉明距离,找出最佳匹配(最小距离)次佳匹配(第二小距离)

cpp

int bestDist1 = 256; // 最佳距离 int bestIdxF = -1; int bestDist2 = 256; // 次佳距离 for(size_t iF=0; iF<vIndicesF.size(); iF++) { const int dist = DescriptorDistance(dKF, dF); // 更新 bestDist1 和 bestDist2 }
步骤4:双重检验接受匹配

一个候选匹配必须同时满足两个条件:

检验条件作用
距离阈值bestDist1 <= TH_LOW确保描述子足够相似
最近邻比率bestDist1 < mfNNratio * bestDist2确保匹配是"独特"的,没有模糊候选

mfNNratio默认值为0.6。比率检验能有效剔除因重复纹理导致的误匹配。

步骤5:方向直方图验证

cpp

float rot = kp.angle - F.mvKeys[bestIdxF].angle; if(rot < 0.0) rot += 360.0f; int bin = round(rot * factor); rotHist[bin].push_back(bestIdxF);

将所有匹配点的主方向角度差统计到30个bin的直方图中。匹配完成后,只保留直方图中数量最多的三个bin中的匹配,剔除其余匹配。

这一步基于的物理约束是:两帧图像之间的旋转是全局一致的。如果某个匹配的角度差与主流方向不一致,它很可能是误匹配。

🔧 关键成员变量

变量默认值说明
mfNNratio0.6最近邻比率阈值
mbCheckOrientationtrue是否进行旋转一致性检验
TH_LOW50描述子距离低阈值
HISTO_LENGTH30方向直方图的bin数量

🎯 应用场景

场景调用位置用途
跟踪参考关键帧Tracking::TrackReferenceKeyFrame()恒速模型失效时,匹配当前帧与参考关键帧
重定位Tracking::Relocalization()跟踪丢失后,匹配当前帧与候选关键帧
闭环检测LoopClosing::DetectLoop()匹配当前关键帧与闭环候选关键帧

💎 总结

SearchByBoW通过FeatureVector的同步遍历将匹配范围从"全局所有特征点"缩小到"同一节点内的特征点",再通过距离阈值 + 比率检验 + 方向一致性三重过滤保证匹配质量。它无需位姿信息即可完成匹配,是ORB-SLAM3实现实时跟踪与闭环检测的关键基础函数。

http://www.jsqmd.com/news/1139641/

相关文章:

  • 写在开头:我为什么开这个专栏?
  • 今天偶然发现有一个评论有500个赞
  • 宝可梦实景图数据集在无人机导航中的计算机视觉应用
  • 终极指南:大麦网双端自动抢票技术方案与实战部署
  • TwinCAT3 3.1 配置汇川 SV660N:EtherCAT 扫描与 8388608 分辨率设置详解
  • 摩托车混流装配防错引导系统硬件怎么选?海雅达 Model 10X 工业三防平板电脑采购预算与部署成本评估
  • 成对评估上包络线:有界指标的稳健效果边界计算方法
  • Metasploit进阶:从set payload到generate命令的Shellcode生成与免杀实战
  • Linux防火墙极简配置指南:10分钟掌握firewalld核心安全策略
  • 常见硬件设计问题分析及解决方案-EMC静电问题
  • Seedance 2.5本地AI生图与视频生成:部署指南与参数调优
  • F28377D Modbus RTU移植:硬件时序、寄存器映射与实时性硬核实践
  • 从SSTI模板注入到docker容器内网渗透:靶机练习之Tempus_fugit3
  • Windows系统下Xray被动扫描环境配置全攻略:从证书安装到代理设置
  • 狼人杀不只是游戏:9 个大模型的多轮博弈能力观察
  • 2026年东莞良心4s店哪家服务好:最新权威排名与专业指南。
  • 洛雪音乐音源完全指南:如何免费获取全网无损音乐资源
  • 如何快速掌握res-downloader:全网视频音频资源下载的终极指南
  • Snipe-IT新手部署全路径指南:XAMPP/Ubuntu/Docker三路实操
  • 基于行车记录仪与AI的道路病害自动检测技术实践
  • 从“人工智障”到“智能管家”:手把手教你重构一个R语言AI智能体
  • WindowResizer:Windows窗口调整终极解决方案,告别无法调整大小的顽固窗口
  • PCB孔金属化工艺:从化学镀铜到直接电镀的2种方案与15μm厚度控制
  • Fortify路径遍历漏洞深度解析:从输入验证到数据流防护的完整方案
  • 3个月成为专业前端开发者:freeCodeCamp前端开发库认证完全指南
  • edge浏览器图片助手(ImageAssistant) 批量图片下载器扩展一款用于嗅探、分析网页图片并提供批量下载等功能的浏览器扩展程序
  • (十五)「JVS-Rules规则引擎 V2.5」— 赋值节点
  • Ideogram4开源图像生成模型:从环境搭建到区域编辑的完整实践指南
  • 3步掌握WebODM:免费开源的无人机图像处理终极指南
  • 剪流 GEO 完整操作流程拆解:资深运营手把手教你从 0 到 1 做优化