ORB-SLAM3 关键帧相似度计算
float si mpVoc-score(F-mBowVec, pKFi-mBowVec);这行代码的核心作用是计算当前帧与某个候选关键帧在视觉上的相似度得分得分越高两者在视觉上就越相似。这个得分是后续筛选和聚合候选关键帧的基础也是ORB-SLAM3重定位流程中“粗匹配”的关键一环。下面将从得分计算方式和算法实现两个层面来详细解释 得分计算方式基于词袋向量的相似度mpVoc-score是DBoW2库提供的接口其输入是两个词袋向量BowVector输出是一个浮点数代表它们的相似度。输入是什么F-mBowVec是当前帧的词袋向量pKFi-mBowVec是候选关键帧的词袋向量。每个向量都是一个std::mapWordId, WordValue记录了该帧图像包含的“视觉单词”及其对应的TF-IDF权重。如何计算相似度DBoW2库提供了多种评分方法例如L1范数、L2范数、卡方距离、点积等。ORB-SLAM3通常使用L1范数评分L1Scoring其本质是计算两个词袋向量的加权曼哈顿距离。得分越高代表两帧共享的视觉单词越多、权重越大它们在视觉内容上就越相似。⚙️ 算法实现为什么这一步是关键理解了得分计算方式后我们再把这个步骤放回DetectRelocalizationCandidates函数的上下文中就能更清楚地看到它的重要性从“粗筛”到“精筛”在这行代码之前系统通过倒排索引mvInvertedFile快速找出了所有与当前帧有共视单词的关键帧这是第一步的“粗筛”。计算精确得分score()函数就是第二步的“精筛”。它基于TF-IDF权重为每一个通过粗筛的关键帧计算一个精确的相似度得分。TF-IDF确保了那些罕见但更具区分度的视觉单词高区分度对得分的贡献更大而普遍出现的单词如天空、墙壁的贡献被削弱。指导后续筛选这个得分会被用于后续的筛选逻辑。例如代码中常见的策略是只保留得分大于最高分75%的候选帧然后将这些高得分帧的得分累加到它们共视图中的邻居帧上形成“累积得分”从而找到视觉上最相似的“区域”而非单个孤立的帧。 总结mpVoc-score(...)这行代码本质上是利用DBoW2库提供的相似度计算接口将两个词袋向量转换为一个量化的视觉相似度得分。这个得分是重定位候选帧筛选流程的核心依据它让系统能够快速地从成百上千个候选帧中精确定位到与当前帧视觉上最相似的几个关键帧。补充mpVoc-score(F-mBowVec, pKFi-mBowVec)这行代码其核心是调用DBoW2库来计算两个词袋向量的相似度得分。在ORB-SLAM3中默认使用的是L1范数评分L1 Scoring方法。下面将从代码结构、计算原理和具体例子三个方面来拆解这个过程。 1. 代码结构DBoW2的评分家族在DBoW2库中score是一个定义在基类GeneralScoring中的纯虚函数。它通过一个宏__SCORING_CLASS来定义不同的评分方法。cpp// Thirdparty/DBoW2/DBoW2/ScoringObject.h[reference:6] namespace DBoW2 { /// Base class of scoring functions class GeneralScoring { public: // 纯虚函数计算两个向量的得分 virtual double score(const BowVector v, const BowVector w) const 0; // ... }; }ORB-SLAM3支持多种评分方法并通过宏来声明评分方法宏定义是否需要归一化L1范数评分 (L1 Scoring)__SCORING_CLASS(L1Scoring, true, L1)是L2范数评分 (L2 Scoring)__SCORING_CLASS(L2Scoring, true, L2)是卡方评分 (ChiSquare Scoring)__SCORING_CLASS(ChiSquareScoring, true, L1)是KL散度评分 (KL Scoring)__SCORING_CLASS(KLScoring, true, L1)是巴氏距离评分 (Bhattacharyya Scoring)__SCORING_CLASS(BhattacharyyaScoring, true, L1)是点积评分 (Dot Product Scoring)__SCORING_CLASS(DotProductScoring, false, L1)否ORB-SLAM3默认使用的是L1Scoring。这类评分通常要求向量先进行归一化mustNormalize返回true。⚙️ 2. 核心原理L1范数评分如何计算L1Scoring的计算分为两步向量归一化和计算相似度。步骤一向量归一化 (Normalization)在进行相似度计算前需要对词袋向量v和w进行L1归一化。对于一个词袋向量v它包含了一系列(单词ID, 权重)对。L1归一化就是让向量中所有权重的绝对值之和等于1。公式v_norm v / ||v||₁其中||v||₁ Σ |v_i|是向量中所有元素绝对值之和。步骤二计算L1相似度归一化后两个向量的L1相似度通过以下交集Intersection方法计算公式score(v, w) Σᵢ min(v_i, w_i)这个公式的含义是遍历两个向量中所有共同的单词将其权重中较小的那个累加起来。注意由于L1归一化保证了向量元素和为1这个交集得分天然地落在[0, 1]区间内。得分越高表示两个向量共享的视觉单词越多图像越相似。 3. 实例演示从公式到数字让我们通过一个具体的例子来理解这个过程。假设我们有两个极度简化的词袋向量词汇表里只有单词A、B、C三个视觉单词。图像1 (当前帧)包含单词A(权重2) 和 单词B(权重3)。向量v {A: 2, B: 3}图像2 (候选关键帧)包含单词B(权重4) 和 单词C(权重1)。向量w {B: 4, C: 1}步骤一L1归一化||v||₁ 2 3 5v_norm {A: 2/5, B: 3/5} {A: 0.4, B: 0.6}||w||₁ 4 1 5w_norm {B: 4/5, C: 1/5} {B: 0.8, C: 0.2}步骤二计算L1相似度找到两个向量共有的单词即交集这里是单词B。对于单词Bmin(v_norm(B), w_norm(B)) min(0.6, 0.8) 0.6最终的相似度得分score 0.6这个0.6就是mpVoc-score()函数返回的float si值。 4. 总结float si mpVoc-score(F-mBowVec, pKFi-mBowVec);mpVoc是一个指向DBoW2::Vocabulary类的指针它内部维护着一个DBoW2::L1Scoring类型的评分对象。score()函数接收两个BowVector作为输入。函数内部首先对两个向量进行L1归一化。然后计算它们归一化后权重的交集和得到一个[0, 1]范围内的浮点数。这个数值就是两帧图像在视觉上的相似度得分分数越高视觉上越相似。关于累加的结果绝不会大于 1最大值就是1。原因其实很简单 数学证明假设有两个经过L1 归一化的非负向量v和w它们的所有元素和都等于 1∑ivi1,∑iwi1i∑vi1,i∑wi1对于每个共同的单词i我们取min(v_i, w_i)。由于min(v_i, w_i) ≤ v_i也 ≤w_i所以对整个向量求和∑imin(vi,wi)≤∑ivi1i∑min(vi,wi)≤i∑vi1同理它也 ≤ 1。因此累加和≤ 1并且只有当两个向量完全相等非零元素完全相同且权重一致时才能达到1。 直观理解你可以把 L1 归一化后的向量看作一个“概率分布”每个单词的权重就是它出现的相对频率。两个分布的交集共同单词中较小的频率的总和不可能超过整个分布的总概率即 1。这就像两个馅饼你只能取它们重叠的那部分最多也只能取到整个馅饼1。所以mpVoc-score()返回的相似度得分始终在[0, 1]之间非常合理。