以下是ORB-SLAM3中ORBmatcher::SearchByBoW函数的完整代码及详细解读。 完整代码cppint ORBmatcher::SearchByBoW(KeyFrame* pKF, Frame F, vectorMapPoint* vpMapPointMatches) { // 1. 获取关键帧的地图点 const vectorMapPoint* vpMapPointsKF pKF-GetMapPointMatches(); // 2. 初始化当前帧的匹配结果容器全部置空 vpMapPointMatches vectorMapPoint*(F.N, static_castMapPoint*(NULL)); // 3. 获取关键帧的特征向量FeatureVector const DBoW2::FeatureVector vFeatVecKF pKF-mFeatVec; int nmatches 0; // 4. 初始化方向直方图用于旋转一致性检验 vectorint rotHist[HISTO_LENGTH]; for(int i0; iHISTO_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 vectorunsigned int vIndicesKF KFit-second; const vectorunsigned int vIndicesF Fit-second; // 6.2 遍历关键帧中该节点的所有特征点 for(size_t iKF0; iKFvIndicesKF.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 iF0; iFvIndicesF.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_castfloat(bestDist1) mfNNratio * static_castfloat(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 i0; iHISTO_LENGTH; i) { // 只保留数量最多的三个bin中的匹配 if(i ind1 || i ind2 || i ind3) continue; // 剔除其他bin中的匹配 for(size_t j0, jendrotHist[i].size(); jjend; j) { vpMapPointMatches[rotHist[i][j]] static_castMapPoint*(NULL); nmatches--; } } } return nmatches; } 详细解读 函数作用SearchByBoW利用词袋模型Bag-of-Words加速两帧关键帧与普通帧或两个关键帧之间的特征匹配。它只比较属于同一词汇树节点的特征点避免了全局暴力搜索大幅提升匹配效率。该函数广泛应用于跟踪参考关键帧、重定位和闭环检测等场景。 输入参数参数类型说明pKFKeyFrame*参考关键帧提供地图点FFrame当前帧待匹配的特征点vpMapPointMatchesvectorMapPoint*输出当前帧每个特征点匹配到的地图点 返回值返回成功匹配的数量int。⚙️ 核心原理五步匹配流程步骤1数据准备cppconst vectorMapPoint* vpMapPointsKF pKF-GetMapPointMatches(); vpMapPointMatches vectorMapPoint*(F.N, static_castMapPoint*(NULL)); const DBoW2::FeatureVector vFeatVecKF pKF-mFeatVec;获取关键帧中每个特征点对应的地图点初始化当前帧的匹配结果全部置空获取关键帧的特征向量mFeatVec—— 一个按节点ID索引特征点索引列表的映射表步骤2同步遍历 FeatureVectorcppDBoW2::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::mapNodeId, vectorunsigned intKey词汇树节点IDNodeIdValue属于该节点的特征点索引列表同步遍历确保只比较两帧中属于同一节点的特征点。这利用了词袋模型的核心思想属于同一节点的特征点在视觉上相似最可能产生正确匹配。步骤3节点内暴力匹配对于关键帧中一个有效特征点有对应地图点在当前帧同一节点的所有特征点中计算汉明距离找出最佳匹配最小距离和次佳匹配第二小距离。cppint bestDist1 256; // 最佳距离 int bestIdxF -1; int bestDist2 256; // 次佳距离 for(size_t iF0; iFvIndicesF.size(); iF) { const int dist DescriptorDistance(dKF, dF); // 更新 bestDist1 和 bestDist2 }步骤4双重检验接受匹配一个候选匹配必须同时满足两个条件检验条件作用距离阈值bestDist1 TH_LOW确保描述子足够相似最近邻比率bestDist1 mfNNratio * bestDist2确保匹配是独特的没有模糊候选mfNNratio默认值为0.6。比率检验能有效剔除因重复纹理导致的误匹配。步骤5方向直方图验证cppfloat 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实现实时跟踪与闭环检测的关键基础函数。