DBoW的编译和运行
本文最后更新于 2025年8月8日 晚上
本文主要分享了在Linux下DboW3和DBoW2的编译和示例运行。
概述
DBoW2 和 DBoW3 都是用于图像检索与定位的视觉词袋(Bag-of-Words, BoW)库,主要应用于计算机视觉领域,尤其在 SLAM(同步定位与建图)、图像检索、视觉定位等任务中。它们的核心思想是将图像特征描述子(如 ORB、SIFT 等)量化成“视觉词汇”,然后用词袋模型表示整幅图像,实现高效的图像匹配与检索。
**DBoW2 和 DBoW3 都支持二进制描述符和浮点描述符。**常见支持的类型有:
- 二进制:ORB、BRIEF、BRISK
- 浮点:SIFT、SURF
你只需在初始化词袋对象时指定描述符类型即可。
DBoW2
- 全称:DBoW2(Database of Bag of Words 2)
- 作者:Dorian Galvez-Lopez
- 语言:C++
- 功能:
- 支持多种特征描述子(如 ORB、BRIEF、SIFT 等)。
- 可生成和管理视觉词典(Vocabulary)。
- 支持图像检索(快速查询与匹配)、回环检测(Loop Closure Detection)等。
- 广泛应用于 ORB-SLAM、LSD-SLAM 等 SLAM 系统。
 
- 开源地址:https://github.com/dorian3d/DBoW2
DBoW3
- 全称:DBoW3(Database of Bag of Words 3)
- 作者:同为 Dorian Galvez-Lopez(以及社区参与者)
- 语言:C++
- 功能:
- 是 DBoW2 的升级版,API 更加现代化,易于集成,支持 C++11。
- DBoW3 可以在 Linux 和 Windows 上编译。
- 与 DBoW2 yml 文件兼容。
 
- 性能优化,部分功能简化,代码更易维护。
- DBoW3 能够开箱即用地使用二进制和浮点描述符。无需为任何描述符重新实现任何类。
- 重写了部分代码以优化速度。DBoW3 的界面也得到了简化。
- 可以使用二进制文件。二进制文件的加载/保存速度比 yml 文件快 4-5 倍。此外,二进制文件还可以压缩。
 
- 去除了对 OpenCV 2.x 的依赖,支持 OpenCV 3 及更新版本。
- DBoW3 仅需要 OpenCV。DBoW2 对 DLIB 的依赖已被移除。
 
- 仍可用于图像检索、回环检测等任务。
 
- 是 DBoW2 的升级版,API 更加现代化,易于集成,支持 C++11。
- 开源地址:https://github.com/rmsalinas/DBoW3
区别
- DBoW3 是对 DBoW2 的重写和优化,API 更现代,依赖更少,适配新版本 OpenCV,推荐新项目采用 DBoW3。
- DBoW2 仍被很多经典项目(如 ORB-SLAM2)所使用。
安装依赖
- 
OpenCV4因为安全性提升、解析器更复杂、兼容性增强等原因导致其读取 *.yml文件的速度要远慢于OpenCV3。
- 
安装Boost库(DBoW2需要): 1 
 2sudo apt-get update
 sudo apt-get install libboost-all-dev
- 
完成。 
DboW3
- 
克隆代码: 1 
 2
 3cd your_floder
 git clone https://github.com/rmsalinas/DBow3.git
 cd DBow3
- 
(可选)在根目录的 CMakeLists.txt中Line53find_package(OpenCV REQUIRED)前指定OpenCV的路径:1 set(OpenCV_DIR /usr/local/opencv/opencv3413/share/OpenCV)
- 
编译代码: 1 
 2
 3
 4mkdir build
 cd build
 cmake .. -DBUILD_TESTS=ON
 make -j8
- 
运行示例: 1 
 2# 把in.yml转存为DBoW3支持的二进制格式out.dbow以加快词袋的读取速度。
 ./tests/test_iobinary in.yml out.dbow
- 
完成。 
DBoW2
- 
克隆代码: 1 
 2
 3
 4git clone https://github.com/raulmur/ORB_SLAM2.git
 cd ORB_SLAM2
 git fetch origin pull/21/head:pr-21
 git checkout pr-21后面只需要 Thirdparty/下的DBoW2文件夹。这里使用了ORB-SLAM2中pr-21的修改后的DBoW2,其中添加了加载和保存二进制词袋的功能。主要修改文件为 Thirdparty/DBoW2/DBoW2/TemplatedVocabulary.h,添加的内容为:1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79/**
 * Loads the vocabulary from a binary file
 * @param filename
 */
 bool loadFromBinaryFile(const std::string &filename);
 /**
 * Saves the vocabulary into a binary file
 * @param filename
 */
 void saveToBinaryFile(const std::string &filename) const;
 template<class TDescriptor, class F>
 bool TemplatedVocabulary<TDescriptor,F>::loadFromBinaryFile(const std::string &filename) {
 fstream f;
 f.open(filename.c_str(), ios_base::in|ios::binary);
 unsigned int nb_nodes, size_node;
 f.read((char*)&nb_nodes, sizeof(nb_nodes));
 f.read((char*)&size_node, sizeof(size_node));
 f.read((char*)&m_k, sizeof(m_k));
 f.read((char*)&m_L, sizeof(m_L));
 f.read((char*)&m_scoring, sizeof(m_scoring));
 f.read((char*)&m_weighting, sizeof(m_weighting));
 createScoringObject();
 m_words.clear();
 m_words.reserve(pow((double)m_k, (double)m_L + 1));
 m_nodes.clear();
 m_nodes.resize(nb_nodes+1);
 m_nodes[0].id = 0;
 char buf[size_node]; int nid = 1;
 while (!f.eof()) {
 f.read(buf, size_node);
 m_nodes[nid].id = nid;
 // FIXME
 const int* ptr=(int*)buf;
 m_nodes[nid].parent = *ptr;
 //m_nodes[nid].parent = *(const int*)buf;
 m_nodes[m_nodes[nid].parent].children.push_back(nid);
 m_nodes[nid].descriptor = cv::Mat(1, F::L, CV_8U);
 memcpy(m_nodes[nid].descriptor.data, buf+4, F::L);
 m_nodes[nid].weight = *(float*)(buf+4+F::L);
 if (buf[8+F::L]) { // is leaf
 int wid = m_words.size();
 m_words.resize(wid+1);
 m_nodes[nid].word_id = wid;
 m_words[wid] = &m_nodes[nid];
 }
 else
 m_nodes[nid].children.reserve(m_k);
 nid+=1;
 }
 f.close();
 return true;
 }
 template<class TDescriptor, class F>
 void TemplatedVocabulary<TDescriptor,F>::saveToBinaryFile(const std::string &filename) const {
 fstream f;
 f.open(filename.c_str(), ios_base::out|ios::binary);
 f.open ( filename.c_str (),ios_base::out|ios::binary);
 unsigned int nb_nodes = m_nodes.size();
 float _weight;
 unsigned int size_node = sizeof(m_nodes[0].parent) + F::L*sizeof(char) + sizeof(_weight) + sizeof(bool);
 f.write((char*)&nb_nodes, sizeof(nb_nodes));
 f.write((char*)&size_node, sizeof(size_node));
 f.write((char*)&m_k, sizeof(m_k));
 f.write((char*)&m_L, sizeof(m_L));
 f.write((char*)&m_scoring, sizeof(m_scoring));
 f.write((char*)&m_weighting, sizeof(m_weighting));
 for(size_t i=1; i<nb_nodes;i++) {
 const Node& node = m_nodes[i];
 f.write((char*)&node.parent, sizeof(node.parent));
 f.write((char*)node.descriptor.data, F::L);
 _weight = node.weight; f.write((char*)&_weight, sizeof(_weight));
 bool is_leaf = node.isLeaf(); f.write((char*)&is_leaf, sizeof(is_leaf)); // i put this one at the end for alignement....
 }
 f.close();
 }
- 
在 DBoW2/DBoW2/里添加demo.cpp文件,文件内容为:1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62/**
 * File: Demo.cpp
 * Date: November 2011
 * Author: Dorian Galvez-Lopez
 * Description: demo application of DBoW2
 * License: see the LICENSE.txt file
 */
 #include <iostream>
 #include <vector>
 #include <string>
 // DBoW2
 #include "FORB.h"
 #include "TemplatedVocabulary.h"
 // OpenCV
 #include <opencv2/core.hpp>
 #include <opencv2/highgui.hpp>
 #include <opencv2/features2d.hpp>
 using namespace DBoW2;
 using namespace std;
 //command line parser
 class CmdLineParser{int argc; char **argv; public: CmdLineParser(int _argc,char **_argv):argc(_argc),argv(_argv){} bool operator[] ( string param ) {int idx=-1; for ( int i=0; i<argc && idx==-1; i++ ) if ( string ( argv[i] ) ==param ) idx=i; return ( idx!=-1 ) ; } string operator()(string param,string defvalue="-1"){int idx=-1; for ( int i=0; i<argc && idx==-1; i++ ) if ( string ( argv[i] ) ==param ) idx=i; if ( idx==-1 ) return defvalue; else return ( argv[ idx+1] ); }};
 bool has_suffix(const std::string &str, const std::string &suffix) {
 std::size_t index = str.find(suffix, str.size() - suffix.size());
 return (index != std::string::npos);
 }
 int main(int argc,char **argv)
 {
 try{
 CmdLineParser cml(argc,argv);
 if (cml["-h"] || argc!=3){
 cerr<<"Usage: in.yml out.bin"<<endl;
 return -1;
 }
 DBoW2::TemplatedVocabulary<DBoW2::FORB::TDescriptor, DBoW2::FORB> voc;
 if (has_suffix(argv[1], ".txt")) {
 voc.loadFromTextFile(argv[1]);
 cout<<"loaded"<<endl;
 voc.saveToBinaryFile(argv[2]);
 cout<<"saved"<<endl;
 } else if (has_suffix(argv[1], ".yml")) {
 voc.load(argv[1]);
 cout<<"loaded"<<endl;
 voc.saveToBinaryFile(argv[2]);
 cout<<"saved"<<endl;
 } else {
 voc.loadFromBinaryFile(argv[1]);
 cout<<"loaded"<<endl;
 }
 }catch(std::exception &ex){
 cerr<<ex.what()<<endl;
 }
 return 0;
 }
- 
修改根目录 DBoW2/的CMakeLists.txt文件:1 
 2
 3
 4
 5
 6
 7# 在Line27 `find_package(OpenCV 3.0 QUIET)` 前指定OpenCV的路径:
 set(OpenCV_DIR /usr/local/opencv/opencv3413/share/OpenCV)
 # 在最后一行添加:
 add_executable(demo DBoW2/demo.cpp)
 target_link_libraries(demo DBoW2 ${OpenCV_LIBS})
 MESSAGE( STATUS "OPENCV_DIR= ${OpenCV_DIR} VERSION=${OpenCV_VERSION}" )
- 
编译代码: 1 
 2
 3
 4
 5cd DBoW2
 mkdir build
 cd build
 cmake ..
 make -j8
- 
运行示例: 1 ./demo in.yml out.bin
- 
完成。