视觉SLAM十四讲:回环检测-知识点+代码

2023-05-16

目录

  • 基于外观的几何关系
    • 1. 基础知识
      • 1.1 准确率和召回率
      • 1.2 词袋模型
      • 1.3 字典
      • 1.4 字典的数据结构
      • 1.5 相似度的计算
      • 1.6 相似度评分的处理
      • 1.7 检测回环后的验证
    • 2. 实践与代码解析
      • 2.1 创建字典
      • 2.2 相似度计算

回环检测的关键是,如何有效地检测出相机经过同一个地方。如果能够成功地检测到这件事,就可以为后端的位姿图提供更多有效的数据,使之得到更好的估计,特别是得到一个全局一致的估计。我们可以利用回环检测进行重定位

回环检测的方法:

  1. 暴力匹配。
    对任意两幅图像都进行特征匹配,根据正确匹配的数量确定哪两幅图像存在关联。
    缺点:计算复杂度高,检测效率低。
  2. 基于里程计的几何关系
    当发现相机运动到之前的某一个位置附近时,检测有没有回环关系。
    缺点:累计误差存在,无法正确发现“运动回之前位置”的事实——>附近?累计误差很大的情况下?
  3. 基于外观的几何关系
    仅依据两幅图像的相似性确定回环检测关系,摆脱了累计误差,使回环检测模块成为SLAM系统中相对独立的模块。

基于外观的几何关系

1. 基础知识

1.1 准确率和召回率

在这里插入图片描述
准确率:算法检测到“是回环”的结果里,有多少个真的是回环。(针对结果)

P r e c i s i o n = T P / ( T P + F P ) Precision = TP/(TP+FP) Precision=TP/(TP+FP)

召回率:有多少个真的回环,被算法检测到“是回环”了。(针对真实样本)

R e c a l l = T P / ( T P + F N ) Recall = TP/(TP+FN) Recall=TP/(TP+FN)

slam对准确率要求更高,因为如果实际不是回环,算法却判断为回环(即假阳性)会在后端Pose Graph中添加根本错误的边,严重影响算法结果。而召回率低一些关系不大,因为大不了没检测到回环,后端的优化有点漂移和累积误差而已。

1.2 词袋模型

词袋(BoW),强调的是词的有无,而不是顺序。目的是用“图像上有哪几种特征”来描述一幅图像。

大体步骤:

  1. BoW中的单词(某一类特征的组合,不等于特征点)——>字典
  2. 用单词出现的情况(或直方图)描述整幅图像——>向量描述图像
  3. 比较相似度,定义方式不唯一。
    在这里插入图片描述

字 典 如 何 生 成 ? ? ? \red{字典如何生成???}

1.3 字典

字典生成问题类似于聚类问题,eg.想要一个有k个单词的字典,每个单词可以看作是局部相邻特征点的集合——>K均值算法!将已经提取的大量特征点聚类成一个含有k个单词的字典。

K-means大体步骤:
在这里插入图片描述
如 何 根 据 图 像 中 某 个 特 征 点 查 找 字 典 中 相 应 的 单 词 ? ? \red{如何根据图像中某个特征点查找字典中相应的单词??}

解决办法:K叉树

1.4 字典的数据结构

为提升查找效率,假定有N个特征点,希望构建一个深度为d,每次分叉为k的树——k叉树(叶子层是单词,中间节点的作用是方便查找)
在这里插入图片描述
根据已知特征查找单词时,可逐层对比,找到对应的单词。
在这里插入图片描述
图 像 在 字 典 中 查 找 出 相 应 的 单 词 , 如 何 表 示 图 像 ? 两 图 像 的 相 似 度 如 何 计 算 ? \red{图像在字典中查找出相应的单词,如何表示图像?两图像的相似度如何计算?}

1.5 相似度的计算

需要给叶子节点增加权重—对单词的区分性或重要性加以评估,给他们不同的权值以起到更好的效果。

加权方式:TF-IDF

基本思想:
TF:某单词在一幅图像中经常出现,则区分度高。IDF:某单词在字典中出现频率越低,分类图像时区分度越高。

计算:
TF:假设单幅图像A中单词 w i w_i wi出现了 n i n_i ni次,一共出现的单词数为 n n n次,(即图像里有 n n n个单词,其中某个单词出现的次数是 n i n_i ni次,换句话说就是一幅图像中有 n i n_i ni个特征点同属这个单词)
在这里插入图片描述
IDF:某个叶子节点 w i w_i wi中特征点的数量相对于所有特征点的比例。假设字典中所有特征数量为 n n n w i w_i wi中特征点的数量为 n i n_i ni:
在这里插入图片描述
于是,某叶子节点 w i w_i wi的权重等于TF与IDF之积。

【注意】IDF是离线计算,也就是说所有的图像都已经有了,提取特征点聚类了。而TF是在线计算,来一张算一张。

对图像提取特征点,找到字典里的单词,计算权重,并且形成一个向量。
每个单词(也就是一小部分特征点的集合)指向这么一个数据结构:一个是图像号,一个是权重。这也就是词袋中的“逆序指针/索引”。

关于数据结构的详细解析可查看这篇博客的第三部分——指路。

图像的表示:

每一幅图像,都由向量表示,即一堆 ( w i , n i ) (w_i,n_i) (wini)组成。 w i w_i wi是检测到的单词, n i n_i ni是上面算出的TF和IDF的乘积(每个单词在不同图像中对应的TF是不一样的,而单词自身的IDF是一样的)

A = { ( w 1 , n 1 ) , ( w 2 , n 2 ) , . . . , ( w N , n N ) } = v A A = \{(w_1,n_1),(w_2,n_2),...,(w_N,n_N)\} = v_A A={(w1,n1),(w2,n2),...,(wN,nN)}=vA

由于相似的特征可能会落到同一个类中,因此描述图像的向量会存在大量的零——>稀疏向量。通过词袋,我们用一个向量描述了一幅图像。

计算相似度:

直接计算两个向量的差异得到相似度。具体方法很多,这里给出一种 L 1 L_1 L1范式:
在这里插入图片描述
如 何 根 据 计 算 出 的 相 似 度 进 行 评 估 ? \red{如何根据计算出的相似度进行评估?}

1.6 相似度评分的处理

取一个先验相似度 s ( v t , v t − δ t ) s(v_t,v_t - \delta t) s(vt,vtδt),它表示某时刻关键帧图像与上一时刻的关键帧的相似性,然后其他的分值都参照这个值进行归一化。
在这里插入图片描述
如果当前帧与之前某一帧的相似度超过当前帧与上一帧关键帧相似度的3倍,就认为可能存在回环。

【注意】用于回环检测的帧可以取的稀疏一些,彼此不同但涵盖整个环境。在后端优化中,主要优化的也是关键帧。把"相近"的回环聚成一类,使算法不要反复地检测同一类的回环。

1.7 检测回环后的验证

词袋的回环检测完全依赖外观而没有利用任何的几何信息。这导致外观相似的图像容易被当成回环。并且由于词袋不在乎单词的顺序,只在意单词的有无,更易引发感知偏差,所以在回环检测后,通常会有一个验证步骤。

具体方法分为两类:

  1. 时间上的一致性检测:设立回环缓存机制认为单次检测到的回环不足以构成良好的约束,一段时间内一直检测的回环才当做是回环(n,n+1,n+2……帧都和关键帧像,才当做是回环)
  2. 空间上的一致性检测:把回环上的两帧进行特征匹配,估计相机运动。把运动放到之前的Pose Graph中,检查与之前估计是否有很大出入。

2. 实践与代码解析

选取TUM数据集中的来自一组实际相机运动轨迹的10幅图像,首尾两张图位于同一地方。验证算法可否检测到这一回环。

2.1 创建字典

如何生成及使用ORB字典??–先根据词袋模型生成这10张图像对应的字典

【注意】生成字典的数据应该来自与目标环境类似的地方。在计算能力和内存范围内,通常使用较大规模的字典——字典越大代表单词量越丰富,越容易找到当前图像对应的单词。

准备工作: 安装BoW库-采用cmake流程进行编译安装

相关代码:

int main( int argc, char** argv ) {
    // read the image 
    cout<<"reading images... "<<endl;
    vector<Mat> images; 
    for ( int i=0; i<10; i++ )
    {
        string path = "./data/"+to_string(i+1)+".png";
        images.push_back( imread(path) );
    }
    // detect ORB features
    cout<<"detecting ORB features ... "<<endl;
    Ptr< Feature2D > detector = ORB::create();
    vector<Mat> descriptors;
    for ( Mat& image:images )
    {
        vector<KeyPoint> keypoints; 
        Mat descriptor;
        detector->detectAndCompute( image, Mat(), keypoints, descriptor );
        descriptors.push_back( descriptor );
    }
    
    // create vocabulary 
    cout<<"creating vocabulary ... "<<endl;
    DBoW3::Vocabulary vocab;
    vocab.create( descriptors );
    cout<<"vocabulary info: "<<vocab<<endl;
    vocab.save( "vocabulary.yml.gz" );
    cout<<"done"<<endl;
    
    return 0;
}

【注意】DBoW3::Vocabulary对象的构造函数中,可以指定树的分叉数量以及深度。这里用的是默认的构造函数,k=10,d=5。对于图像特征也使用默认参数,每幅图提取500个特征点。
【make过程出错】:

没有规则可制作目标“/usr/local/lib/libDBoW3.a”,由“gen_vocab” 需求
解决办法:只需要将CMakeList.txt中的libDBow3.a改为libDBow3.so即可。

【运行后单词数为0】:

将图片路径改为详细路径:string path = “/xxxxx/data/”+to_string(i+1)+".png";

【注意】最好还是在读取图片时搞个判断!!!太坑了!!!

实验结果:
在这里插入图片描述
可以看到:分支数量k=10,深度L=5,单词数量为4983,Weighting是权重,Scoring是评分。

2.2 相似度计算

在2.1实践中已经对十幅图像生成了字典,现在使用此字典生成词袋并比较他们的差异。

相关代码:
演示了两种对比方式:图像之间的比较以及图像与数据库之间的比较。

int main(int argc, char **argv) {
    // read the images and database  
    cout << "reading database" << endl;
    DBoW3::Vocabulary vocab("./vocabulary.yml.gz");
    // DBoW3::Vocabulary vocab("./vocab_larger.yml.gz");  // use large vocab if you want: 
    if (vocab.empty()) {
        cerr << "Vocabulary does not exist." << endl;
        return 1;
    }
    cout << "reading images... " << endl;
    vector<Mat> images;
    for (int i = 0; i < 10; i++) {
        string path = "/home/jiachenxin/slambook/ch11/data/" + to_string(i + 1) + ".png";
        images.push_back(imread(path));
    }

    // NOTE: in this case we are comparing images with a vocabulary generated by themselves, this may lead to overfit.
    // detect ORB features
    cout << "detecting ORB features ... " << endl;
    Ptr<Feature2D> detector = ORB::create();
    vector<Mat> descriptors;
    for (Mat &image:images) {
        vector<KeyPoint> keypoints;
        Mat descriptor;
        detector->detectAndCompute(image, Mat(), keypoints, descriptor);
        descriptors.push_back(descriptor);
    }

    // we can compare the images directly or we can compare one image to a database 
    // images :
    cout << "comparing images with images " << endl;
    for (int i = 0; i < images.size(); i++) {
        DBoW3::BowVector v1;
        vocab.transform(descriptors[i], v1); // v1:需要求的该图像的词袋模型 des[i]:第i图像上所有描述子
        for (int j = i; j < images.size(); j++) {
            DBoW3::BowVector v2;
            vocab.transform(descriptors[j], v2);
            double score = vocab.score(v1, v2); // 计算相似度
            cout << "image " << i << " vs image " << j << " : " << score << endl;
        }
        cout << endl;
    }

    // or with database 
    cout << "comparing images with database " << endl;
    DBoW3::Database db(vocab, false, 0);
    for (int i = 0; i < descriptors.size(); i++)
        db.add(descriptors[i]); // 生成数据库
    cout << "database info: " << db << endl;
    for (int i = 0; i < descriptors.size(); i++) {
        DBoW3::QueryResults ret;
        db.query(descriptors[i], ret, 4);      // 返回四个结果
        cout << "searching for image " << i << " returns " << ret << endl << endl;
    }
    cout << "done." << endl;
}

【注意】图像获取地址的修改

实验结果:

两幅图像对应的词袋描述向量:
运行命令:~/slambook/ch11/build$ ./feature_training

两图像间的比较(这里只截了一小块):

运行命令:~/slambook/ch11/build$ ./loop_closure
在这里插入图片描述
图像与数据库的比较(截取一下块):
在这里插入图片描述
可以看到图像1和10的评分明显高于其他图像。

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

视觉SLAM十四讲:回环检测-知识点+代码 的相关文章

随机推荐

  • Matlab中计算程序运行时间的几种方法

    平常科研当中 xff0c 当我们在看文献时 xff0c 没看到一个优秀的算法时都有想要自己动手编程去实现的愿望 xff0c 算法好坏可以用代码的运行时间来评估 xff0c 在MATLAB中大致有以下几种方法来计算程序的运行时间 xff1a
  • easyplayerpro 使用说明_网页无插件流媒体播放器EasyPlayerPro如何二次开发重新封装?...

    原标题 xff1a 网页无插件流媒体播放器EasyPlayerPro如何二次开发重新封装 xff1f EasyPlayerPro流媒体播放器是青犀视频 TSINGSEE 团队研发的比较早能支持H 265编码视频的播放器 xff0c 支持集成
  • php获取curl头_php中CURL请求头和响应头获取方法

    本文主要和大家分享php中CURL请求头和响应头获取方法 xff0c 希望能帮助到大家 1 从CURL中获取响应头 oCurl 61 curl init 设置请求头 有时候需要 有时候不用 看请求网址是否有对应的要求 header 61 3
  • storm32和pixhawk_STORM32云台板调参教程 新人可参考 大师请指点

    本帖最后由 mountain230 于 2015 8 14 13 21 编辑 写在前面 xff1a 自调云台任重道远 第一个难点是硬件 找一套好的云台架子并且配好动力 xff0c 安排好走线 xff0c 需要的话还要添加电滑环 然后才涉及到
  • 如何理解误识率(FAR)拒识率(FRR),TPR,FPR以及ROC曲线

    平时在做指纹 人脸识别时 xff0c 会用到一些评价算法性能评价指标 常见的当属以下几种指标 xff1a 误识率 xff08 FAR xff0c false acceptance rate xff09 拒识率 xff08 FRR xff0c
  • c语言中延时函数delay1,delay什么意思_delay.h这一段是什么意思

    delay中文是什么意思 delay 英 d le 美 d le n 耽搁 延迟 xff0c 拖延 被耽搁或推迟的时间 vt 耽搁 延期 xff0c 推迟 vi 延缓 xff0c 延期 例句 For sentimental reasons
  • c语言 一个字节bit对换,请问在C语言中,如何高效把一字节的位对换(bit0和bit7,bit1和bit6,bit2和bit5,bit3和b...

    回复 114 请问在C语言中 xff0c 如何高效把一字节的位对换 bit0和bit7 bit1和bit6 bit2和bit5 bit3和b 积分 精华汤圆游客 393007870 出0入0汤圆 电梯直达 发表于 2008 12 2 10
  • studio one 3 机架声道设置_Lenovo UC30 声卡驱动跳线VST机架跳线

    只要是支持ASIO的声卡 xff0c 都可以使用宿主机架软件 xff0c 而机架设置好ASIO后 xff0c 还需要设置一下 xff0c 也就是设置麦克风 音乐的输入和麦克风的人声进入到经由机架挂载的效果插件 xff0c 调试后的混音的输出
  • Linux大小端转换实现

    实现 include lt byteswap h gt include lt stdint h gt 64 brief 8字节类型的字节序转化 template lt class T gt typename std enable if lt
  • Vins-Mono 论文 && Coding 一 7(2). pose_graph: 回环检测 && 重定位

    一 处理关键帧流程 void PoseGraph addKeyFrame KeyFrame cur kf bool flag detect loop 1 shift to base frame 将当前帧 pose 转换到 drift fre
  • 浅谈STM32串口通信(一)基本介绍和一个字节传输的实现

    文章目录 0 传输引脚1 传输一个字节1 1 发送一个字节1 2 接收一个字节 2 代码2 1 配置2 2 发送一个字节2 3 接收一个字节 0 传输引脚 串口收发共需要三根线 其中 TX脚为发送引脚 RX脚为发送引脚 GND为地 作为电平
  • 图像融合(Image Fusion)简介

    图像融合 Image Fusion 是用特定的算法将两幅或多幅图像综合成一幅新的图像 融合结果由于能利用两幅 或多幅 图像在时空上的相关性及信息上的互补性 xff0c 并使得融合后得到的图像对场景有更全面 清晰的描述 xff0c 从而更有利
  • IP地址分类

    大家好呀 xff0c 我是请假君 xff0c 今天又来和大家一起学习数通了 xff0c 今天要分享的知识是IP地址的分类 各个网段内具有的IP节点数各不相同 xff0c 为了适应这种需求 xff0c IP地址被分成五类 1 A类IP地址的第
  • 解决映射网络驱动器自动断开问题

    解决映射网络驱动器自动断开问题 hzq0201 2012 04 13 06 47 36 2777 收藏 2 版权 映射的网络驱动器在一段时间自动断开 xff0c 是由于服务器服务自动断开连接功能的默认超时期限造成的 xff0c 我们可以通过
  • vector深度探索

    声明 xff1a 本文中所有图件都来自B站侯捷老师授课视频 vecctor 底层实现原理 图1 GNU2 9 实现的容器vector vector 的内存是动态增长的 xff0c vector 最重要的三个成员变量为 三个迭代器 xff1a
  • 发送一个http请求以及url三部分组成和语法

    浏览器从URL中解析出服务器的主机名浏览器讲服务器的主机名转化成服务器的IP地址 xff08 DNS解析 xff09 浏览器将端口号从URL解析出来浏览器建立一条鱼web服务器的TCP连接浏览器向服务器发送一条http请求报文服务器向浏览器
  • 结构体对齐规则

    结构体 xff1a 结构体 xff08 struct xff09 是由一系列具有相同类型或不同类型的数据构成的集合 因为这一特性 xff0c 方便了开发者在使用的过程中可以将需要的不同的数据类型放在一起 xff0c 做成我们需要的数据类型
  • GPS坐标用于机器人定位的简单处理

    文章目录 前言一 GPS数据格式二 GPS坐标转换二维坐标原理三 参考代码1 转换经纬度格式2 解析通过串口获得的NMEA数据3 将经纬度转换为xy平面二维坐标 前言 最近工作上面接触使用GPS的NMEA数据为机器人提供平面坐标定位 xff
  • 学完C++基础后再学什么?

    学完 xff1f 那是什么程度 xff1f STL用得熟练吗 xff1f 算法和数据结构掌握得怎么样呢 xff1f 会写界面吗 xff1f BOOST呢 xff1f 像楼上所说的换一种语言 xff0c 简直是痴人说梦 xff0c 如果不深入
  • 视觉SLAM十四讲:回环检测-知识点+代码

    目录 基于外观的几何关系1 基础知识1 1 准确率和召回率1 2 词袋模型1 3 字典1 4 字典的数据结构1 5 相似度的计算1 6 相似度评分的处理1 7 检测回环后的验证 2 实践与代码解析2 1 创建字典2 2 相似度计算 回环检测