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

2023-05-16

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

利用 BoVW 表示图像,将图像进行结构化描述。BoVW思想是将图像特征整合成视觉单词,将图像特征空间转化成离散的视觉字典。将新的图像特征映射到视觉字典中最近邻视觉字典,再通过计算视觉字典间距离计算图像的相似度,从而完成识别、图像分类、检索等任务。
基于图像的闭环检测系统,将当前采集的图像和之前数据集中所有采集到的图像进行比较。每幅图像通过该图像的显著视觉特征描述,并用于图像相似性比较。描述符提取图像特征,将图像技术分享表示为一个 n维的描述符集合:技术分享

提取特征点后,每幅图像由一系列的视觉单词组成。每个orb 描述符提取的特征点 技术分享都 关 联 到 视 觉 字 典 中 的 一 个 视 觉 单 词技术分享, 视 觉 字 典 表 示 为 :技术分享。视觉字典 V通过 BoVW 建模方法,对相似描述符聚类进行构建。 每一个视觉单词的 orb描述向量都被认为是一个关联的视觉词表。

在构建好视觉字典之后,对群集进行中心化。通过在群集中心构架 K-D 树,并执行最近邻knn矢量对所有描述符量子化,实现对群集的简化。


测量两幅图像技术分享技术分享的相似度,可以通过计算它们之间的余弦距离获得。每一幅图像 技术分享由不同权重技术分享的词汇 技术分享聚集构成,权重 技术分享是每个词汇在全部图像集中发生的频率。 每个词汇的权重由式:技术分享

式中,N 是存储的所有图像,技术分享是 技术分享中包含图像的数量。如果视觉字典中包含|V|个不同的词汇,可以形成图像的矢量为:技术分享

技术分享

得到每个词汇的权重后,即可求出整幅图像的权重。再利用相似函数计算图像 技术分享技术分享间的相似度,相似函数如式:

技术分享

Bag of Words 字典建立:
1、从训练图像中离线抽取特征
2、将抽取的特征用 k-means++ 算法聚类,将描述子空间划分成 k 类 
3、将划分的每个子空间,继续利用 k-means++ 算法做聚类 
4、按照上述循环,将描述子建立树形结构,如下图所示:

技术分享

字典树在建立过程中,每个叶子也就是每个 word 记录了该 word 在所有的训练图像中出现的频率出现的频率越高,表示这个 word 的区分度越小,频率的计算公式如下:

技术分享


在线更新字典树:

当在字典树中需要插入一幅新图像技术分享图像中提取的特征描述子按照 Hamming 距离从字典树的根部节点开始逐级向下到达叶子节点,可以计算每个叶子节点也就是每个 word 在图像 技术分享中的出现频率:

技术分享


其中 niIt表示 word 在图像中出现的次数 nIt表示图像中描述子的总数在树构建的过程中每个叶子节点存储了 inverse index(倒排挡索引),存储了到达叶子节点的图像 It的 ID 和 word 在图像 It 描述 vector 中第 i 维的值: vit=tf(i,It)×idf(i)
对于一幅图像所有的描述子,做上述操作,可以得到每个 word 的值,将这些值构成图像的描述向量 vt。
对两幅图像比较计算其相似度时,两幅图像相似度计算公式如下:

技术分享 

两幅图像越相似得分越高。字典树除了存储了 inverse index,还存储了 direct index 如上图所示,direct index 方便两幅图像特征搜索,建立特征之间的对应,计算两帧之间的位姿转换。
Database query
由于在计算相似度时,相似度的大小和字典树、图像等有一定关系,这里采用归一化的方式,消除这两种因素的影响:

技术分享

归一化相似度计算公式如下: 其中 vt−Δt表示上一帧图像,上式含义是上一帧图像和当前帧图像是最为相似度的,用和上一帧图像计算的相似度来归一化和字典树中图像计算的相似度。
当 s(vt,vt−Δt)较小时(机器人做旋转时),会把总体的得分拉的很高,论文里面剔除这种因素,选取阈值 α,当前帧和上一帧图像相似度小于 α 时不做回环检测。


Matching group:

假设图像 vt 和图像 vni相似度很大,那么和图像 vni周围的图像也会有很高的相似度,这里将相邻的得分都很高的图像 group 在一起构成 island,得分是 group 中图像得分的总和。


Temporal consistency:
假设图像 vt 和 island Vt1 之间相似度很大,那么图像 vt?kΔt 和 Vtk像素度也应该很大(k 小于 一定值),相当于两串图像间会有 overlap,利用这个条件作为 consistency 的约束。


Efficient geometrical consistency:
对于一幅新图像 Ii,用字典树建立对图像的描述,并且计算和字典树中以前存储的图像之间的得分。


inverse index 加快待比较的图像搜索速度
由于 inverse index 存储了哪些图像也到达该叶子节点,在选择待比较的图像时,只需要比较到达相同叶子节点的图像,不需要和存储的每幅进行比较,从而加快比较速度。


direct index 加快特征比较速度 
假设图像 Ii 和 Ij得分最高,在两幅图像特征匹配时,只需要比较 direct index 中属于同一个 node 的图像特征,node 指字典树的一层,如果是叶子节点层,那么选择是同一个 word 的特征做匹配。


#include <iostream>
#include <vector>

// DBoW2
#include "DBoW2.h" // defines Surf64Vocabulary and Surf64Database

#include <DUtils/DUtils.h>
#include <DVision/DVision.h>

// OpenCV
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/xfeatures2d/nonfree.hpp>


using namespace DBoW2;
using namespace DUtils;
using namespace std;

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

void loadFeatures(vector<vector<vector<float> > > &features);
void changeStructure(const vector<float> &plain, vector<vector<float> > &out,
  int L);
void testVocCreation(const vector<vector<vector<float> > > &features);
void testDatabase(const vector<vector<vector<float> > > &features);


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

// number of training images
const int NIMAGES = 4;

// extended surf gives 128-dimensional vectors
const bool EXTENDED_SURF = false;

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

void wait()
{
  cout << endl << "Press enter to continue" << endl;
  getchar();
}

// ----------------------------------------------------------------------------

int main()
{
  vector<vector<vector<float> > > features;
  loadFeatures(features);
  testVocCreation(features);
  wait();

  testDatabase(features);

  return 0;
}

// ----------------------------------------------------------------------------

void loadFeatures(vector<vector<vector<float> > > &features)
{
  features.clear();
  features.reserve(NIMAGES);
  cv::Ptr<cv::xfeatures2d::SURF> surf = cv::xfeatures2d::SURF::create(400, 4, 2, EXTENDED_SURF);
  cout << "Extracting SURF features..." << endl;
  for(int i = 0; i < NIMAGES; ++i)
  {
    stringstream ss;
    ss << "images/image" << i << ".png";
    cv::Mat image = cv::imread(ss.str(), 0);
    cv::Mat mask;
    vector<cv::KeyPoint> keypoints;
    vector<float> descriptors;

    surf->detectAndCompute(image, mask, keypoints, descriptors);

    features.push_back(vector<vector<float> >());
    changeStructure(descriptors, features.back(), surf->descriptorSize());
  }
}

// ----------------------------------------------------------------------------

void changeStructure(const vector<float> &plain, vector<vector<float> > &out,
  int L)
{
  out.resize(plain.size() / L);
  unsigned int j = 0;
  for(unsigned int i = 0; i < plain.size(); i += L, ++j)
  {
    out[j].resize(L);
    std::copy(plain.begin() + i, plain.begin() + i + L, out[j].begin());
  }
}

// ----------------------------------------------------------------------------

void testVocCreation(const vector<vector<vector<float> > > &features)
{
  // Creates a vocabulary from the training features, setting the branching
    factor and the depth levels of the tree and the weighting and scoring
   schemes      * Creates k clusters from the given descriptors with some seeding algorithm.
  
  const int k = 9;
  const int L = 3;
  const WeightingType weight = TF_IDF;
  const ScoringType score = L1_NORM;

  Surf64Vocabulary voc(k, L, weight, score);

  cout << "Creating a small " << k << "^" << L << " vocabulary..." << endl;
  voc.create(features);
  cout << "... done!" << endl;

  cout << "Vocabulary information: " << endl
  << voc << endl << endl;

  // lets do something with this vocabulary
  cout << "Matching images against themselves (0 low, 1 high): " << endl;
  BowVector v1, v2;
  for(int i = 0; i < NIMAGES; i++)
  {
    //Transforms a set of descriptores into a bow vector
    voc.transform(features[i], v1);
    for(int j = 0; j < NIMAGES; j++)
    {
      voc.transform(features[j], v2);

      double score = voc.score(v1, v2);
      cout << "Image " << i << " vs Image " << j << ": " << score << endl;
    }
  }

  // save the vocabulary to disk
  cout << endl << "Saving vocabulary..." << endl;
  voc.save("small_voc.yml.gz");
  cout << "Done" << endl;
}

// ----------------------------------------------------------------------------

void testDatabase(const vector<vector<vector<float> > > &features)
{
  cout << "Creating a small database..." << endl;

  // load the vocabulary from disk
  Surf64Vocabulary voc("small_voc.yml.gz");

  Surf64Database db(voc, false, 0); // false = do not use direct index
  // (so ignore the last param)
  // The direct index is useful if we want to retrieve the features that 
  // belong to some vocabulary node.
  // db creates a copy of the vocabulary, we may get rid of "voc" now

  // add images to the database
  for(int i = 0; i < NIMAGES; i++)
  {
    db.add(features[i]);
  }

  cout << "... done!" << endl;

  cout << "Database information: " << endl << db << endl;

  // and query the database
  cout << "Querying the database: " << endl;

  QueryResults ret;
  for(int i = 0; i < NIMAGES; i++)
  {
    db.query(features[i], ret, 4);

    // ret[0] is always the same image in this case, because we added it to the 
    // database. ret[1] is the second best match.

    cout << "Searching for Image " << i << ". " << ret << endl;
  }

  cout << endl;

  // we can save the database. The created file includes the vocabulary
  // and the entries added
  cout << "Saving database..." << endl;
  db.save("small_db.yml.gz");
  cout << "... done!" << endl;

  // once saved, we can load it again  
  cout << "Retrieving database once again..." << endl;
  Surf64Database db2("small_db.yml.gz");
  cout << "... done! This is: " << endl << db2 << endl;
}









本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

视觉slam闭环检测之-DBoW2 -视觉词袋构建 的相关文章

  • SLAM方法汇总

    原文 http blog csdn net smartxxyx article details 53068855 目录 SLAM概述 SLAM一般处理流程包括track和map两部分 所谓的track是用来估计相机的位姿 也叫front e
  • 《视觉SLAM十四讲》学习笔记-第四讲部分习题的证明思路

    1 验证SO 3 SE 3 和Sim 3 关于乘法成群 证明 先看SO 3 定义为 SO 3 R R3 3 RR I det R 1 S O 3 R
  • No rule to make target

    No rule to make target 引言 解决方法 引言 报错 No rule to make target Thirdparty g2o lib libg2o so needed by lib libygz SLAM so 停止
  • 从0.3开始搭建LeGO-LOAM+VLP雷达+小车实时建图(保姆级教程,小白踩坑日记)

    背景 SLAM小白 因为项目需要花了两天时间编译代码 连接雷达实现了交互 踩了很多坑 简单记录一下 让后面感兴趣的朋友少走点弯路 肯定有很多不专业的 错误的地方 还请大家不吝赐教 噗通 也可以见知乎 https zhuanlan zhihu
  • 【SLAM】卡尔曼滤波(Kalman Filter)

    卡尔曼滤波 Kalman filter 一种利用线性系统状态方程 通过系统输入输出观测数据 对系统状态进行最优估计的算法 由于观测数据中包括系统中的噪声和干扰的影响 所以最优估计也可看作是滤波过程 卡尔曼滤波器的原理解释如下 首先 我们先要
  • 速腾聚创雷达最新驱动安装(包含ring和timestamp)运行lio-sam

    记录一下搞slam的过程 ring和timestamp 最近想跑lio sam 需要用到ring和timestamp两个参数 lio sam作者用的velodyne雷达是带这两个参数的 但是rs雷达的老版驱动录制的点云包没有这两个参数 在g
  • Sophus使用记录

    sophus库是一个基于Eigen的C 李群李代数库 可以用来方便地进行李群李代数的运算 头文件 主要用到以下两个头文件 include
  • 从零开始一起学习SLAM(9)不推公式,如何真正理解对极约束?

    文章目录 对极几何基本概念 如何得到极线方程 作业 此文发于公众号 计算机视觉life 原文链接 从零开始一起学习SLAM 不推公式 如何真正理解对极约束 自从小白向师兄学习了李群李代数和相机成像模型的基本原理后 感觉书上的内容没那么难了
  • 【SLAM】libQGLViewer:VS 2019 + Qt 5.14.2 + Win 10 配置

    libQGLViewer 2 7 2 VS 2019 Qt 5 14 2 Win 10 配置 注意 这次配置没有完全成功 编译25个成功 一个失败 失败的是 qglviewerplugin qglviewerplugin 是一个可选控件 不
  • vscode配置eigen3

    目录 1 头文件包含 2 c cpp properties json 3 CMakeList txt 4 完整代码 1 头文件包含 Eigen 核心部分 include
  • 关于GPS、惯导、视觉里程计的几个定义

    1 首先写几个定义 惯性导航系统 Inertial Navigation System INS 全球定位卫星系统 Global Navigation Satellite System GNSS GNSS 包括全球定位系统 Global Po
  • IMU预积分的一些理解

    IMU预积分 算是比较简单的一个算法 无奈网上找到的资料都讲的晦涩难懂 看明白了也觉得不过如此 讲一下我的理解 整体流程 1 推导IMU离散运动方程 2 根据离散运动方程 进行预积分 并将预积分的误差项拆分出来 因为我们在定义误差的时候 有
  • 动态场景下基于实例分割的SLAM(毕业设计开题及语义分割部分)

    动态场景下基于实例分割的SLAM 毕业论文设计思路及流水 前言 今年选了个比较难的毕设题目 这里记录一下自己思路和流程 为之后的学弟学妹 划掉 铺个方向 会按日期不定期的更新 一 开题 2019 12 24 考研前选择课题是 利用深度学习对
  • SLAM--三角测量SVD分解法、最小二乘法及R t矩阵的判断

    目录 一 三角测量 方法一 SVD分解法的推导 方法二 最小二乘法求解 二 ORB SLAM2 三角测量源码 三 利用Eigen源码实现三角测量 方法一 SVD分解法 方法二 最小二乘法求解 速度最快 方法三 利用OpenCV自带函数 四
  • 用Eigen库练习代数运算方式以便后续对刚体旋转和移动做基础

    include
  • 无人车

    1 无人车四大核心技术 自动驾驶实际包含三个问题 一是我在哪 二是我要去哪 三是如何去 第一个问题是环境感知和精确定位 无人车需要的是厘米级定位 厘米级定位是无人驾驶的难点之一 不光是车辆本身的语义级定位 还有一个绝对坐标定位 第二个问题是
  • LeGO-LOAM中的数学公式推导

    LeGO LOAM是一种在LOAM之上进行改进的激光雷达建图方法 建图效果比LOAM要好 但是建图较为稀疏 计算量也更小了 本文原地址 wykxwyc的博客 github注释后LeGO LOAM源码 LeGO LOAM NOTED 关于代码
  • LOAM算法详解

    激光SLAM 帧间匹配方法 Point to Plane ICP NDT Feature based Method 回环检测方法 Scan to Scan Scan to Map LOAM创新点 定位和建图的分离 里程计模块 高频低质量的帧
  • Eigen几何模块的使用方法

    include
  • 什么是深度学习的无监督学习与有监督学习

    无监督学习 深度学习中的无监督学习方法是一种训练算法 它在没有标注输出的情况下从输入数据中学习模式和特征 这种方法的核心是探索和理解数据的内在结构和分布 而不是通过已知的输出来指导学习过程 无监督学习在深度学习领域有许多不同的形式和应用 以

随机推荐

  • 【设计模式】我终于读懂了迭代器模式。。。

    看一个具体的需求 编写程序展示一个学校院系结构 xff1a 需求是这样 要在一个页面中展示出学校的院系组成 xff0c 一个学校有多个学院 xff0c 一个学院有多个系 如图 xff1a 传统的设计方案 类图 传统的方式的问题分析 将学院看
  • stm32f103zet6引脚说明

    stm32f103zet6是一种嵌入式 微控制器的集成电路 xff08 IC xff09 xff0c 是由ST公司开发的STM32F1系列的其中一种 xff0c 芯体尺寸是32位 xff0c 速度是72MHz xff0c 程序存储器容量是2
  • 连接postgres特别消耗cpu资源而引发的PostgreSQL性能优化考虑 .

    由于是开发阶段 xff0c 所以并没有配置postgres的参数 xff0c 都是使用安装时的默认配置 xff0c 以前运行也不见得有什么不正常 xff0c 可是前几天我的cpu资源占用突然升高 查看进程 xff0c 发现有一个postgr
  • Call Component Interface from AE

    Local File amp Source amp fileLog Local string amp strLine amp FilePath amp EmplStatus Local array of string amp Item Lo
  • javaweb各种框架组合案例(六):springboot+spring data jpa(hibernate)+restful

    一 介绍 1 springboot是spring项目的总结 43 整合 当我们搭smm ssh ssjdbc等组合框架时 xff0c 各种配置不胜其烦 xff0c 不仅是配置问题 xff0c 在添加各种依赖时也是让人头疼 xff0c 关键有
  • vue 如何取数组的某个值,vue提取json中,某一个元素的值,并且用value组成一个数组...

    这个是瞎捣鼓出来的 需要是做一个下拉select选择框 xff0c 因为用的是vant xff0c 所以代码是这样的 readonly clickable name 61 34 areaCode 34 value 61 34 value 3
  • 嵌入式开发的职业前景分析

    嵌入式开发的职业前景分析 http developer 51cto com 2010 01 06 09 53 佚名 互联网 我要评论 5 嵌入式开发由于应用广泛而有着很大的发展潜力 xff0c 但这方面的人才目前并不多 本文介绍了嵌入式开发
  • 无人机炸机再不怕,这款降落伞带你飞!

    以色列公司研究出一款可以保证无人机安全飞行的降落伞 无人机的出现开辟了新的航空天地 xff0c 人们在享受它带来方便的同时 xff0c 也对它炸机的危险表示深深的担忧 为什么人们对无人机炸机会有那么深的顾虑 xff1f 其一是炸机的诱因很多
  • 谈谈我学习图像处理的经历与收获

    谈谈我学习图像处理的经历与收获 大概是在2011年年底的时候 xff0c 我在家里整理以前的IT书籍 发现我买的几本图像处理的书 xff0c 于是我又随手翻了翻 xff0c 发现自己还是挺感兴趣的 xff0c 就直接放到书架上了 xff0c
  • GPS数据读取与处理

    GPS数据读取与处理 GPS模块简介 SiRF芯片在2004年发布的最新的第三代芯片SiRFstar III xff08 GSW 3 0 3 1 xff09 xff0c 使得民用GPS芯片在性能方面登上了一个顶峰 xff0c 灵敏度比以前的
  • 想自己造无人机吗?Intel推出基于 Linux x86的自助无人机开发板

    英特尔开发论坛会议消息 xff1a 英特尔手里不乏新的和升级的硬件工具包 xff0c 从无人机自助套件到各种大气的物联网包不等 xff0c 工程玩家利用这些无人机硬件可以自己组装无人机 Aero组装套件是一个最好玩的包 xff0c 即日起接
  • 回眸--从高考到程序猿

    杨过等待了小龙女16年 xff0c 终成眷属 xff0c 从此郎情妾意双双在浪迹天涯 而参加高考后也奋斗了16年 xff0c 梦中却总是想起回眸后那流泪的脸 19年前 xff0c 我们那里流行的不是高考 xff0c 而是中考 xff0c 因
  • istanbul —— JavaScript 代码覆盖率检查工具

    istanbul 是一个 JavaScript 的代码覆盖率检查工具 https yqfile alicdn com e6425e746f3af8cef9c3e64dde6ff2f9a336929d png 34 gt 文章转载自 开源中国
  • vscode怎么修改颜色主题里的某种颜色

    我习惯用深色主题 齿轮 gt 颜色主题 gt monokai是我的菜 比较精神 又不刺眼 但是这个主题的注释的颜色太浅了 几乎和背景重合 注释很重要 能体现和记录自己的代码思路 所有 改变一下comment的颜色很重要 在vscode图标右
  • 双系统linux安装分区详解,win10环境下安装Ubantu双系统(详解图解)

    1 准备工作 xff1a 先去ubantu官网 https www ubuntu com download 去下载ubantu镜像 根据自己的实际情况选择32位的或者64位的 xff0c 现在的电脑大部分已经都是4g内存了 xff0c 我推
  • Ubuntu - 硬盘分区、格式化、自动挂载配置

    Ubuntu系统的硬盘空间不够用了 xff0c 需要增加新的硬盘扩容 将硬盘分区 格式化 自动挂载配置的整个过程记下来 xff0c 备忘 运行环境 Enviroment Ubuntu 10 10 一 硬盘分区 Hard disk add n
  • linux下安装F-prot杀毒软件

    一 f xff0d prot的安装 1 首先我们要创建一个带有超级权限的用户 sudo passwa root 2 su 切换用户 3 下载F prot http www f prot com download home user down
  • 使用Github自动构建Docker

    原文链接 xff1a http yangbingdong com 2017 docker automated build by github 一开始玩Docker总是用别人的镜像确实很爽歪歪 But xff0c 如果要定制个性化的Image
  • 常用 API 函数(6): 菜单函数

    AppendMenu在指定的菜单里添加一个菜单项CheckMenuItem复选或撤消复选指定的菜单条目CheckMenuRadioItem指定一个菜单条目被复选成 34 单选 34 项目CreateMenu创建新菜单CreatePopupM
  • 视觉slam闭环检测之-DBoW2 -视觉词袋构建

    视觉slam闭环检测之 DBoW2 视觉词袋构建 利用 BoVW 表示图像 xff0c 将图像进行结构化描述 BoVW思想是将图像特征整合成视觉单词 xff0c 将图像特征空间转化成离散的视觉字典 将新的图像特征映射到视觉字典中最近邻视觉字