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

简介: 视觉slam闭环检测之-DBoW2 -视觉词袋构建 利用 BoVW 表示图像,将图像进行结构化描述。BoVW思想是将图像特征整合成视觉单词,将图像特征空间转化成离散的视觉字典。将新的图像特征映射到视觉字典中最近邻视觉字典,再通过计算视觉字典间距离计算图像的相似度,从而完成识别、图像分类、检索等任务。基于图像的闭环检测系统,将当前采集的图像和之前数据集中所有采集到的图像进行比较。每幅图像通

视觉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;
}









相关文章
|
6月前
|
机器学习/深度学习 监控 算法
计算机视觉实战项目(图像分类+目标检测+目标跟踪+姿态识别+车道线识别+车牌识别)
计算机视觉实战项目(图像分类+目标检测+目标跟踪+姿态识别+车道线识别+车牌识别)
|
机器学习/深度学习 传感器 编解码
一文详解视觉Transformer在CV中的现状、趋势和未来方向(分类/检测/分割/多传感器融合)(中)
本综述根据三个基本的CV任务和不同的数据流类型,全面调查了100多种不同的视觉Transformer,并提出了一种分类法,根据其动机、结构和应用场景来组织代表性方法。由于它们在训练设置和专用视觉任务上的差异,论文还评估并比较了不同配置下的所有现有视觉Transformer。此外,论文还揭示了一系列重要但尚未开发的方面,这些方面可能使此类视觉Transformer能够从众多架构中脱颖而出,例如,松散的高级语义嵌入,以弥合视觉Transformer与序列式之间的差距。最后,提出了未来有前景的研究方向。
一文详解视觉Transformer在CV中的现状、趋势和未来方向(分类/检测/分割/多传感器融合)(中)
|
17天前
|
自动驾驶 计算机视觉
单目三维检测实时泛化,纯视觉自动驾驶鲁棒感知方法入选ECCV 2024
【10月更文挑战第25天】单目三维物体检测在自动驾驶领域具有重要应用价值,但训练数据和测试数据的分布差异会影响模型性能。为此,研究人员提出了一种名为“单目测试时适应”(MonoTTA)的方法,通过可靠性驱动的适应和噪声防护适应两个策略,有效处理测试时的数据分布变化,提高模型在未知数据上的泛化能力。实验结果表明,MonoTTA方法在KITTI和nuScenes数据集上显著提升了性能。
19 2
|
6月前
|
机器学习/深度学习 算法 数据挖掘
计算机视觉五大核心研究任务全解:分类识别、检测分割、人体分析、三维视觉、视频分析
计算机视觉五大核心研究任务全解:分类识别、检测分割、人体分析、三维视觉、视频分析
598 1
|
5月前
|
机器学习/深度学习 算法
【机器学习】剪贴画图像等文本引导运动生成技术革新
【机器学习】剪贴画图像等文本引导运动生成技术革新
49 1
|
6月前
|
传感器 机器学习/深度学习 自动驾驶
【多模态融合】CRN 多视角相机与Radar融合 实现3D检测、目标跟踪、BEV分割 ICCV2023
本文介绍使用雷达与多视角相机融合,实现3D目标检测、3D目标跟踪、道路环境BEV分割,它是来自ICCV2023的。CRN,全称是Camera Radar Net,是一个多视角相机-雷达融合框架。 通过融合多视角相机和雷达的特性,生成语义丰富且空间精确的BEV特征图。实现3D物体检测、跟踪和BEV分割任务。
519 1
|
6月前
|
机器学习/深度学习 存储 算法
基于多模态融合与图神经网络的用户精准感知系统研究
基于多模态融合与图神经网络的用户精准感知系统研究
206 0
|
6月前
|
机器学习/深度学习 算法 API
视觉智能平台如何识别图像的某个特征?
视觉智能平台如何识别图像的某个特征?
65 0
|
算法 TensorFlow 算法框架/工具
基于Aidlux平台的人脸关键点检测以及换脸算法
Face Mesh:468个人脸部关键点精确定位并支持多个人同时检测,支持关键点3D坐标。 Face Swap:超好玩的换脸算法,把明星的脸融合到你的身体上,让你也有星范。
|
机器学习/深度学习 传感器 编解码
一文详解视觉Transformer在CV中的现状、趋势和未来方向(分类/检测/分割/多传感器融合)(上)
本综述根据三个基本的CV任务和不同的数据流类型,全面调查了100多种不同的视觉Transformer,并提出了一种分类法,根据其动机、结构和应用场景来组织代表性方法。由于它们在训练设置和专用视觉任务上的差异,论文还评估并比较了不同配置下的所有现有视觉Transformer。此外,论文还揭示了一系列重要但尚未开发的方面,这些方面可能使此类视觉Transformer能够从众多架构中脱颖而出,例如,松散的高级语义嵌入,以弥合视觉Transformer与序列式之间的差距。最后,提出了未来有前景的研究方向。
一文详解视觉Transformer在CV中的现状、趋势和未来方向(分类/检测/分割/多传感器融合)(上)