1. 前言
现在一谈到视觉slam回环检测,基本上想到的都是用词袋模型来实现,几个主流的slam框架都在使用它,比如:
因此很有必要了解一下它的原理.
2. 何为词袋模型 ?
如上左图是一个很直观的词袋模型,袋子中装的就是单词,有了这个单词袋,上面一段话就可以用一个统计直方图来表示,即统计单词袋中每个单词在这段话中出现的次数,于是就将这段话转换成了一个词袋向量
[
8
,
5
,
4
,
4
,
3
,
2...
]
[8, 5, 4, 4, 3, 2 ...]
[8,5,4,4,3,2...],如果我们想要判断这段话是否与之前的某段话类似,我们就只需要将之前的某段话转换成词袋向量,然后比较两个词袋向量的相似性,即可判断两段话是否类似.该方法最显著的优势就是不用一个单词一个单词的去对两段话进行比较,而是对每段话进行量化生成一个词袋向量,然后通过比较向量的相似性来判断段落的相似性,显著的减少了比较的时间.
视觉词袋模型原理与词袋模型类似,如上右图所示,视觉词袋中的"单词"为提取的视觉特征,而待转换的对象为视觉图片,该模型通过将视觉图像转换成视觉词袋向量来对图像的相似性进行比较.至此,还存在两个疑问: 视觉词袋模型中的"单词"是如何生成的? 有了视觉词袋向量,如何判断两张图片的相似性? 这两个疑问的解释分别对应下面章节的"词袋模型的生成"和"图像数据库检索"部分.
3. 词袋模型的生成
词袋模型生成过程如上图所示,包括两个步骤: 特征提取和对特征进行kmeans聚类生成视觉"单词"两个过程.
3.1 特征提取
论文中提到特征点提取算法采用的是FAST特征点检测器和BRIEF描述器,主要原因是因为BRIEF描述子是通过比较特征点周围patch内多组两个像素的强度值来生成的,结果是一个二进制向量,可以直接用汉明距离来计算两个描述子之间的距离,因此该描述子计算和比较都非常的快.原理可查看链接.
FAST特征点检测原理
BRIEF描述子原理
3.2 kmeans聚类生成视觉"单词"
该过程为了后续检测图片的相似性的需要,使用的是一个由多层词袋组成的图像数据库进行视觉"单词"的生成,这样可以将数据库保存到本地,方便后面相似图片的快速查找,该过程如下图所示:
构建了一个词袋树(词袋树的叶子节点即为生成的视觉"单词"),并且在构建过程中生成了Inverse index 和 Direct index两个查找表.
3.2.1 词袋树构建
词袋树的构建需要指定
k
w
k_{w}
kw和
L
w
L_{w}
Lw两个参数,其中
k
w
k_{w}
kw表示每个节点的分支数,
L
w
L_{w}
Lw表示整个树的高度.设置完参数后首先将所有的描述子作为根节点,然后利用kmeans算法将根节点中的描述子聚类成
k
w
k_{w}
kw个集合,然后再对每个集合进行kmeans聚类又生成
k
w
k_{w}
kw个集合,反复此过程
L
w
L_{w}
Lw次,最后生成
k
w
L
w
k_{w}^{L_{w}}
kwLw个叶子节点.此时每个叶子节点中包含的是一组描述子的集合,但最后的视觉单词只包含一个描述子,因此代码中将描述子集合的平均值作为视觉"单词"的描述子.
视觉"单词"除了描述子属性外还带有一个权重属性,也就是这个单词对于区分图像相似程度的重要性.采用的是
t
f
−
i
d
f
tf-idf
tf−idf 方法,该方法的计算公式如下(参考代码):
m_words[i]->weight = log((double)NDocs / (double)Ni[i]);
代码中 NDocs 是总的用于训练的图像数量,Ni[i] 表示单词在所有训练图像中出现的频次.因此可以通过公式可以看出单词在图像中出现的频次越高,对应的权重越小,也就是说单词在图像中出现的几率越高,可区分性越差,越不能进行图像相似性的判断,比较符合常理.
3.2.2 Inverse index
随着词袋树的生成过程,同时生成了Inverse index查找表,该表记录了词袋中每个单词其出现过的图像集合.这个表在进行图像比较时非常有用,因为只用比较拥有相同单词的图像即可,不需要遍历整个图像数据库.
3.2.3 Direct index
随着词袋树的生成过程,同时也生成了Direct index查找表,该查找表是针对每张图像保存其所包含的单词(叶子节点)的父节点和父节点内所包含的描述子集合.这张表的最大作用是加速回环检测过程中图像的几何一致验证过程,原因是该表提供了一个很好的先验.原理是经过kmeans聚类后,每个聚类集合里面的特征点相似的可能性较大,因此,在进行两张图像的特征点的几何一致性验证时,可以通过聚类的先验找到近似匹配的特征点,加速收敛过程.
4. 图像数据库检索
图像数据库检索的目的是生成词袋向量,并对两幅图像的相似性进行定量计算,因此包括确定图像中单词、词袋向量生成和相似性计算三个过程.
4.1 确定图像中存在的单词
(1) 检测图像中的特征
(2) 加载第三节生成的词袋树数据库
(3) 对于每个特征点,从根节点开始,计算当前描述子与下一层每个节点所包含的描述子集合的中心的距离,并选取距离最短的节点重复上述过程,直到到达叶子层.该叶子节点所对应单词即为当前特征点对应的单词.
4.2 词袋向量生成
按照词袋模型原理来说,一幅图像的词袋向量是指该图像中出现的单词相对于词袋中的单词进行的统计,生成一个统计直方图向量,但是一般的视觉词袋中的单词数目达百万个之多,不可能将一个词袋向量的维度设置为百万级,代码中采用的是继承map的方式,只记录出现的单词id以及该单词出现的总权重数.其数据结构如下:
class BowVector: public std::map<WordId, WordValue>
其 WordId 记录的是单词的id,WordValue 记录的是总的单词权重(单词出现的次数乘以单词的权重).
4.3 相似性定量计算
论文中给出的词袋向量相似性是通过计算L1范数,计算方法如下:
但实际代码中的计算公式如下:
s
(
v
1
,
v
2
)
=
−
1
2
∑
v
1
∈
v
1
,
v
2
∈
v
2
(
∣
v
2
−
v
1
∣
−
∣
v
2
∣
−
∣
v
1
∣
)
(1)
s(\mathbf{v}_{1}, \mathbf{v}_{2}) = -\frac{1}{2}\sum_{v1\in\mathbf{v}_{1},v2\in\mathbf{v}_{2}}(|v_{2}-v_{1}| - |v_{2}| - |v_{1}|) \tag{1}
s(v1,v2)=−21v1∈v1,v2∈v2∑(∣v2−v1∣−∣v2∣−∣v1∣) (1)
以上公式都能很好的刻画两个向量的相似性,当两向量完全相同时,都能保证输出的得分值为1.
5. 回环检测
回环检测总共包含四个步骤,下面分别进行详细描述.
5.1 数据库检索
假设我们获得图像
I
t
I_{t}
It,通过数据库检索匹配得分
s
(
v
t
,
v
j
)
s(\mathbf{v}_{t}, \mathbf{v}_{j})
s(vt,vj)我们可以获得多个匹配的候选者
<
v
t
,
v
t
1
>
,
<
v
t
,
v
t
2
>
,
<
v
t
,
v
t
3
>
,
.
.
.
<\mathbf{v}_{t}, \mathbf{v}_{t1}>,<\mathbf{v}_{t}, \mathbf{v}_{t2}>, <\mathbf{v}_{t}, \mathbf{v}_{t3}>,...
<vt,vt1>,<vt,vt2>,<vt,vt3>,....然后利用期待的最好得分
s
(
v
t
,
v
t
−
Δ
t
)
s(\mathbf{v}_{t},\mathbf{v}_{t-\Delta t})
s(vt,vt−Δt)对所有候选得分进行归一化,公式如下:
η
(
v
t
,
v
t
j
)
=
s
(
v
t
,
v
j
)
s
(
v
t
,
v
t
−
Δ
t
)
(2)
\eta(\mathbf{v}_{t},\mathbf{v}_{t_{j}}) = \frac{s(\mathbf{v}_{t}, \mathbf{v}_{j})}{s(\mathbf{v}_{t}, \mathbf{v}_{t-\Delta t})} \tag{2}
η(vt,vtj)=s(vt,vt−Δt)s(vt,vj)(2)
然后判断每个候选者的归一化匹配得分是否大于给定的最小阈值
α
\alpha
α,如果没有,则直接滤除.
注意:
v
t
−
Δ
t
\mathbf{v}_{t-\Delta t}
vt−Δt是先前图片的词袋向量,如果
s
(
v
t
,
v
t
−
Δ
t
)
s(\mathbf{v}_{t}, \mathbf{v}_{t-\Delta t})
s(vt,vt−Δt)比较小的话(假设原地旋转),将会使得公式(2)获得的归一化得分非常大,这是非常不合理,因此,对于先前图像的选择需要满足大于
s
(
v
t
,
v
t
−
Δ
t
)
s(\mathbf{v}_{t}, \mathbf{v}_{t-\Delta t})
s(vt,vt−Δt)的最小设置得分或者包含足够数量的特征点.
5.2 匹配分组
为了避免在进行数据库检索时时间上靠的很近的图像相互竞争,我们将候选者进行分组,分组依据的是时间间隔
T
i
T_{i}
Ti,假设该时间间隔内包含时间戳
t
n
i
,
.
.
.
,
t
m
i
t_{n_{i}},...,t_{m_{i}}
tni,...,tmi,则将这些时间戳对应的候选者分成同一组,并且按下面公式计算组得分:
H
(
v
t
,
V
T
i
)
=
∑
j
=
n
i
m
i
η
(
v
t
,
v
t
j
)
(3)
H(\mathbf{v}_{t}, V_{T_{i}}) = \sum_{j=n_{i}}^{m_{i}}\eta(\mathbf{v}_{t}, \mathbf{v}_{t_{j}}) \tag{3}
H(vt,VTi)=j=ni∑miη(vt,vtj)(3)
组得分最高的分组将会作为匹配组,然后在进行后续的时间一致性验证.
5.3 时间一致性验证
在获得匹配组 V T ′ V_{T'} VT′之后,我们需要验证时间一致性,也即当前匹配 < v t , V T ′ > <\mathbf{v}_{t},V_{T'}> <vt,VT′>之前的 k k k组匹配应该与当前匹配保持一致,这样才能保证相邻的分组之间是接近重叠的.时间一致性验证通过之后,我们只保留唯一的一个匹配 < v t , v t ′ > <\mathbf{v}_{t}, \mathbf{v}_{t'}> <vt,vt′>,其中 v t ′ ∈ V T ′ \mathbf{v}_{t'}\in V_{T'} vt′∈VT′,该匹配是 η \eta η最大值的那一个,并且将其作为闭环候选者,进行后面的空间一致性验证.
5.4 有效的空间一致性验证
我们在进行几何一致性检查时需要在图像 I t I_{t} It和 I t ′ I_{t'} It′之间查找至少12对对应特征点,以通过RANSAC算法计算出两张图像之间的基础矩阵.该配对特征点的获取最简单的方法是利用穷举法,但其复杂度是 O ( n 2 ) O(n^{2}) O(n2),速度太慢,可以通过在构建词袋树的过程中维护的Direct index表来加速此过程.此过程实现是: 先通过Direct index表查找图像 I t ′ I_{t'} It′所对应的特征单词的父节点,然后根据父节点中的特征描述子集合在 I t I_{t} It图像中查找对应的特征点,这样就避免了对 I t I_{t} It中的特征点的穷举过程,加速了配对点的查找.
6. 参考
论文: http://doriangalvez.com/papers/GalvezTRO12.pdf
源码: https://github.com/dorian3d/DBoW2
效果: https://www.youtube.com/watch?v=Y6J6C_-mgRw