ORB与CCM-SLAM

2023-05-16

入口函数

mono_euroc.cc

int main(int argc, char **argv)
    
// 图像序列的文件名字符串序列
    vector<string> vstrImageFilenames;
    // 时间戳
    vector<double> vTimestamps;

    LoadImages(string(argv[3]),             // path_to_image_folder
               string(argv[4]),             // path_to_times_file
               vstrImageFilenames,          // 读取到的图像名称数组
               vTimestamps);   

    ORB_SLAM2::System SLAM(
        argv[1],                            // path_to_vocabulary
        argv[2],                            // path_to_settings
        ORB_SLAM2::System::MONOCULAR,       // 单目模式
        true);   

system.cc

  
System::System(......)

    mpVocabulary = new ORBVocabulary();

    mpKeyFrameDatabase = new KeyFrameDatabase(*mpVocabulary);

    mpMap = new Map();

    mpFrameDrawer = new FrameDrawer(mpMap);

    mpMapDrawer = new MapDrawer(mpMap, strSettingsFile);

 mpTracker = new Tracking(this,						//现在还不是很明白为什么这里还需要一个this指针  
    						 mpVocabulary,				//字典
    						 mpFrameDrawer, 			//帧绘制器
    						 mpMapDrawer,				//地图绘制器
                             mpMap, 					//地图
                             mpKeyFrameDatabase, 		//关键帧地图
                             strSettingsFile, 			//设置文件路径
                             mSensor);					//传感器类型iomanip


Tracking.cc


  
Tracking::Tracking(.....)

//读入配置文件参数
。。。。。。。。。。。。。

    mpORBextractorLeft = new ORBextractor(
        nFeatures,      //参数的含义还是看上面的注释吧
        fScaleFactor,
        nLevels,
        fIniThFAST,
        fMinThFAST);

ORBextractor.cc
  

首先声明一些容器的size,

ORBextractor::ORBextractor(。。。。。)

    mvScaleFactor.resize(nlevels);  
	//存储这个sigma^2,其实就是每层图像相对初始图像缩放因子的平方
    mvLevelSigma2.resize(nlevels);
	//对于初始图像,这两个参数都是1
    mvScaleFactor[0]=1.0f;
    mvLevelSigma2[0]=1.0f;


	//然后逐层计算图像金字塔中图像相当于初始图像的缩放系数 
    for(int i=1; i<nlevels; i++)  
    {
		//其实就是这样累乘计算得出来的
        mvScaleFactor[i]=mvScaleFactor[i-1]*scaleFactor;
		//原来这里的sigma^2就是每层图像相对于初始图像缩放因子的平方
        mvLevelSigma2[i]=mvScaleFactor[i]*mvScaleFactor[i];
    }


    //接下来的两个向量保存上面的参数的倒数
    mvInvScaleFactor.resize(nlevels);
    mvInvLevelSigma2.resize(nlevels);
    for(int i=0; i<nlevels; i++)
    {
        mvInvScaleFactor[i]=1.0f/mvScaleFactor[i];
        mvInvLevelSigma2[i]=1.0f/mvLevelSigma2[i];
    }


    //调整图像金字塔vector以使得其符合设定的图像层数
    mvImagePyramid.resize(nlevels);

	//每层需要提取出来的特征点个数,这个向量也要根据图像金字塔设定的层数进行调整
    mnFeaturesPerLevel.resize(nlevels);

//

构建图像金字塔,计算每一层图像需要分配的特征点个数

//

之后:

//这里的512表示512个点(上面的数组中是存储的坐标所以是256*2*2)
    const int npoints = 512;
注意到pattern0数据类型为Points*,bit_pattern_31_是int[]型,所以这里需要进行强制类型转换
    const Point* pattern0 = (const Point*)bit_pattern_31_;	
	//使用std::back_inserter的目的是可以快覆盖掉这个容器pattern之前的数据
	//其实这里的操作就是,将在全局变量区域的、int格式的随机采样点以cv::point格式复制到当前类对象中的成员变量中
    std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern));

之后,画一个园,以对称的方式计算园边界每一个点的(x,y)坐标,用于后面计算方向

std::vector<int> umax;	                 ///<计算特征点方向的时候,有个圆形的图像区域,这个vector中存储了每行u轴的边界(四分之一,其他部分通过对称获得)
umax.resize(HALF_PATCH_SIZE + 1);

至此

ORBextractor::ORBextractor(。。。。。)

结束

返回Tracking.cc

 // 在单目初始化的时候,会用mpIniORBextractor来作为特征点提取器
if(sensor==System::MONOCULAR)
        mpIniORBextractor = new ORBextractor(2*nFeatures,fScaleFactor,nLevels,fIniThFAST,fMinThFAST);

至此

Tracking::Tracking(.....)

结束

回到

mono_euroc.cc


 统计追踪一帧耗时 (仅Tracker线程)  
    vector<float> vTimesTrack;
    vTimesTrack.resize(nImages);

// Main loop
// step 4 依次追踪序列中的每一张图像  
    cv::Mat im;
    for(int ni=0; ni<nImages; ni++)
    {
        // Read image from file
        // step 4.1 读根据前面获得的图像文件名读取图像,读取过程中不改变图像的格式 
        im = cv::imread(vstrImageFilenames[ni],CV_LOAD_IMAGE_UNCHANGED);
        double tframe = vTimestamps[ni];
        // Pass the image to the SLAM system
        // step 4.4 追踪当前图像
        SLAM.TrackMonocular(im,tframe);

System.cc

cv::Mat System::TrackMonocular(const cv::Mat &im, const double &timestamp)

选择模式。是否关闭建图线程,仅追踪

 cv::Mat Tcw = mpTracker->GrabImageMonocular(im,timestamp);

// Step 1 :将彩色图像转为灰度图像  

// Step 2 :构造Frame
//判断该帧是不是初始化  
    if(mState==NOT_INITIALIZED || mState==NO_IMAGES_YET) //没有成功初始化的前一个状态就是NO_IMAGES_YET
        mCurrentFrame = Frame(
            mImGray,
            timestamp,
            mpIniORBextractor,      //初始化ORB特征点提取器会提取2倍的指定特征点数目
            mpORBVocabulary,
            mK,
            mDistCoef,
            mbf,
            mThDepth);
    else
        mCurrentFrame = Frame(
            mImGray,
            timestamp,
            mpORBextractorLeft,     //正常运行的时的ORB特征点提取器,提取指定数目特征点
            mpORBVocabulary,
            mK,
            mDistCoef,
            mbf,
            mThDepth);

Frame.cc


Frame::Frame(.....)  


// Step 1 帧的ID 自增  

// Step 2 计算图像金字塔的参数   

// Step 3 对这个单目图像进行提取特征点, 第一个参数0-左图, 1-右图  
    ExtractORB(0,imGray);
void Frame::ExtractORB(int flag, const cv::Mat &im)
{
    // 判断是左图还是右图
    if(flag==0)
        // 左图的话就套使用左图指定的特征点提取器,并将提取结果保存到对应的变量中 
        // 这里使用了仿函数来完成,重载了括号运算符 ORBextractor::operator() 
        (*mpORBextractorLeft)(im,				//待提取特征点的图像
							  cv::Mat(),		//掩摸图像, 实际没有用到
							  mvKeys,			//输出变量,用于保存提取后的特征点
							  mDescriptors);	//输出变量,用于保存特征点的描述子
    else
        // 右图的话就需要使用右图指定的特征点提取器,并将提取结果保存到对应的变量中 
        (*mpORBextractorRight)(im,cv::Mat(),mvKeysRight,mDescriptorsRight);
}

ORBextractor.cc

仿函数

void ORBextractor::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _keypoints,
                      OutputArray _descriptors)
    Mat image = _image.getMat();

OutputArray是opencv源码中常用到的类型,类型大概是用来表示为Mat和vector<>的。

而getMat()函数即是将OutputArray数据转换成Mat类型。


//判断图像的格式是否正确,要求是单通道灰度值
   assert(image.type() == CV_8UC1 );  

assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行,原型定义:

  

// Step 2 构建图像金字塔 

   ComputePyramid(image);
void ORBextractor::ComputePyramid(cv::Mat image)

   // Step 3 计算图像的特征点,并且将特征点进行均匀化。均匀的特征点可以提高位姿计算精度
// 存储所有的特征点,注意此处为二维的vector,第一维存储的是金字塔的层数,第二维存储的是那一层金字塔图像里提取的所有特征点  
    vector < vector<KeyPoint> > allKeypoints; 
    //使用四叉树的方式计算每层图像的特征点并进行分配
    ComputeKeyPointsOctTree(allKeypoints);
void ORBextractor::ComputeKeyPointsOctTree(
	vector<vector<KeyPoint> >& allKeypoints)

每一层划分网格,提取每一个网格的特征点,把当前图层绝对坐标存在


vToDistributeKeys中,  
        keypoints = DistributeOctTree(vToDistributeKeys, 			//当前图层提取出来的特征点,也即是等待剔除的特征点
																	//NOTICE 注意此时特征点所使用的坐标都是在“半径扩充图像”下的
									  minBorderX, maxBorderX,		//当前图层图像的边界,而这里的坐标却都是在“边缘扩充图像”下的
                                      minBorderY, maxBorderY,
									  mnFeaturesPerLevel[level], 	//希望保留下来的当前层图像的特征点个数
									  level);						//当前层图像所在的图层

函数 DistributeOctTree() 进行八叉树筛选(非极大值抑制),不断将存在特征点的图像区域进行4等分,直到分出了足够多的分区,每个分区内只保留响应值最大的特征点.
其代码实现比较琐碎,程序里还定义了一个 ExtractorNode 类用于进行八叉树分配


// compute orientations
//然后计算这些特征点的方向信息,注意这里还是分层计算的  
    for (int level = 0; level < nlevels; ++level)
        computeOrientation(mvImagePyramid[level],	//对应的图层的图像
						   allKeypoints[level], 	//这个图层中提取并保留下来的特征点容器
						   umax);		

// Step 4 拷贝图像描述子到新的矩阵descriptors  
	//统计整个图像金字塔中的特征点
    int nkeypoints = 0;
	//开始遍历每层图像金字塔,并且累加每层的特征点个数
    for (int level = 0; level < nlevels; ++level)
        nkeypoints += (int)allKeypoints[level].size();
        _descriptors.create(nkeypoints,		//矩阵的行数,对应为特征点的总个数
							32, 			//矩阵的列数,对应为使用32*8=256位描述子
							CV_8U);			//矩阵元素的格式

//  Step 5 对图像进行高斯模糊  
        GaussianBlur(workingMat, 		//源图像
					 workingMat, 		//输出图像
					 Size(7, 7), 		//高斯滤波器kernel大小,必须为正的奇数
					 2, 				//高斯滤波在x方向的标准差
					 2, 				//高斯滤波在y方向的标准差
					 BORDER_REFLECT_101);//边缘拓展点插值类型

// Step 6 计算高斯模糊后图像的描述子  
        computeDescriptors(workingMat, 	//高斯模糊之后的图层图像
						   keypoints, 	//当前图层中的特征点集合
						   desc, 		//存储计算之后的描述子
						   pattern);	//随机采样模板

 退出至System.cc

进入frame


// Step 4 用OpenCV的矫正函数、内参对提取到的特征点进行矫正   
UndistortKeyPoints();
    // 初始化本帧的地图点
    mvpMapPoints = vector<MapPoint*>(N,static_cast<MapPoint*>(NULL));
	// 记录地图点是否为外点,初始化均为外点false
    mvbOutlier = vector<bool>(N,false);

//  Step 5 计算去畸变后图像边界,将特征点分配到网格中。这个过程一般是在第一帧或者是相机标定参数发生变化之后进行  
        ComputeImageBounds(imGray);

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

ORB与CCM-SLAM 的相关文章

  • 微信小程序SLAM AR零基础入门教程

    鬼灭之刃花街篇 开播在即 今天带大家零基础使用Kivicube制作一个炭治郎的SLAM AR云手办 可以通过微信小程序将AR版的炭治郎放置在家中 提前感受鬼灭的氛围 先上个GIF大家看看动态的展示效果 在这里先科普一下本次教程使用到的AR技
  • Event-based Stereo Visual Odometry(双目事件相机里程计)论文学习

    本文详细介绍一篇双目事件相机里程计的论文 Event based Stereo Visual Odometry 港科大沈邵劼团队Yi Zhou和TU Berlin的Guillermo Gallego共同完成 并公布了代码 我准备在接下来一段
  • np.meshgrid()函数 以及 三维空间中的坐标位置生成 以及 numpy.repeat()函数介绍

    一 np meshgrid 函数 1 np meshgrid 介绍 X Y np meshgrid x y 代表的是将x中每一个数据和y中每一个数据组合生成很多点 然后将这些点的x坐标放入到X中 y坐标放入Y中 并且相应位置是对应的 下面是
  • 【大一立项】如何亲手搭建ROS小车:硬件和软件介绍

    本次博客将详细介绍上篇博客中提到的ROS小车的硬件和软件部分 由于十一实验室不开门 所以部分代码还没有上传到Github 下位机 下位机使用Arduino 因为大一上刚学完用Arduino做循迹小车 其实Arduino作为ROS小车的下位机
  • 使用EKF融合odometry及imu数据

    整理资料发现早前学习robot pose ekf的笔记 大抵是一些原理基础的东西加一些自己的理解 可能有不太正确的地方 当时做工程遇到的情况为机器人在一些如光滑的地面上打滑的情形 期望使用EKF利用imu对odom数据进行校正 就结果来看
  • SLAM评估工具evo的使用

    evo官方指南 参考博客 lt 官方手册 这篇参考博客 完全可以掌握evo的基本操作 gt Then 实践出真知 1 安装evo sudo apt install python pip pip install evo upgrade no
  • LeGO-LOAM论文翻译(内容精简)

    LeGO LOAM是一种在LOAM之上进行改进的激光雷达建图方法 建图效果比LOAM要好 但是建图较为稀疏 计算量也更小了 本文原地址 wykxwyc的博客 github注释后LeGO LOAM源码 LeGO LOAM NOTED 关于代码
  • LeGO-LOAM 系列(1): LeGO-LOAM 安装以及概述

    一 github GitHub RobustFieldAutonomyLab LeGO LOAM 二 安装依赖 1 ROS Ubuntu 64 bit 16 04 ROS Kinetic 比较常规 就不赘述了 2 gtsam Georgia
  • Sophus安装踩坑

    装SLAM十四讲第二版提供的Sophus Eigen版本3 4 0 报错 home ch 下载 Sophus 13fb3288311485dc94e3226b69c9b59cd06ff94e test core test so2 cpp 9
  • 高斯牛顿法求非线性最小二乘的步骤和c++代码实现

    slam图优化的本质是一个非线性优化问题 Gauss Newton求解步骤 1 线性化误差函数 2 构建线性系统 3 求解线性系统 4 更新解 并不断迭代直至收敛 一个简单的代码实现 一维参数xy 高维变为对应的矩阵即可 include
  • Eigen::aligned_allocator

    如果STL容器中的元素是Eigen库数据结构 例如这里定义一个vector容器 元素是Matrix4d 如下所示 vector
  • docker dbus-x11

    本来想用terminator启动nvidia docker 显示出图形界面的 结果发现启动的时候出问题了 terminator 1 dbind WARNING 07 31 53 725 Couldn t connect to accessi
  • Ceres Solver从零开始手把手教学使用

    目录 一 简介 二 安装 三 介绍 四 Hello Word 五 导数 1 数值导数 2解析求导 六 实践 Powell函数 一 简介 笔者已经半年没有更新新的内容了 最近学习视觉SLAM的过程中发现自己之前学习的库基础不够扎实 Ceres
  • Ubuntu18.04安装pcl(过程/坑记录式教程)

    Ubuntu18 04安装pcl 1 下载pcl 2 安装依赖项 3 编译 4 安装 5 网上教程说要安装QT5和VTK 但按照本文的 本文记录了安装时出现的问题 出错的安装命令也记录了 建议浏览一遍再参考 不要错用了错误的指令 1 下载p
  • SLAM练习题(十一)—— G2O实战

    SLAM 学习笔记 写在前面的话 算是一点小小的感悟吧 估计位姿的方法有线性方法和非线性方法 线性方法就是特征点法中的2D 2D的对极约束 3D 2D的PnP问题 非线性方法有BA优化 它将位姿的估计问题转换成了一个误差关于优化量的最小二乘
  • 3.Open3D教程——点云数据操作

    点云数据 本教程阐述了基本的点云用法 随需要的文件链接 1 显示点云 import open3d as o3d import numpy as np print Load a ply point cloud print it and ren
  • OpenCV 特征匹配多个对象

    如何在一张图像上找到一种类型的多个对象 我使用 ORB 特征查找器和强力匹配器 opencv 3 2 0 我的源代码 import numpy as np import cv2 from matplotlib import pyplot a
  • Todesk突然高速通道使用已结束

    今天使用Todesk直接报出如下错误 好像对于海外用户需要付费购买海外会员 大家有没有什么可以替换的远程控制软件的吗 能分享一下吗
  • LSH 是将向量转换为汉明距离的二进制向量吗?

    我读了一些关于 LSH 的论文 我知道它用于解决近似 k NN 问题 我们可以将算法分为两部分 给定一个向量D尺寸 其中D是大 的任何值 用一组翻译它N where N lt
  • Opencv 对象检测:ORB GPU 检测器和 SURF GPU 描述符提取器

    我只是做了一个小实验来尝试不同的检测器 描述符组合 我的代码使用 ORB GPU 检测器来检测特征 并使用 SURF GPU 描述符来计算描述符 我使用 BruteForceMatcher GPU 来匹配描述符 并使用 knnMatch 方

随机推荐

  • windows安装GO语言环境

    GO语言版本 Windows 平台和 Mac 平台推荐下载可执行文件版 xff0c Linux 平台下载压缩文件版 版本 xff1a 1 16 8 出现上面这个界面就说明已经安装好了 查看GO版本 可以打开终端窗口 xff0c 输入go v
  • 3par命令集

    3PAR是惠普公司的存储产品 它提供了基于CLI的管理终端来管理存储系统 主要的3PAR CLI命令有 show 显示系统信息 showsys 显示系统概况信息shownode 显示节点信息showport 显示端口信息showdisks
  • 华为欧拉系统RPM强制卸载libffi-devel

    查看欧拉系统有没有安装这个包 yum list span class token operator span span class token function grep span libffi devel 准备离线下载时 xff0c 发现
  • 最全Arrays.sort函数举例

    Arrays sort 的作用是对括号中的数组进行排序 xff0c 时间复杂度O xff08 n logn xff09 xff0c 方法返回值为void 是在原来数组的空间基础上进行升序排序 xff0c 因此不需要定义一个数组接收它 xff
  • 【无标题】驱动设计思想P9- linux驱动(韦东山)

    linux驱动 xff08 韦东山 xff09 驱动设计思想P9 linux 驱动 61 驱动框架 43 硬件操作 61 驱动框架 43 单片机 面向对象 字符设备驱动程序抽象出file operation结构体对硬件部分抽象为led op
  • Ubuntu16.04 安装ROS 过程中遇到的问题

    操作系统 xff1a Ubuntu16 04 ROS版本 xff1a Kinetic 纯入门 xff0c 安装过程错误百出 xff0c 几乎是一步一个报错 xff0c 每执行一个命令就要各种查找解决方法 xff0c 终于用一个下午加晚上的时
  • Github学习心得(3):项目的修改

    书接上回Github创建版本库 xff0c 我们已经成功地创建了一个版本库 xff0c 并且上传了readme txt文件 在实际项目中 xff0c 我们总是要对代码进行修改 xff0c 那么就需要及时地更新Github版本库中的文件 以本
  • 解决Keil全局搜索时无法跳转的问题

    检查一下自己的工程文件路径中是否有英文括号 xff0c 如果有则删掉
  • 阿木实验室的普罗米修斯仿真平台搭建的难点和坑点

    普罗米修斯仿真实验平台搭建的难点解决 0 我为什么要辛苦搭环境 xff1f 前一段时间阿木实验室提供了一个搭好环境的镜像 xff0c 凭阿木币购买 xff0c 是真的好用 xff0c 阿木币嘛 xff0c 签一签道就会有 关于这个镜像其实还
  • Visual Studio Code无法监视此大工作空间中的文件更改的解决办法

    Visual Studio Code无法监视此大工作空间中的文件更改 xff08 错误ENOSPC xff09 当您看到此通知时 xff0c 表明VS Code文件监视程序用尽了句柄 xff0c 因为工作空间很大并且包含许多文件 在调整平台
  • Ubuntu增加交换分区即swap方法

    用交换文件的方式添加交换分区 xff0c 如下 sudo su 进入管理员模式 xff0c 会提示输入密码 cd usr mkdir swap cd swap dd if 61 dev zero of 61 swapfile bs 61 1
  • 为什么px4源码里找不到main函数?——从hello sky理解px4的编程规范

    如果你刚自学了c 43 43 xff0c 准备开始看px4的源码 xff0c 但翻来覆去找了半天没有找到一个标准的main 究其原因 xff0c 在nuttx系统里 xff0c 程序入口不是main xff0c 所以找不到任何main 如果
  • matlab提取外接盒、围盒、凸包和骨架

    宁收宁马的费呢 xff01 老子自己做的 xff0c 免费提供该大家 xff0c 卖资料的垃圾 xff0c 能收几个钱 拍一幅含多个人体目标的图像 xff0c 利用外接盒 围盒和凸包对每个人体目标区域进行表达 xff0c 并提取其骨架 ma
  • QLearning算法实现(python)

    文章目录 环境要求QLearning算法简介算法实现预设值Q表定义行为环境反馈环境更新Q学习主循环 环境要求 开发环境 xff1a python3 需要导入的包 xff1a numpy pandas time QLearning算法简介 Q
  • HTTP工作原理

    文章目录 简介 HTTP与HTTPS端口区别HTTP请求的方法OPTIONSHEADGETPOSTPUTDELETETRACECONNECT注意 xff1a HTTP 请求 响应的步骤 简介 HTTP协议定义Web客户端如何从Web服务器请
  • pandas行和列的操作

    文章目录 Series创建查增 改删 DataFrame创建查增删 columns amp index的指定 在Pandas中 xff0c Series是一维容器 xff0c DataFrame表示一个数据表 DataFrame中的任一行
  • 安装MAVROS过程 catkin:未找到命令 的问题

    PX4官网安装教程 源码方式安装 1 第一步执行 xff0c 创建文件夹 xff1b 移到创建文件夹中 mkdir p catkin ws src cd catkin ws 2 执行 catkin init 出错如下图 解决方法 xff1a
  • 数据规范化处理

    常用的三种数据规范化处理方法 常用的数据规范化处理主要有Min max规范化 Z Score规范化和小数定标规范化 下面简要介绍下三种数据规范化方法 xff0c 并用python的机器学习库SciKit Learn来实现数据变化 Min m
  • matplotlab不能显示中文解决(windows)

    汉字字体 xff0c 优先使用楷体 xff0c 找不到则使用黑体 plt rcParams 39 font sans serif 39 61 39 Kaitt 39 39 SimHei 39 正常显示负号 plt rcParams 39 a
  • ORB与CCM-SLAM

    入口函数 mono euroc cc int main int argc char argv 图像序列的文件名字符串序列 vector lt string gt vstrImageFilenames 时间戳 vector lt double