图像对应的bag-of-words向量\(v_t\)

假设词典总共有\(W\)个单词,那么每一幅图像能够用一个\(W\)维的向量表示

\((t_1, t_2, t_3, ..., t_W)\)其中

\[t_i = \frac{n_{id}}{n_{nd}}\log\frac{N}{n_i}
\]

其中,\(n_{id}\)是单词i在当前帧图像中出现的次数,\(n_d\)是当前图像中所以单词的数目,\(n_i\)是词汇\(i\)在整个数据库中出现的次数,\(N\)是为所有图像中描述子的数目,\(\frac{n_{id}}{n_{nd}}\)表示\(tf\),\(\log\frac{N}{n_i}\)表示\(idf\),在建立视觉词袋的时候已经得到。

反向索引:描述词汇中的每一个单词在出现过的图像列表,能够加速查找具有相同词汇的图像。(用什么数据结构实现的?)存储一系列\(<I_t, v_t^i>\)(其中\(I_t\)为图像的索引,\(v_t^i\)为该单词在图像中的权重)。查询数据库时只需要比较有相同词汇的图像,加速查找,也就是说搜索图像只需要(1)词袋和(2)反向索引

具体流程如下:提取当前帧的描述子,查询字典,得到单词,查找反向索引表,得到所有具有该单词的图像。

直接索引:(存储每一幅图像的特征)对于每一幅图像\(I_t\),存储其使用的词汇的祖先节点(任何一层l)及每一个节点的局部特征\(f_{tj}\)

直接索引能够加快闭环检测的几何认证,因为只有具有相同的词汇或者在第l层有相同的祖先的关键帧才需要进行几何认证

直接索引存储每一个图像\(I_t\)中词汇的在第\(l\)层(预先给定的)的所在的节点已经所有该图像中属于该节点的描述子。

DBow2的作用:通过视觉词汇将一幅图像转换成稀疏的数字向量(能够对大量的图像进行处理)

视觉词汇是离线建立的,通过将描述子空间划分成W个视觉词汇

代码如下:

#include <iostream>
#include <vector> // DBoW2
//#include "DBoW2/DBoW2.h"
//#include <DUtils/DUtils.h>
//#include <DUtilsCV/DUtilsCV.h> // defines macros CVXX
//#include <DVision/DVision.h> #include "Thirdparty/DBoW2/DBoW2/FORB.h"
#include "Thirdparty/DBoW2/DBoW2/TemplatedVocabulary.h"
//#include "Thirdparty/DBoW2/DBoW2/FClass.h" // OpenCV
#include <opencv2/opencv.hpp>
#include "opencv2/core/core.hpp"
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <opencv2/nonfree/features2d.hpp>
//#include <opencv2/features2d/features2d.hpp> // ROS
#include <rosbag/bag.h>
#include <rosbag/view.h>
#include <ros/ros.h>
#include <sensor_msgs/Image.h>
#include <boost/foreach.hpp>
#include <cv_bridge/cv_bridge.h> #include "ORBextractor.h" #include <dirent.h>
#include <string.h> using namespace DBoW2;
using namespace DUtils;
using namespace std;
using namespace ORB_SLAM; // - - - - - --- - - - -- - - - - - /// ORB Vocabulary
typedef DBoW2::TemplatedVocabulary<DBoW2::FORB::TDescriptor, DBoW2::FORB>
ORBVocabulary; /// ORB Database
//typedef DBoW2::TemplatedDatabase<DBoW2::FORB::TDescriptor, DBoW2::FORB>
//ORBDatabase; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void extractORBFeatures(cv::Mat &image, vector<vector<cv::Mat> > &features, ORBextractor* extractor);
void changeStructureORB( const cv::Mat &descriptor,vector<bool> &mask, vector<cv::Mat> &out);
void isInImage(vector<cv::KeyPoint> &keys, float &cx, float &cy, float &rMin, float &rMax, vector<bool> &mask);
void createVocabularyFile(ORBVocabulary &voc, std::string &fileName, const vector<vector<cv::Mat> > &features); // ---------------------------------------------------------------------------- int main()
{
//Extracting ORB features from image folder
vector<std::string> filenames;
std::string folder = "/home/saodiseng/FRONTAL/"; cv::glob(folder, filenames); // initialze ORBextractor
int nLevels = 5;//6;
ORBextractor* extractor = new ORBextractor(1000,1.2,nLevels,1,20);
int nImages = filenames.size(); vector<vector<cv::Mat > > features;
features.clear();
features.reserve(nImages); //cv_bridge::CvImageConstPtr cv_ptr;
cv::Mat image; //cout << "> Using bag file: " << bagFile << endl;
cout << "> Extracting Features from " << nImages << " images..." << endl;
//BOOST_FOREACH(rosbag::MessageInstance const m, viewTopic)
for(int i = 0; i < nImages; ++i)
{
//sensor_msgs::Image::ConstPtr i = m.instantiate<sensor_msgs::Image>();
std::cout << "Processing the " << i <<" image " << std::endl;
cv::Mat src = cv::imread(filenames[i]);
imshow("View", src);
cv::waitKey(1);
if (!src.empty())
{
//cv_ptr = cv_bridge::toCvShare(i);
cv::cvtColor(src, image, CV_RGB2GRAY);
extractORBFeatures(image, features, extractor);
} } //bag.close(); cout << "... Extraction done!" << endl; // Creating the Vocabulary
// define vocabulary
const int k = 10; // branching factor
const WeightingType weight = TF_IDF;
const ScoringType score = L1_NORM;
ORBVocabulary voc(k, nLevels, weight, score); std::string vociName = "vociOmni.txt"; createVocabularyFile(voc, vociName, features); cout << "--- THE END ---" << endl; return 0;
} // ---------------------------------------------------------------------------- void extractORBFeatures(cv::Mat &image, vector<vector<cv::Mat> > &features, ORBextractor* extractor) {
vector<cv::KeyPoint> keypoints;
cv::Mat descriptorORB;
// extract
(*extractor)(image, cv::Mat(), keypoints, descriptorORB); // reject features outside region of interest
vector<bool> mask; float cx = 0; float cy = 0;
float rMin = 0; float rMax = 0;
isInImage(keypoints, cx, cy, rMin, rMax, mask); // create descriptor vector for the vocabulary
features.push_back(vector<cv::Mat>());
changeStructureORB(descriptorORB, mask, features.back());
imshow("ORBFeature", features.back().back());
} // ---------------------------------------------------------------------------- void changeStructureORB( const cv::Mat &descriptor,vector<bool> &mask, vector<cv::Mat> &out) {
for (int i = 0; i < descriptor.rows; i++) {
if(mask[i]) {
out.push_back(descriptor.row(i));
}
}
} // ---------------------------------------------------------------------------- void isInImage(vector<cv::KeyPoint> &keys, float &cx, float &cy, float &rMin, float &rMax, vector<bool> &mask) {
int N = keys.size();
mask = vector<bool>(N, false);
int num = 0;
for(int i=0; i<N; i++) {
cv::KeyPoint kp = keys[i]; float u = kp.pt.x;
float v = kp.pt.y; if(u>20 && u<320-20 && v>20 && v<240-20)
{
mask[i] = true;
num ++;
}
}
std::cout << "In image number " << num << std::endl; } // ---------------------------------------------------------------------------- void createVocabularyFile(ORBVocabulary &voc, std::string &fileName, const vector<vector<cv::Mat> > &features)
{ cout << "> Creating vocabulary. May take some time ..." << endl;
voc.create(features);
cout << "... done!" << endl; cout << "> Vocabulary information: " << endl
<< voc << endl << endl; // save the vocabulary to disk
cout << endl << "> Saving vocabulary..." << endl;
voc.saveToTextFile(fileName);
cout << "... saved to file: " << fileName << endl;
}

基于DBoW2做闭环检测

A.查询数据库

通过数据库存储和检索相似的所有图像。步骤为:首先将图像图像转换成bag-of-words向量\(v_t\)(\(tf-idf\),开头的公式),然后查找数据库中最相似的bag-of-words向量集,\(s(v_t,v_{ti})\)(多少个??)->正则化(\(s(v_t,v_{t- \Delta t}\))很小的情况单独考虑)为\(\eta (v_t, v_{t_j})=\frac{s(v_t,v_{t_j})}{s(v_t, v_{t-\Delta t})}\)->舍弃小于阈值的匹配

计算两个bag-of-word向量(两帧图像)1和v2的相似度

\[s(v_1, v_2)=1-\frac{1}{2}\vert \frac{v_1}{\vert v_1\vert}-\frac{v_2}{\vert v_2\vert}\vert
\]

B.匹配聚类

为了防止时间上很近的关键帧之间相互竞争,将检索得到的时间戳相差比价小的帧聚成island并将它们看做一个匹配,一系列匹配可以转换成一个匹配\(<v_t, V_{T_i}>\),island也根据评分排序,选择最高的。Island的得分为$ H(v_t, V_{T_i})=\sum_{j=n_i}^{m_i}\eta(v_t, v_{t_j})$。

C.Temporal consistency(时间一致性)

检测\(V_{T_i}\)和以前的查询结果\(<v_{t-\Delta t},V_{T_j}>\)的\(T_i,T_j\)时间一致性,\(<v_t,V_{T_i}>\)必须和k个以前的匹配查询结果\(<v_{t-\Delta t},V_{T_j}>\)一致(\(k\)个以前的匹配的island时间\(T_i\)接近重叠),一旦通过一致性检验,则选取island\(V_{T_i}\)中得分最高的词汇\(v_{t'}\)

D.有效的几何一致性

用RANSAC方法得到\(I_t\)和\(I_t'\)的基础矩阵(至少12个对应点),查找对应的特征点(brute force和k-d tree方法)

使用直接索引近似最近邻(字典树中属于第l层的同一个节点,\(l\)提前设定,是速度和recall的折中)具体做法为:

(1)往数据库中加入图像时,在直接索引中存储节点和一些特征的对;

(2)在得到图像间的对应点时,在直接索引中查找只有在第l层属于同一个节点的描述子,并进行比较。这个方法能够提高对应点计算,l是提前固定的是对应点数目和进行该操作的时间的折中。

DBoW2应用的更多相关文章

  1. DBoW2库介绍

    DBoW2库是University of Zaragoza里的Lopez等人开发的开源软件库. 由于在SLAM回环检测上的优异表现(特别是ORB-SLAM2),DBoW2库受到了广大SLAM爱好者的关 ...

  2. DBoW2算法原理介绍

    本篇介绍DBoW2算法原理介绍,下篇介绍DBoW2的应用. DBow2算法 DBow2是一种高效的回环检测算法,DBOW2算法的全称为Bags of binary words for fast pla ...

  3. 视觉slam闭环检测之-DBoW2 -视觉词袋构建

    需要准备的知识点:http://www.cnblogs.com/zjiaxing/p/5616653.html      http://www.cnblogs.com/zjiaxing/p/56166 ...

  4. BoW算法及DBoW2库简介(二)

    一.BoW算法 用OpenCV实现了最简单的BoW算法进行了一次小规模的图像检索任务,使用UKbench数据库,算法原理和网上的描述差不多,使用K-means算法进行聚类,这里使用KDTree算法进行 ...

  5. BoW算法及DBoW2库简介

    由于在ORB-SLAM2中扩展图像识别模块,因此总结一下BoW算法,并对DBoW2库做简单介绍. 1. BoW算法 BoW算法即Bag of Words模型,是图像检索领域最常用的方法,也是基于内容的 ...

  6. DBoW2 词袋模型笔记

    DBoW算法用于解决Place Recognition问题,ORB-SLAM,VINS-Mono等SLAM系统中的闭环检测模块均采用了该算法.来源于西班牙的Juan D. Tardos课题组. 主要是 ...

  7. 【AR实验室】mulberryAR : ORBSLAM2+VVSION

    本文转载请注明出处 —— polobymulberry-博客园 0x00 - 前言 mulberryAR是我业余时间弄的一个AR引擎,目前主要支持单目视觉SLAM+3D渲染,并且支持iOS端,但是该引 ...

  8. ORB-SLAM(六)回环检测

    上一篇提到,无论在单目.双目还是RGBD中,追踪得到的位姿都是有误差的.随着路径的不断延伸,前面帧的误差会一直传递到后面去,导致最后一帧的位姿在世界坐标系里的误差有可能非常大.除了利用优化方法在局部和 ...

  9. ORB-SLAM(二)性能

    ORB-SLAM程序提供了运行Monocular.Stereo和RGBD数据的程序.编译成功后,可以通过运行TUM的标准数据来验证程序是否成功.如果想自己测试一些数据,可以通过OpenCV提供的接口调 ...

随机推荐

  1. poj3728 商务旅行

    [Description]小 T 要经常进行商务旅行,他所在的国家有 N 个城镇,标号为 1,2,3,...,N,这 N 个城镇构成一棵树.每个城镇可以买入和卖出货物,同一城镇买入和卖出的价格一样,小 ...

  2. [SCOI2008]配对

    题目描述 你有 n 个整数Ai和n 个整数Bi.你需要把它们配对,即每个Ai恰好对应一个Bp[i].要求所有配对的整数差的绝对值之和尽量小,但不允许两个相同的数配对.例如A={5,6,8},B={5, ...

  3. Linux上安装Libssh2

    由于项目需要使用libssh2,在安装时,遇到一些问题,发现网上的都是互相抄,把自己遇到的问题,记下来,希望可以帮助到别人,自己下次使用时候,也方便查找,节约时间. 安装的流程: 1.下载源码,wge ...

  4. 2015 多校联赛 ——HDU5360(贪心+优先队列)

    Sample Input 4 8 4 1 3 2 2 1 0 3 5 3 6 4 2 1 7 6 8 3 3 2 0 5 0 3 6 4 5 2 7 7 6 7 6 8 2 2 3 3 3 0 0 2 ...

  5. bzoj1858[Scoi2010]序列操作 线段树

    1858: [Scoi2010]序列操作 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 3079  Solved: 1475[Submit][Statu ...

  6. SSD:TensorFlow中的单次多重检测器

    SSD:TensorFlow中的单次多重检测器 SSD Notebook 包含 SSD TensorFlow 的最小示例. 很快,就检测出了两个主要步骤:在图像上运行SSD网络,并使用通用算法(top ...

  7. 基于GCC的openMP学习与测试

    (一).openMP简述 Open Multiprocessing (OpenMP) 框架是一种功能极为强大的规范,可以帮助您利用 C.C++ 和 Fortran 应用程序中的多个核心带来的好处,是基 ...

  8. 关于惠普hp服务器开机时F10菜单变成F10 Function Disabled的解决方法

    今天笔者由于在Intelligent Provisioning智能配置里不小心将"启动Intelligent Provisioning"选项钩选成禁用了,如下 结果就造成,在之后服 ...

  9. 值得珍藏的HTTP协议详解

    转自:http://www.cnblogs.com/li0803/archive/2008/11/03/1324746.html 引言 HTTP是一个属于应用层的面向对象的协议,由于其简捷.快速的方式 ...

  10. 阿里架构师带你深入浅出jvm

    本文跟大家聊聊JVM的内部结构,从组件中的多线程处理,JVM系统线程,局部变量数组等方面进行解析 JVM JVM = 类加载器(classloader) + 执行引擎(execution engine ...