DBoW2中的词典创建原理k-means++算法及代码分析

DBoW2中的词典创建原理k-means++算法及代码分析


ORBSLAM中的词典是通过一个145.3MB的txt文件加载的,VINS-Mono中的词典是通过一个60MB的bin文件加载的,它们都是事先已经生成好的词典,我们需要用到的时候直接按照相应的格式调用就可以了。本文主要讲述的是我们如何通过手头上的图像,自己去创建一个词典。至于应该选取多少图像,选取什么场景下的图像才能创建出稳健的词典,这里就不讨论了。

1.k-means++算法简介

k均值聚类算法(k-means)是一种迭代求解的无监督聚类分析算法,它可以将数据集中在某些方面相似的数据成员进行分类,而k-means++算法是在k-means算法的基础上改进而来的。在介绍k-means++算法前,先从下表来认识一下k-means算法的基本实现步骤,
在这里插入图片描述
k-means++算法也只是对初始点的选择有改进而已,其他步骤和k-means算法一样。初始聚类中心选取的基本思路就是,初始的聚类中心之间的相互距离要尽可能的远,其具体实现步骤如下表所示,
在这里插入图片描述

2.DBoW2创建词典源码分析

进入到TemplatedVocabulary.h文件下,词典类的构造函数声明

 TemplatedVocabulary(int k = 10, int L = 5, 
    WeightingType weighting = TF_IDF, ScoringType scoring = L1_NORM);

词典类的构造函数定义

template<class TDescriptor, class F>
TemplatedVocabulary<TDescriptor,F>::TemplatedVocabulary
  (int k, int L, WeightingType weighting, ScoringType scoring)
  : m_k(k), m_L(L), m_weighting(weighting), m_scoring(scoring),
  m_scoring_object(NULL)
{
   
  createScoringObject();
}

实例化词典类时,如果不指定树的分叉数和层数等,那么它们将使用默认值,分叉数m_k默认值为10,层数m_L默认值为5,权重类型m_weighting默认为TF_IDF,评分算法m_scoring默认为L1_NORM。

接下来找到下面的create()函数,它的参数为所有图像的BRIEF描述子,

template<class TDescriptor, class F>
void TemplatedVocabulary<TDescriptor,F>::create(
  const std::vector<std::vector<TDescriptor> > &training_features)
{
   
  m_nodes.clear();//树形结构的各层节点,包括叶子
  m_words.clear();//单词(树形结构的叶子)
  
  // 节点总数,这其实就是一个等比数列的求和
  int expected_nodes = 
		(int)((pow((double)m_k, (double)m_L + 1) - 1)/(m_k - 1));
  //节点存储空间的预置
  m_nodes.reserve(expected_nodes); // avoid allocations when creating the tree
  
  // 将所有描述子集合到一个vector
  vector<pDescriptor> features;
  getFeatures(training_features, features);

  // create root 
  // 生成根节点
  m_nodes.push_back(Node(0)); // root
  
  // create the tree
  // 利用k-means++算法(内有k-means++算法的递归嵌套)
  HKmeansStep(0, features, 1);

  // create the words
  // 建立一个只有叶节点的序列m_words
  createWords();

  // and set the weight of each node of the tree
  // 为每个叶节点生成权重,此处计算IDF部分,如果不用IDF,则设为1
  setNodeWeights(training_features);  
}

接下来先分析HKmeansStep()函数

/**
* @brief  
* @param  parent_id                    父节点id
* @param  descriptors		           该父节点包含的描述子集合
* @param  current_level                当前层数
*/
template<class TDescriptor, class F>
void TemplatedVocabulary<TDescriptor,F>::HKmeansStep(NodeId parent_id, 
  const vector<pDescriptor> &descriptors, int current_level)
{
   
  if(descriptors.empty()) return;
        
  // 用来存储所有子节点本身的描述子 
  vector<TDescriptor> clusters;
  
  // 用来存储每个子节点包含的描述子(可能不包含子节点本身的描述子)在descriptors向量中的id
  // groups[i] = [j1, j2, ...],表示的意义为
  // id号为j1, j2, ...的描述子包含在子节点i中
  vector<vector<unsigned int> > groups;
	
  clusters.reserve(m_k);
  groups.reserve(m_k);
  
  // 如果特征描述个数小于m_k,直接分类
  if((int)descriptors.size() <= m_k)
  {
   
    groups.resize(descriptors.size());

    for(unsigned int
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值