基于词袋模型DBoW2的回环检测实现DLoopDetector (c++)

1. 相关链接

源码地址: https://github.com/dorian3d/DLoopDetector
源码原理: https://blog.csdn.net/weixin_37835423/article/details/112890634

2. demo测试

2.1 源码安装DBow2

git clone https://github.com/dorian3d/DBoW2.git
cd DBoW2
mkdir build & cd build
cmake ..
make
sudo make install

2.2 修改cmakelist.txt文件
源码默认不生成可执行文件,需要修改CMakeLists.txt中的第5行如下:

option(BUILD_DemoBRIEF  "Build demo application with BRIEF features" OFF)

改为

option(BUILD_DemoBRIEF  "Build demo application with BRIEF features" ON)

2.3 下载编译DLoopDetector

git clone https://github.com/dorian3d/DLoopDetector.git
cd DLoopDetector
mkdir build & cd build
cmake ..
make

2.4 下载测试数据集
数据集地址: https://drive.google.com/uc?export=download&id=1MpZwPjXDAUxKfSTpeCjG0PAUpaeWuo7D
然后解压到目录 “DLoopDetector/build/” 下.

2.5 运行demo

cd DLoopDetector/build
./demo_brief

2.6 结果


从结果中可以看出,重复走过的地方都有效的检测出了回环,可验证该算法进行回环检测的有效性.

3. 源码解析

注: 下面注释中的"博客"指的都是我的上一篇博客

template<class TDescriptor, class F>
bool TemplatedLoopDetector<TDescriptor, F>::detectLoop(
  const std::vector<cv::KeyPoint> &keys, 
  const std::vector<TDescriptor> &descriptors,
  DetectionResult &match)
{
  EntryId entry_id = m_database->size();
  match.query = entry_id;
  
  BowVector bowvec;
  FeatureVector featvec;
  
  // 通过词袋模型转换描述子为词袋向量,并更新Direct index查找表,加速后面几何一致性验证过程
  if(m_params.geom_check == GEOM_DI)
    m_database->getVocabulary()->transform(descriptors, bowvec, featvec,
      m_params.di_levels);
  else
    m_database->getVocabulary()->transform(descriptors, bowvec);

  if((int)entry_id <= m_params.dislocal)
  {
    // only add the entry to the database and finish
    m_database->add(bowvec, featvec);
    match.status = CLOSE_MATCHES_ONLY;
  }
  else
  {
    int max_id = (int)entry_id - m_params.dislocal;
    
    // 对应博客5.1节中的数据库候选者检索,检索出可能的候选者
    QueryResults qret;
    m_database->query(bowvec, qret, m_params.max_db_results, max_id);

    // update database
    m_database->add(bowvec, featvec); // returns entry_id
    
    if(!qret.empty())
    {
      // factor to compute normalized similarity score, if necessary
      double ns_factor = 1.0;
      
      // 对应博客5.1节 计算期待的最好得分s(v_t, v_t_dt)
      if(m_params.use_nss)
      {
        ns_factor = m_database->getVocabulary()->score(bowvec, m_last_bowvec);
      }
      
      if(!m_params.use_nss || ns_factor >= m_params.min_nss_factor)
      {
        // 对应博客5.1节最后通过阈值alpha移除归一化匹配得分较小的候选者
        removeLowScores(qret, m_params.alpha * ns_factor);
        
        if(!qret.empty())
        {
          // the best candidate is the one with highest score by now
          match.match = qret[0].Id;
          
          // 对应博客5.2节中对候选者进行分组
          // compute islands
          vector<tIsland> islands;
          computeIslands(qret, islands); 
          // this modifies qret and changes the score order
          
          // get best island
          if(!islands.empty())
          {
            // 对应博客5.2节中获取匹配组(组内候选者归一化得分之和最大组)
            const tIsland& island = 
              *std::max_element(islands.begin(), islands.end());
            
            // 对应博客5.3节中进行时间一致性检查
            // check temporal consistency of this island
            updateTemporalWindow(island, entry_id);
            
            // 对应博客5.3节中,在进行完时间一致性检查后,获取匹配组内的最优候选者(归一化得分最高者)作为闭环候选者
            // get the best candidate (maybe match)
            match.match = island.best_entry;
            
            if(getConsistentEntries() > m_params.k)
            {
              // candidate loop detected
              // check geometry
              bool detection;

              // 对应博客5.4节,进行几何一致性检测
              if(m_params.geom_check == GEOM_DI)
              {
                // all the DI stuff is implicit in the database
                detection = isGeometricallyConsistent_DI(island.best_entry, 
                  keys, descriptors, featvec);
              }
              else if(m_params.geom_check == GEOM_FLANN)
              {
                cv::FlannBasedMatcher flann_structure;
                getFlannStructure(descriptors, flann_structure);
                            
                detection = isGeometricallyConsistent_Flann(island.best_entry, 
                  keys, descriptors, flann_structure);
              }
              else if(m_params.geom_check == GEOM_EXHAUSTIVE)
              { 
                detection = isGeometricallyConsistent_Exhaustive(
                  m_image_keys[island.best_entry], 
                  m_image_descriptors[island.best_entry],
                  keys, descriptors);            
              }
              else // GEOM_NONE, accept the match
              {
                detection = true;
              }
              
              // 更新回环检测状态
              if(detection)
              {
                match.status = LOOP_DETECTED;
              }
              else
              {
                match.status = NO_GEOMETRICAL_CONSISTENCY;
              }
              
            } // if enough temporal matches
            else
            {
              match.status = NO_TEMPORAL_CONSISTENCY;
            }
            
          } // if there is some island
          else
          {
            match.status = NO_GROUPS;
          }
        } // if !qret empty after removing low scores
        else
        {
          match.status = LOW_SCORES;
        }
      } // if (ns_factor > min normal score)
      else
      {
        match.status = LOW_NSS_FACTOR;
      }
    } // if(!qret.empty())
    else
    {
      match.status = NO_DB_RESULTS;
    }
  }

  // update record
  // m_image_keys and m_image_descriptors have the same length
  if(m_image_keys.size() == entry_id)
  {
    m_image_keys.push_back(keys);
    m_image_descriptors.push_back(descriptors);
  }
  else
  {
    m_image_keys[entry_id] = keys;
    m_image_descriptors[entry_id] = descriptors;
  }
  
  // store this bowvec if we are going to use it in next iteratons
  if(m_params.use_nss && (int)entry_id + 1 > m_params.dislocal)
  {
    m_last_bowvec = bowvec;
  }

  return match.detection();
}
  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值