ORB_SLAM2:词袋模型 Bag of Words

一、理解BoW

假设有三句话

复制代码
我喜欢吃苹果
我喜欢吃香蕉
我喜欢看电影

我们建立一个词典

复制代码
Vocabulary:

Word 0:我
Word 1:喜欢
Word 2:吃
Word 3:苹果
Word 4:香蕉
Word 5:看
Word 6:电影

那么刚才的三句话就可以表示为:

复制代码
我    → Word 0
喜欢  → Word 1
吃    → Word 2
苹果  → Word 3

即   BoW1​=[1,1,1,1,0,0,0]

同理,剩下的两句可以表示为:

复制代码
BoW2​=[1,1,1,0,1,0,0]
BoW3​=[1,1,0,0,0,1,1]

于是,经过上面的处理,一句话就可以用一个向量表示。视觉BoW也是同样的思想,但是图像中并没有文字,但是有特征点和描述子。

假设一张图片提取了1000个 ORB特征点,

复制代码
Feature 1 → ORB descriptor
Feature 2 → ORB descriptor
Feature 3 → ORB descriptor
...
Feature 1000 → ORB descriptor

每个特征点的描述子都是256bit,这些描述子直接比较起来虽然可以,但是数量很多。所以 ORB-SLAM2 做了一件非常聪明的事情:把大量相似的 ORB 描述子归成一个"视觉单词"

什么是"视觉单词"?

假设一张图有1000个ORB特征,经过Vocabulary:

复制代码
ORB Feature
   ↓
Visual Word

Feature 1  → Word 25
Feature 2  → Word 102
Feature 3  → Word 25
Feature 4  → Word 8
Feature 5  → Word 102
...

统计以后:

复制代码
Word 8    → 50个特征
Word 25   → 120个特征
Word 102  → 83个特征
Word 300  → 17个特征
...

于是整张图片就变成:

复制代码
BoW=[0,0,0,...,50,...,120,...,83,...]

这就是视觉 BoW。

所以ORB_SLAM2的BoW本质可以理解为:一张图片 --> ORB特征 --> 视觉单词 -->词频统计,也就是不再关心每一个具体的ORB特征是什么,而是关心这张图片出现了哪些视觉词,以及各自有多少。

二、ORB_SLAM2 中 ComputeBoW()

函数作用:把当前 Frame中的所有ORB 描述子,转换成视觉词袋表示。

cpp 复制代码
void Frame::ComputeBoW()
{
    if(mBowVec.empty())
    {
        vector<cv::Mat> vCurrentDesc = Converter::toDescriptorVector(mDescriptors);
        mpORBvocabulary->transform(vCurrentDesc,mBowVec,mFeatVec,4);
    }
}

变量含义

cpp 复制代码
transform(输入描述子, 输出BoW向量, 输出Feature向量, 深度);

mDescriptors 表示当前帧中所有特征点的描述子

mBowVec是什么?

mBowVec 通常是:BowVector mBowVec;它不是简单的:vector<float>,而是一个稀疏的:视觉词 ID → 权重

例如:

复制代码
Word 10  → 0.12
Word 53  → 0.08
Word 128 → 0.25
Word 456 → 0.15
Word 900 → 0.40

为什么不是统计次数而是权重?

假设:

复制代码
图像A:

Word 1 → 100次
Word 2 → 10次
Word 3 → 1次

简单统计:

复制代码
[100, 10, 1]

但是有些视觉词特别常见。例如:

复制代码
墙
边缘
角点
纹理

这些可能在很多图像里都出现。如果一个视觉词几乎每张图都出现,那么它对区分两张图的作用其实很小。这时候就会引入类似:TF-IDF的思想。

TF:Term Frequency。表示:这个视觉词在当前图像中出现得多不多?

IDF:Inverse Document Frequency。表示:这个视觉词在所有图像中常不常见?

于是:

复制代码
常见视觉词
↓
权重低

而:

复制代码
稀有视觉词
↓
权重高

所以最终:mBowVec 不是简单的出现次数,而是经过权重计算后的

复制代码
视觉词 → 权重

mBowVec与mFeatVec的区别

mBowVec表示:这张图像整体上有哪些视觉词? 它主要用于:**判断两张图像是否相似。**例如:

复制代码
Word 10  → 0.12
Word 53  → 0.08
Word 128 → 0.25
...

mFeatVec表示:**每个视觉词对应了哪些 ORB 特征?**例如:

复制代码
Word 10 → 特征 3、25、100
Word 53 → 特征 5、19、88
Word 128 → 特征 2、8、30、55

深度是什么?

mpORBvocabulary 指向ORBVocabulary,来自于Vocabulary/ORBvoc.txt。本质是哪个是一个巨大的ORB描述子字典,它不是简单的

cpp 复制代码
Word 0
Word 1
Word 2
...

而是一个树状结构,即Vocabulary Tree(词汇树)。

为什么需要树状结构?

假设 Vocabulary 有:100万个视觉单词。现在来了一个 ORB 描述子:D。最笨的方法:

复制代码
D 和 Word 0 比较
D 和 Word 1 比较
D 和 Word 2 比较
...
D 和 Word 999999 比较

需要比较:1000000 次,这非常慢。所以 ORB-SLAM2 建立了一棵树。例如:

复制代码
                    Root
                     |
          ┌──────────┼──────────┐
          ↓          ↓          ↓
        Node1      Node2      Node3
        / | \      / | \      / | \
       ↓  ↓  ↓    ↓  ↓  ↓    ↓  ↓  ↓
      ...        ...         ...

通过树逐层寻找,这样就不用和所有单词比较。

复制代码
ORB descriptor
      ↓
第一层
      ↓
第二层
      ↓
第三层
      ↓
第四层
      ↓
...
      ↓
Visual Word

三、BoW 相似 ≠ 一定是同一个地方

比如:两个办公室都有墙、桌子、窗户、电脑,那么 BoW 可能比较相似。只能说明可能存在回环,不能直接证明这是在同一个地方。因此,ORB_SLAM2后边还需要进行特征匹配、几何验证、Sim3、回环检测。

四、具体步骤流程

cpp 复制代码
                 当前图像
                    │
                    ↓
              ORB特征提取
                    │
                    ↓
             ORB KeyPoints
                    │
                    ↓
             ORB Descriptors
                    │
                    ↓
          mDescriptors(N×32)
                    │
                    ↓
     Converter::toDescriptorVector()
                    │
                    ↓
         vector<cv::Mat>
                    │
                    ↓
      mpORBvocabulary->transform()
                    │
                    ↓
             Vocabulary Tree
                    │
                    ↓
        每个描述子寻找Visual Word
                    │
             ┌──────┴──────┐
             ↓             ↓
          mBowVec       mFeatVec
             │             │
             ↓             ↓
       图像整体表示      特征分组
             │             │
             ↓             ↓
       相似度计算       快速特征匹配
             │
             ↓
        回环候选检测
             │
             ↓
        几何一致性验证
             │
             ↓
          回环闭合

五、举例说明

假设当前图像有 10 个 ORB 描述子:

复制代码
D1
D2
D3
D4
D5
D6
D7
D8
D9
D10

经过 Vocabulary:

复制代码
D1  → Word 5
D2  → Word 5
D3  → Word 8
D4  → Word 12
D5  → Word 5
D6  → Word 20
D7  → Word 8
D8  → Word 12
D9  → Word 12
D10 → Word 20

统计:

复制代码
Word 5  → 3
Word 8  → 2
Word 12 → 3
Word 20 → 2

那么这张图就可以表示成:

复制代码
Word 5  : 3
Word 8  : 2
Word 12 : 3
Word 20 : 2

再经过权重归一化,这就是:mBowVec。

复制代码
Word 5  : 0.30
Word 8  : 0.20
Word 12 : 0.30
Word 20 : 0.20

两张图像怎么比较?

假设:图像 A

复制代码
Word 5  : 0.30
Word 8  : 0.20
Word 12 : 0.30
Word 20 : 0.20

图像 B

复制代码
Word 5  : 0.28
Word 8  : 0.22
Word 12 : 0.32
Word 20 : 0.18

两者非常接近。所以:Similarity(A,B)比较高。

而图像 C

复制代码
Word 100 : 0.3
Word 200 : 0.2
Word 300 : 0.3
Word 500 : 0.2

和 A 几乎没有共同视觉词。那么:Similarity(A,C)就比较低。

相关推荐
熊猫_豆豆12 天前
家庭扫地机器人路径规划Python版本
机器人·扫地机器人·slam·路径规划
事已至此_先吃饭吧16 天前
调试 ORB_SLAM2 非ROS版本
slam
全息数据20 天前
里程计运动模型及标定【激光slam(一)】
slam·激光slam·定位与建图
zh路西法20 天前
【3D SLAM源码解读系列】(四) GTSAM与iSAM2——从回环约束到位姿图优化
c++·slam·gtsam·pgo·isam2
乱七八糟的屋子1 个月前
TooN 超详细入门实战教程|C++轻量极致精简矩阵库(机器人/SLAM专用)
矩阵·机器人·数值计算·slam·c++矩阵库·轻量线性代数·机器人数学
kobesdu1 个月前
激光雷达运动畸变是怎么来的,FAST-LIO又如何用IMU反向传播消除它?
ros·slam·fastlio
酸梅果茶2 个月前
【7】lightning_lm项目-LIO 前端 -IVox 局部地图
前端·slam
a1117762 个月前
机器人导航入门指南(从 0 到 1)
笔记·学习·slam
酸梅果茶2 个月前
【6】lightning_lm项目-LIO 前端 -点云预处理模块
前端·slam