DBoW2算法

来源:互联网 发布:阿里云学生库存不足 编辑:程序博客网 时间:2024/05/29 10:03

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);                    }                }            }    }
0 0
原创粉丝点击