深蓝-视觉slam-第八讲学习笔记

在这里插入图片描述
回环检测的意义:前端提供特征点的提取和轨迹,地图的初始化,后端负责优化这些数据。如果像里程计一样只考虑相邻时间上的关键帧,那么误差就会出现累积,无法构建全局一致的轨迹和地图。但是,回环检测模块能够给出除了相邻帧的一些时间更久远的约束,相机经过同一个地方,采集到了相似的数据。而回环检测的关键就是如何有效的检测出相机经过同一个地方,如果能够成功的检测到,就可以为后端的位姿图提供更多的数据,使之得到更好的估计,得到全局一致的估计。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
假阳性:感知偏差 假阴性:感知变异
算法需要设置参数,当某个阈值提高时,算法更为严格,检测出更少的回环,使准确率下降,反之,选择宽松的配置,检测出的回环数量将增加,得到更高的召回率,但可能其中混有不是回环的情况,导致准确率下降。
在SLAM中,我们对准确率要求高,召回率则宽容一些,由于假阳性的回环将在后端的位姿图中添加错误的边,会导致优化算法给出完全错误的结果。相比之下,召回率低一些,顶多有部分的回环没有检测出来,地图可能会受一些累积误差的影响,然而只需一两次的回环就可以消除了,所以我们在选择回环检测算法时,更倾向于把参数设置的更严格,或者检测之后在加上回环验证。
在这里插入图片描述
在词袋中,可以使用在这里插入图片描述来度量图像之间的相似程度。当a,b 两个向量完全一样时,将的到1;完全相反时(a为0的地方b为1)得到0.这样就定义了两个描述向量相似性,也定义了图像之间的形似性。其中a,b向量代表的是图像中某个特征(单词)的有无。

接下来,我们来介绍字典的生成,以及如何利用字典实际的计算两幅图像间的相似性。
单词不是从单幅图像上得到的,而是某一类特征的组合,字典的生成类似一个聚类。
假设我们对大量的图像提取了特征点,例如有N个,现在找一个有K个单词的字典,每个单词可以看作局部相邻特征点的集合。
在这里插入图片描述
K-means需要指定聚类数量和随机选取中心点使得每次聚类结果不相同,涉及效率上的问题。随后有层次聚类,K-means++等算法弥补不足。
在这里插入图片描述
通过层次聚类,最终在叶子层构建了单词,而树结构中的中间节点仅快速查找时使用。着一个k分支,d深度的树,可以容纳 k d k_d kd个单词,在查找某个给定的特征对应 的单词时只需要将它与每个中间节点的聚类中心作比较(d次),即可找到最后的单词,保证了对数级别的查找效率。
实例:训练字典
安装BoW库,使用10幅图像训练一个小字典。

#include "DBoW3/DBoW3.h"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <boost/format.hpp>
#include <iostream>
#include <vector>
#include <string>

using namespace cv;
using namespace std;

boost::format fmt_other("../data/%d.png");
//十张图像训练字典
int main(int argc, char**argv)
{
    cout << "reading images....." << endl;
    vector<Mat> images;
    for(int i = 0; i< 10; i++)
    {
        images.push_back(imread((fmt_other % i ).str(), 0));

    }
    cout << "detecting ORB features..." << endl;
    Ptr<Feature2D> detector = ORB::create();
    vector<Mat> descriptors;
    for(Mat &image:images) {
        vector<KeyPoint> keypoint;
        Mat descriptor;
        detector->detectAndCompute(image, Mat(), keypoint, descriptor);
        descriptors.push_back(descriptor);
    }

    cout << "create vocalulary..." << endl;
    DBoW3::Vocabulary vocab;
    vocab.create(descriptors);
    cout << "vocabulary info:" << vocab << endl;
    vocab.save("vocabulary.yaml");
    cout << "done" << endl;
    return 0;

}

运行结果:在对应的目录下生成yaml文件。
在这里插入图片描述
由运行结果可以看到:分支数量K是10, 深度L是5, 单词数量4487,Weightting 是权重,Scoring 是评分;

评分由下部分计算:
假设从一幅图像中 提取了N个特征,找到这N个特征对应的单词后,我们就相当于有了该图像单词列表中的分布,或者是置放图。就是有无的意思。但我们希望对单词的区分性或和重要性加以评估,给他们不同的权值以达到更好的效果。
TF-IDF是文档检索中常用的一种加权方式,也可用于BoW中
TF:某个单词在一幅图像中出现的频率高,则它的区分度就高;
IDF:某个单词在字典中出现的频率越低,分类图像时区分度越高;
在建立字典时就可以计算IDF:统计某个也子结点Wi中特征数量相对于所有特征数量的比率.
IDF部分:假设所有的特征 数量为n, wi数量为ni, 那么该单词的IDF为: I D F i = l o g n n i IDF_i = log \frac{n}{n_i} IDFi=lognin
TF部分:某个特征在单幅图像中出现的频率。假设图像A中单词wi出现了ni次,而一共出现的次数为n次,那么TF为 I D F i = n i n IDF_i = \frac{n_i}{n} IDFi=nni;
于是wi的权重等与TF和IDF:
在这里插入图片描述
考虑权重后,可以对某幅图像A,它的特征点可对应到许多个单词,组成它的BoW,如下:A的计算;
由于相似的特征可能落在同一个类中,因此实际的 v A v_A vA中存在大量的0。我们通过词袋用单个向量 v A v_A vA描述了一幅图像A。这个向量是一个稀疏向量,它的非零部分指示了图像A中含有那些单词,而这部分的值为TF-IDF的值。
在这里插入图片描述
实例:相似度的计算
对十幅图像生成了字典,使用字典生成词袋并比较他们的差异。
实例代码:

#include "DBoW3/DBoW3.h"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <iostream>
#include <vector>
#include <string>

using namespace cv;
using namespace std;
//根据训练的字典计算相似评分
int main(int argc, char **argv) 
{
    cout << "reading database" << endl;
    DBoW3::Vocabulary vocab("./vocabulary.yaml");
    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 = "../data/" + to_string(i + 1) + ".png";
        images.push_back(imread(path));
    }

    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);
    }

    //方法一:通过图片间的比较看形似程度
    cout << "comparing iamge whith images" << endl;
    for(int i = 0; i  < images.size(); i++) {
        DBoW3::BowVector v1;
        vocab.transform(descriptors[i], v1);
        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 << "---------------------"<< endl;;
        }
        cout << endl;
    }
    //通过图片间的比较看形似程度
    cout << "comparing iamge whith images" << endl;
    for(int i = 0; i  < images.size(); i++) {
        DBoW3::BowVector v1;
        vocab.transform(descriptors[i], v1);
        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 << "\t";
        }
        cout << endl;
    }

    //方法二:图像与数据库间的比较
    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;
}

在这里插入图片描述
在这里插入图片描述
使用第二种方法,在数据库查询时,DBoW对上面的分数进行排序,给出最相似的结果。可以看出图1和图10最相似,但肉眼看相似度在百分之80以上,而实验结果却是无关图像的相似度约为百分之2, 相似图像的相似度约为百分之5,没有那么明显是不是训练的数据不够多,字典的规模不够大呢?
实例:增加字典规模: 对应的数据集没有给出,若是想要看数据集对形似性的影响,寻找相关的数据集放到对应的目录即可;

#include "DBoW3/DBoW3.h"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <iostream>
#include <vector>
#include <string>

using namespace cv;
using namespace std;

int main( int argc, char** argv )
{
    string dataset_dir = argv[1];
    ifstream fin ( dataset_dir+"/associate.txt" );
    if ( !fin )
    {
        cout<<"please generate the associate file called associate.txt!"<<endl;
        return 1;
    }

    vector<string> rgb_files, depth_files;
    vector<double> rgb_times, depth_times;
    while ( !fin.eof() )
    {
        string rgb_time, rgb_file, depth_time, depth_file;
        fin>>rgb_time>>rgb_file>>depth_time>>depth_file;
        rgb_times.push_back ( atof ( rgb_time.c_str() ) );
        depth_times.push_back ( atof ( depth_time.c_str() ) );
        rgb_files.push_back ( dataset_dir+"/"+rgb_file );
        depth_files.push_back ( dataset_dir+"/"+depth_file );

        if ( fin.good() == false )
            break;
    }
    fin.close();
    
    cout<<"generating features ... "<<endl;
    vector<Mat> descriptors;
    Ptr< Feature2D > detector = ORB::create();
    int index = 1;
    for ( string rgb_file:rgb_files )
    {
        Mat image = imread(rgb_file);
        vector<KeyPoint> keypoints; 
        Mat descriptor;
        detector->detectAndCompute( image, Mat(), keypoints, descriptor );
        descriptors.push_back( descriptor );
        cout<<"extracting features from image " << index++ <<endl;
    }
    cout<<"extract total "<<descriptors.size()*500<<" features."<<endl;
    
    // create vocabulary 
    cout<<"creating vocabulary, please wait ... "<<endl;
    DBoW3::Vocabulary vocab;
    vocab.create( descriptors );
    cout<<"vocabulary info: "<<vocab<<endl;
    vocab.save( "vocab_larger.yml.gz" );
    cout<<"done"<<endl;
    
    return 0;
}

在这里插入图片描述
相似性评分的处理:两幅图像的绝对大小不好,例如:有些环境外观本就很相似,有些环境各个地方都有很大的不同。考虑这些情况,我们先取一个先验相似度 s ( v t , v t − Δ t ) s(v_t, v_{t-\Delta t}) s(vt,vtΔt),它表示某时刻关键帧图像与上一时刻的关键帧的相似性。。然后,其他的分值都参考这个值进行归一化:
在这里插入图片描述
也就是说:如果当前帧与之前某个关键帧的相似读超过当前帧与上一个关键帧相似度的3倍,就可能存在循环。
在这里插入图片描述
稠密深度估计中,不同之处在与,我们无法把每个像素都当成特征点计算描述子。因此稠密深度古籍中,匹配环节很很重要,如何确定第一幅图中的像素在其他图中的位置,需要使用到极线搜索,块匹配技术。然后就可以像三角册量一样计算他们的深度,不同的是,需要通过对此三角测量让深度估计收敛,而不是使用一次。我们希望深度估计随着测量的增加从一个非常不确定的量,逐渐收敛到一个稳定的值——深度滤波器

相比与双目和RGB-D来说,单目测到的深度并不可靠,深度比较弱。
单个像素的亮度没有区分性,可以取p1周围wxw的小块,然后在极线上也取很多同样大小的小块进行比较,就可以在一定程度上提高区分性——块匹配,在这个过程中假设不同图像间整个小块的灰度值不变。
在这里插入图片描述
特征点法中,可以通过特征匹配找到相机2中对应的p2位置,但现在没有描述子,所以只能在极线上搜索和p1长的很像的点。感觉像回环检测,回环检测是通过词袋来解决的,但这里没有特征,所以只能采用其他方法。
在这里插入图片描述
对SSD和NCC去均值后,结果更可靠。

在这里插入图片描述
高斯分布的深度滤波器
对像素点深度的估计,本身可以建模成一个状态估计问题,于是就有滤波器, 非线性优化两种求解思路。虽然非线性优化效果好,但是SlAM的实时性要求高,前端已经占据了不少的计算量,建图还是采用计算量较少的滤波器方法。
对深度的分布假设存在若干种不同的做法。一种是假设深度服从高斯分布,得到卡尔曼式的方法; 另一种是均匀-高斯混合分布的假设。
我们来推导高斯分布的深度滤波器:设某个像素点的深度d服从高斯分布: P ( d ) P(d) P(d)= N ( μ N( \mu N(μ, σ 2 \sigma^2 σ2)

每当新的数据到来时,我们都会观测到它的深度。同样,假设这次观测的分布也是一个高斯分布: P ( d o b s ) = N ( μ o b s P(d_{obs} ) = N (\mu_{obs} P(dobs)=N(μobs, σ o b s 2 ) \sigma_{obs}^2) σobs2)

我们的问题是:如何根据观测的信息更新原先的d的分布。这是一个信息融合问题。我们知道两个高斯分布的乘以依然是一个高斯分布。融合后的高斯分布为: μ f u s e = σ o b s 2 μ + σ 2 μ o b s σ 2 + σ o b s 2 \mu_{fuse} = \frac{\sigma_{obs}^2 \mu + \sigma^2 \mu_{obs}}{\sigma^2 + \sigma_{obs}^2} μfuse=σ2+σobs2σobs2μ+σ2μobs, σ f u s e 2 = σ 2 σ o b s 2 σ 2 + σ o b s 2 \sigma_{fuse}^2 = \frac{\sigma^2 \sigma_{obs}^2}{\sigma^2 + \sigma_{obs}^2} σfuse2=σ2+σobs2σ2σobs2

由于只有观测没有运动方程,所以深度仅用信息融合部分,而无需像完整的卡尔曼那样进行预测和更新。不过虽然融合的方法简单,但是仍有问题:如何确定我们观测到的深度的分布,即如何计算 μ o b s \mu_{obs} μobs, σ o b s \sigma_{obs} σobs
关于 μ o b s \mu_{obs} μobs, σ o b s \sigma_{obs} σobs的计算可以考虑几何不确定性和光度不确定性。这个暂考虑几何关系带来的不确定性质。现在,假设通过极线搜索和块匹配确定了参考帧某个像素在当前帧的投影位置。这个位置对深度的不确定性有多大?
在这里插入图片描述
估计稠密深度的完整过程:

  • 假设所有的像素的深度满足某个初始的高斯分布。
  • 当新数据产生时,通过极线搜索和块匹配确定投像素点的投影位置。
  • 根据几何关系来计算三角化后的深度不确定性。
  • 将当前观测融合进上一次的估计中。若收敛则停止计算,否则返回第二步。

在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《视觉SLAM十四讲》第三章主要介绍了视觉SLAM中的关键技术——特征提取和描述子。本章首先介绍了特征点的概念和特征点的选择原则。特征点即图像中具有鲁棒性和区分度的点,可以通过对其进行检测和描述来进行特征匹配和跟踪。在进行特征提取时,作者介绍了常见的特征检测算法,如Harris角点检测、SIFT和SURF算法等,并对其进行了比较和分析。 接着,本章详细阐述了特征描述子的概念和作用。特征描述子是对特征点周围区域的图像信息进行编码,以实现特征匹配和跟踪。常见的特征描述子包括SIFT、SURF和ORB等。作者从描述子的表示形式、计算方式和匹配方法等方面进行了介绍,并对它们进行了比较和评价。同时,还提到了基于二进制描述子的方法,如BRIEF、BRISK和FREAK等。 在特征匹配方面,本章介绍了特征描述子匹配的基本原理和流程。以基于特征点的视觉SLAM为例,作者详细解释了特征点的匹配过程,包括特征点的选择、特征点描述子匹配和筛选等步骤。并介绍了如何通过验证特征点的三角化和PnP求解来估计相机的位姿。 此外,本章还介绍了一些特定场景下的特征点选择和提取策略,如动态环境下的特征点追踪和关键帧选择等。 综上所述,《视觉SLAM十四讲》第三章主要介绍了特征提取和描述子在视觉SLAM中的重要性和应用。通过对特征点的检测和描述,可以实现特征匹配和跟踪,为后续的相机位姿估计和建图提供基础。该章内容详细且通俗易懂,对于学习和理解视觉SLAM有着重要的指导作用。 ### 回答2: 《视觉SLAM十四讲-Ch3》主要介绍了视觉SLAM(同时定位与建图)技术的基本原理和实现方法。本章主要涵盖了三维几何表示和变换、相机模型和相机姿态以及特征提取与匹配等内容。 首先,本章介绍了三维几何表示和变换的概念。通过介绍欧氏空间中的点、向量和坐标变换,深入解释了相机在三维空间中的位置和朝向的表示方式。同时,引入了齐次坐标和投影矩阵的概念,为后续的相机模型和姿态估计打下了基础。 其次,本章详细讲解了相机模型和相机姿态的原理与应用。其中,介绍了针孔相机模型,分析了图像坐标和相机坐标之间的映射关系。通过投影矩阵的推导,给出了透视投影和仿射投影的公式,并解释了相机焦距和主点的含义。此外,还介绍了如何通过计算相机的外参矩阵来估计相机的姿态,以及如何将图像坐标转换为相机坐标。 最后,本章介绍了特征提取与匹配的技术。首先,介绍了角点和边缘点的概念,以及如何利用差分和梯度计算来检测图像中的角点和边缘点。然后,介绍了如何通过特征描述符来表示图像中的特征点,并通过特征匹配算法找到两幅图像之间的对应关系。特征提取与匹配是视觉SLAM中非常重要的步骤,对于后续的相机定位和建图至关重要。 综上所述,《视觉SLAM十四讲-Ch3》通过系统地介绍了视觉SLAM技术的基本概念和实现方法,包括三维几何表示和变换、相机模型和相机姿态的原理与应用,以及特征提取与匹配的技术。这些内容为读者深入理解和掌握SLAM技术提供了很好的基础。 ### 回答3: 视觉SLAM(Simultaneous Localization and Mapping)是一种通过计算机视觉技术,实现机器智能的同时实时定位和地图构建的方法。在《视觉SLAM十四讲》第三讲中,主要介绍了视觉SLAM的基本概念和关键技术。 首先,讲解了视觉SLAM的理论基础,包括自我运动估计和地图构建两个部分。自我运动估计是通过相邻帧之间的视觉信息,计算相机在三维空间中的运动,从而实现机器的实时定位;地图构建是通过对场景中特征点的观测和跟踪,建立起一个三维空间中的地图。这两个过程相互影响,通过不断迭代优化,实现高精度的定位和地图构建。 接着,讲解了基于特征的视觉SLAM算法。特征提取与描述是建立视觉SLAM系统的关键步骤,通过提取场景中的特征点,并为其生成描述子,来实现特征点的匹配和跟踪。同时,还介绍了一些常用的特征点提取和匹配算法,如FAST、SIFT等。 在SLAM框架方面,本节还介绍了基于视觉的前端和后端优化。前端主要负责实时的特征跟踪和估计相机运动,后端则是通过优化技术,对前端输出的轨迹和地图进行优化求解,从而提高系统的精度和鲁棒性。 最后,本节提到了几个视觉SLAM的应用场景,如自主导航、增强现实等。这些应用对于实时高精度的定位和地图建立都有着很高的要求,因此,视觉SLAM的技术在这些领域有着广泛的应用前景。 总的来说,《视觉SLAM十四讲》第三讲对视觉SLAM的基本概念和关键技术进行了系统的介绍。理论基础、特征提取与描述、SLAM框架和应用场景等方面的内容都给出了详细的解释和案例,有助于读者更好地理解和应用视觉SLAM技术。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值