DBoW2算法

DBoW2是一种高效的回环检测算法,DBow2算法的全称为Bags of binary words for fast place recognition in image sequence,使用的特征检测算法为Fast,描述子为BRIEF描述子,是一种离线方法。

算法流程
Bag of Words字典建立方法(最终得到的就是每一层的不同类的median,每一个叶子节点对应的就是一个词汇)

这里写图片描述

建树流程

这里写图片描述

权重设置
权重设置用的是idf,意思是词汇在训练过程中出现的频率越高,区分度越低,因此权重越低
      idf=logNni

每一个节点包含

struct Node
{
    //在所有节点中的标号
    NodeId id;

    //该节点的权重,该权重为
    //训练的过程中设置的,在得到了树之后,
    //所有的描述子过一遍树,得到每一个单词出现
    //的次数,除以总的描述子数目
    WorldValue weight;

    //描述符,为每一类的均值(对于brief描述子,则要对均值进行二值化)
    TDescriptor descriptor;

    //如果有叶节点,则有词汇的ID
    WordID word_id;
}

上面的方法是分层聚类的,每一次聚类得到的多个节点,都有median v表示该类,可以用来判断新的词汇是否属于该类。最终建立的树包括W个叶节点,也就是W个视觉词汇,词汇也用median表示。

http://www.cnblogs.com/jian-li/p/5664559.html


视觉词典
DBoW2库采用树状结构存储词袋,搜索复杂度在log(N)。每个叶节点被赋予一个权重,默认为TF-IDF。

TF-IDF主要思想是:如果某个词或短语在一片文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类.TF-IDF实际上是TF*IDF,TF代表词频(Term Frequency),表示词条在文档d中的出现频率.IDF代表逆向文件频率(Inverse Document Frequency)。如果包含词条t的文档越少,IDF越大,表明词条t具有很好的类别区分能力。
第k个叶节点的TF和IDF分别定义为

IDFk=lognumber of all imagesnumber of images reach kth leaf node

TFk=lognumber of features locates in leaf node knumber of all features

视觉词典可以通过离线训练大量数据得到。训练中只计算和保存单词的IDF值,即单词在众多图像中的区分度。TF则是从实际图像中计算得到各个单词的频率。单词的TF越高,说明单词在这幅图像中出现的越多;单词的IDF越高,说明单词本身具有高区分度。二者结合起来,即可得到这幅图像的BoW描述。

template<class TDescriptor, class F>
void TemplatedVocabulary<TDescriptor, F>::create(
  const std::vector<std::vector<TDescriptor> > &training_features, // 图像特征集合
  int k, // 每层的类的个数
  int L, // 树的层数
  WeightingType weighting, //权重类型,默认为TF-IDF
  ScoringType scoring) // 得分的类型,默认为L1-norm
  {
      m_nodes.clear();
      m_words.clear();

      //节点数 = Sum_(i=0..L) (k^i)
      int expected_nodes = (int)((pow((double)m_k,(double)m_L + 1) - 1) / (m_k - 1));
      m_nodes.reserve(expected_nodes);

      //将所有特征描述集合到一个vector
      std::vector<pDescriptor> features;
      getFeature(training_features, features);

      //生成根节点
      m_nodes.push_back(Node(0)); //root

      //k-means++ (内有递归)
      HKmeansStep(0, features, 1);

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

      //为每一个叶节点生成权重,此处计算IDF部分,如果不用IDF,则为1
      setNodeWeights(training_features)
  }

  /*k-means++过程*/

  template<class TDescriptor, class F>
  void TemplatedVocabulary<TDescriptor, F>::HKmeansStep(
    NodeId parent_id, // 父节点ID
    const std::vector<pDescriptor> &descriptor, // 该父节点对应的描述集合
    int current_level // 当前层数)
    {
        if(descriptors.empty()) return;

        //用来储存子节点的特征描述
        std::vector<TDescriptor> clusters;

        //用来储存每一个子节点对应的特征描述在descriptor向量中的id
        std::vector<std::vector<unsigned int > groups; //groups[i] = {j1, j2, ...}
        // j1, j2 ... indices of descriptors associated to cluster 1

        clusters.reserve(m_k);
        group.reserve(m_k);

        //如果特征描述个数小于m_k,直接分类
        if((int)descriptors.size() <= m_k)
        {
            groups.resize(descriptors.size());

            for(unsigned int i = 0; i < descriptors.size(); i++)
            {
                groups[i].push_back(i);
                clusters[i].push_back(*descriptors[i]);
            }
        }
        else
        {
            //k_means分类
            bool first_time = true;
            bool goon = true;

            //用来检测迭代过程中前后两次分类结果是否一致,如果一致,分类结束
            std::vector<int> last_association, current_association;

            //迭代过程
            while(goon)
            {
                // 1. 分类
                if(first_time)
                {
                    //第一次分类,初始化分类
                    initiateClusters(descriptors, clusters);
                }
                else
                {
                    //计算每一类的meanValue
                    for(unsigned int c = 0; c < clusters.size(); c++)
                    {
                        std::vector<pDescriptor> cluster_descriptors;
                        cluster_descriptors.reserve(groups[c].size());

                        //利用group,读取每一类对应的id
                        std::vector<unsigned int>::const_iterator vit;

                        for(vit = groups[c].begin(); vit != groups[c].end(); ++vit)
                        {
                            cluster_descriptors.push_back(descriptors[*vit]);
                        }

                        //计算meanValue
                        F::meanValue(cluster_descriptors, clusters[c]);
                    }
                }

                // 2. 利用1计算的中心重新分类
                groups.clear();
                groups.resize(clusters.size(), std::vector<unsigned int>());
                current_association.resize(descriptors.size());

                typename std::vector<pDescriptor>::const_iterator fit;

                //对每一个特征,计算它与k个中心特征的距离,标记距离最小的中心特征的id
                for(fit = descriptors.begin(); fit != descriptors.end(); ++fit)
                {
                    double best_dist = F::distance(*(*fit)), clusters[0]);
                    undigned int icluster = 0;
                    for(unsigned int c = 1; c < clusters.size(); ++c)
                    {
                        double dist = F::distance(*(*fit), clusters[c]);
                        if(dist < best_dist)
                        {
                            best_dist = dist;
                            icluster = c;
                        }
                    }
                    //记录分类信息
                    groups[icluster].push_back(fit - descriptors.begin());
                    current_association[fit - descriptors.begin()] = icluster;
                }

                //k-means++ ensures all the clusters has any feature associated with them
                //3 . 检测前后两次分类结果是否一致,如一致,分类结束
                if(first_time)
                    first_time = false;
                else
                {
                    goon = false;
                    for(unsigned int i = 0; i < current_association.size(); i++)
                    {
                        if(current_association[i] != last_association[i])
                            goon = true;
                        break;
                    }
                }

                if(goon)
                {
                    last_association = current_association;
                }
            }

            //生成本层的节点,其特征描述为每一类的meanValue
            for(unsigned int i = 0; i < clusters.size(); ++i)
            {
                NodeId id = m_nodes.size();
                m_nodes.push_back(Node(id));
                m_nodes.back().descriptor = clusters[i];
                m_nodes.back().parent = parent_id;
                m_nodes[parent_id].children.push_back(id);
            }

            // go on with the next level
            if(current_level < m_L)
            {
                // iterate again with the resulting clusters
                const std::vector<NodeId> &children_ids = m_nodes[parent_id].children;
                for(unsigned int i = 0; i < clusters.size(); ++i)
                {
                    NodeId id = children_ids[i];

                    std::vector<pDescriptor> child_features;
                    child_features.reserve(groups[i].size());

                    std::vector<unsigned int>::const_iterator vit;
                    for(vit = groups[i].begin(); vit != groups[i].end(); ++vit)
                    {
                        child_features.push_back(descriptors[*vit]);
                    }

                    //进入下一层,继续分类
                    if(child_features.size() > 1)
                    {
                        HKmeansStep(id, child_features, current_level + 1);
                    }
                }
            }
    }
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值