---
# System prepended metadata

title: DBoW2

---

# 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);
  }
```