# DBoW2 ## TemplatedVocabulary.h ### TemplatedVocabulary<TDescriptor,F>::transform() 將影像的特徵點轉為BowVector跟FeatureVector 在[Frame::ComputeBoW()](#Frame::ComputeBoW())中引用 #### 定義 ``` void TemplatedVocabulary<TDescriptor,F>::transform( const std::vector<TDescriptor>& features, //影像中所有的特徵點 BowVector &v, //輸出 BowVector FeatureVector &fv, //輸出 FeatureVector int levelsup //距離葉子的深度 ) const ``` #### 函式流程 1. 將特徵點轉為葉節點的ID與權重 2. 將值存入到BowVector 和 FeatureVector 3. 歸一化 #### 流程詳解 ``` v.clear(); fv.clear(); if(empty()) // safe for subclasses { return; } // normalize // 根據選擇的評分類型來確定是否需要將 BowVector 歸一化 LNorm norm; bool must = m_scoring_object->mustNormalize(norm); typename vector<TDescriptor>::const_iterator fit; if(m_weighting == TF || m_weighting == TF_IDF) { ``` 1. 將特徵點轉為葉節點的ID與權重 ``` unsigned int i_feature = 0; // 遍歷圖像中所有的特徵點 for(fit = features.begin(); fit < features.end(); ++fit, ++i_feature) { WordId id; // 葉子節點的Word id NodeId nid; // FeatureVector 裡的NodeId,用於加速搜索 WordValue w; // 葉子節點Word對應的權重 // w is the idf value if TF_IDF, 1 if TF // 將當前描述子轉化為Word id, Word weight,節點所屬的父節點id(這裡的父節點不是葉子的上一層,它距離葉子深度為levelsup) transform(*fit, id, w, &nid, levelsup); ``` 跳到[TemplatedVocabulary<TDescriptor,F>::transform()2](#TemplatedVocabulary<TDescriptor,F>::transform()2) 2. 將值存入到BowVector 和 FeatureVector ``` if(w > 0) // not stopped { // 如果Word 權重大於0,將其添加到BowVector 和 FeatureVector v.addWeight(id, w); fv.addFeature(nid, i_feature); } } ``` v.addWeight: 跳到[BowVector::addWeight()](#BowVector::addWeight()) fv.addFeature: 跳到[FeatureVector::addFeature()](#FeatureVector::addFeature()) 3. 歸一化 ``` if(!v.empty() && !must) { // unnecessary when normalizing const double nd = v.size(); for(BowVector::iterator vit = v.begin(); vit != v.end(); vit++) vit->second /= nd; } } else // IDF || BINARY { unsigned int i_feature = 0; for(fit = features.begin(); fit < features.end(); ++fit, ++i_feature) { WordId id; NodeId nid; WordValue w; // w is idf if IDF, or 1 if BINARY transform(*fit, id, w, &nid, levelsup); if(w > 0) // not stopped { v.addIfNotExist(id, w); fv.addFeature(nid, i_feature); } } } // if m_weighting == ... if(must) v.normalize(norm); ``` ### TemplatedVocabulary<TDescriptor,F>::transform()2 將描述子轉換成word ID 在[TemplatedVocabulary<TDescriptor,F>::transform()](#TemplatedVocabulary<TDescriptor,F>::transform())中引用 #### 定義 ``` void TemplatedVocabulary<TDescriptor,F>::transform( const TDescriptor &feature, //特徵描述子 WordId &word_id, //word id WordValue &weight, //word 權重 NodeId *nid, //node id int levelsup //離葉子的深度 ) const ``` #### 函式流程 1. 計算當前節點下的子節點與描述子的距離 2. 確認節點是否在搜索層級下 3. 確認節點是否在葉子節點,不是就重複搜尋 4. 存取距離最小的 Word id 和 weight #### 流程詳解 ``` // propagate the feature down the tree vector<NodeId> nodes; typename vector<NodeId>::const_iterator nit; // level at which the node must be stored in nid, if given // m_L: depth levels, m_L = 6 in ORB-SLAM2 // nid_level 當前特徵點轉化為的Word 所屬的 node id,方便索引 const int nid_level = m_L - levelsup; if(nid_level <= 0 && nid != NULL) *nid = 0; // root NodeId final_id = 0; // root int current_level = 0; ``` 1. 計算當前節點下的子節點與描述子的距離 ``` do { // 更新樹的深度 ++current_level; // 取出當前節點所有子節點的id nodes = m_nodes[final_id].children; // 取子節點中第1個的id,用於後面距離比較的初始值 final_id = nodes[0]; // 取當前節點第一個子節點的描述子距離初始化最佳(小)距離 double best_d = F::distance(feature, m_nodes[final_id].descriptor); // 遍歷nodes中所有的描述子,找到最小距離對應的描述子 for(nit = nodes.begin() + 1; nit != nodes.end(); ++nit) { NodeId id = *nit; double d = F::distance(feature, m_nodes[id].descriptor); if(d < best_d) { best_d = d; final_id = id; } } ``` 2. 確認節點是否在搜索層級下 ``` // 記錄當前描述子轉化為Word後所屬的 node id,它距離葉子深度為levelsup if(nid != NULL && current_level == nid_level) *nid = final_id; ``` 3. 確認節點是否在葉子節點,不是就重複搜尋 ``` } while( !m_nodes[final_id].isLeaf() ); ``` 4. 存取距離最小的 Word id 和 weight ``` // turn node id into word id // 取出 vocabulary tree中node距離當前feature 描述子距離最小的那個node的 Word id 和 weight word_id = m_nodes[final_id].word_id; weight = m_nodes[final_id].weight; ``` --- ## BowVector.cpp ### BowVector::addWeight() 將權重加入 BowVector 裡 在[TemplatedVocabulary<TDescriptor,F>::transform()](#TemplatedVocabulary<TDescriptor,F>::transform())中引用 #### 定義 ``` void BowVector::addWeight(WordId id, WordValue v) ``` #### 函式流程 1. 搜尋大於等於id的第一個值的位置 2. 更新權重 #### 流程詳解 1. 搜尋大於等於id的第一個值的位置 ``` BowVector::iterator vit = this->lower_bound(id); ``` 2. 更新權重 ``` if(vit != this->end() && !(this->key_comp()(id, vit->first))) { //如果id = vit->first,gji 說明是同一個word,權重更新 vit->second += v; } else { //如果word id不在BowVector中,就加入 this->insert(vit, BowVector::value_type(id, v)); } ``` ## FeatureVector::addFeature() 將描述子加入FeatureVector裡 在[TemplatedVocabulary<TDescriptor,F>::transform()](#TemplatedVocabulary<TDescriptor,F>::transform())中引用 #### 定義 ``` void FeatureVector::addFeature(NodeId id, //id unsigned int i_feature) //特徵的索引 ``` #### 函式流程 1. 搜尋大於等於id的第一個值的位置 2. 更新特徵點 #### 流程詳解 1. 搜尋大於等於id的第一個值的位置 ``` FeatureVector::iterator vit = this->lower_bound(id); ``` 2. 更新特徵點 ``` // 將同樣node id下的特徵點索引值放在一個向量裡 if(vit != this->end() && vit->first == id) { // 如果這個node id已經創建,可以直接插入特徵點索引 vit->second.push_back(i_feature); } else { // 如果這個node id還未創建,創建後再插入特徵點索引 vit = this->insert(vit, FeatureVector::value_type(id, std::vector<unsigned int>() )); vit->second.push_back(i_feature); } ```