Frame部分的学习
Frame中主要包括了不同传感器下的帧构造函数、提取ORB特征、计算词袋、设置传感器的姿态、更新位姿矩阵、获取传感器的中心、获取旋转矩阵的逆矩阵、判断地图点是否再被观测用于跟踪、计算关键点是否越过了栅格、获取关键点所在的区域、计算双目传感器的匹配、从深度图中计算得到右图的像素点位置、将关键点重投影到三维世界坐标系、获取去畸变的关键点、计算去畸变图像的边界、将特征点分配到某一个栅格中加速后期的特征点匹配。
Frame维护的变量
- 词袋类型的指针
- ORB特征提取类型的指针,包括左右特征特征提取器
- 相机的内参包括畸变参数
- 双目传感器的基线长度以及与焦距fx相乘的基线值
- 深度值 mthDepth
- 关键点的数目
- 没矫正前左右图像上提取的关键点和矫正过的关键点,矫正过的关键点才是slam系统中使用的
- 右图上的uv值和像素点的深度值
- 词袋的相关变量,词袋向量和特征向量
- 关键点和地图点之间的map关系
- 判断map关系是否out的标记位
- 关键点和某栅格的对应关系,减轻投影地图点时的匹配复杂度
- 传感器的位姿
- 当前和下一帧的帧号
- 在金字塔下的特征提取匹配的一些参数
- 无畸变的图像边界
- 是否为初始化计算的标记位
- 旋转、平移矩阵和传感器中心
Frame内功能函数的大致实现过程
静态变量的初始化 下一帧的帧号设置为0
初始化计算标记位设置为true
其他的一些变量,包括传感器的内参
Frame的拷贝构造函数,基本栅格内的信息以及当前帧的姿态
双目的Frame构造函数:
- 获取金字塔的层数等一些尺度信息
- 启动左右图特征点检测的线程
- 获取提取出的关键点的数目并进行判断,不能为0
- 对提取出的特征点的像素坐标进行矫正,获取无畸变的特征点的像素坐标
- 计算双目之间的左右图的匹配关系
- 如果是第一帧或者是传感器内参数发生变化时,都将计算图像的边界
- 最后将特征点分配到每一个栅格中
RGB_D相机的Frame构造函数(主要是区别部分):
- 由于该传感器下只有一帧RGB图像,所以在特征点提取方面不需要开多线程进行提取
单目相机的Frame构造函数:
- 由于单目缺少三维信息,所以对于右图以及深度方面,这边设置的时候都设置成了-1
三类构造函数的过程大致是这样的一个过程
下面来说怎么把这些提取出的关键点分配到某一个栅格内
AssignFeaturesToGrid()函数
其中涉及到了PoseInGrid()这个函数
PoseInGrid():判断该关键点是否在限定的栅格内
如果在栅格内的话,把该点的栅格位置内记录这个点的index
如果没有,这个关键点的index不会被记录
ExtractORB(int flag, const cv::Mat &im)函数
根据flag的标记位来进行左右图的特征点提取,这边主要是利用了ORBExtractor中重新定义的()运算符
SetPose(cv::Mat Tcw)
设置当前帧的姿态,这里涉及到了UpdatePoseMatrices()。
UpdatePoseMatrices():主要是获取了当前帧的旋转矩阵、旋转矩阵的逆、平移矩阵以及相机的中心坐标
isInFrustum()
- 设置MapPoint中的mbTrackInView变量为false
- 获取该点在世界坐标系中pose
- 将它转换到相机坐标系下的pose
- check它的z坐标是否为负数,如果是就直接return false;如果不是,函数将继续往下执行。
- 将该点进行投影获得相机坐标系下的坐标,判断其是否已经在图像区域外
- 获取该点的最大最小距离(点到相机中心的距离),判断其是否在该距离范围之内,如果不在,直接返回false
- 计算观测该点的视角的cos值,值较小说明视角越偏离相机中心,如果该值小于一定的阈值之后,直接返回false
- 预测在这个距离下的尺度
- 走完上面的所有判断之后,该点将被标记为可被跟踪
- 更新MapPoint中的跟踪标记位、投影之后的uv、投影之后的右图中的u、尺度和观测角Cos
- return ture
GetFeaturesInArea()
- 单纯看这个函数的话,暂时了解到该函数在某(x,y)出进行了r距离范围的搜索,得到在该区域内的特征点的index,这边的minLevel和maxLevel暂时不明白是啥,猜是金字塔的层数,后面看到了继续理解,判断给关键点所在的金字塔层数,只有在最大和最小层数范围内的关键点index才会被记录。
- 最后范围区域内搜索到的满足条件的关键点的index vector
ComputeBoW()
如果没进行词袋的计算,就进行词袋的计算,将描述子转化成描述向量,利用词袋库,将这些信息都转化成ORB的词汇信息。
UndistortKeyPoints()
对关键点进行取畸变
- 判断畸变参数是否为0,如果是,就不需要进行矫正,直接将提取出的关键点mvKeys赋值给mvKeysUn
- 将所有的关键点放进一个大矩阵中,利用opencv的畸变矫正点的函数,获得去畸变的关键点坐标。
- 通过循环将去畸变之后的点赋值给mvKeysUn
ComputeImageBounds(const cv::Mat &imLeft)
计算图像的边界
如果畸变参数为0,那么边界点就是(0,0)和(480,640);
不然,将理想的角点形成一个4×2的矩阵,也是通过opencv的undistortPoints进行去畸变,获取真实的图像的边界
ComputeStereoMatches()
计算双目的立体匹配,主要是获取双目传感器的视差图。之前实习了解过大部分的立体匹配算法,但是这边不具体展开,因为我现在用的传感器是单目和深度,所以…好!下一个函数
ComputeStereoFromRGBD(const cv::Mat &imDepth)
该函数利用了双目的特点,由于深度相机是直接一张彩色图和深度图,不存在右图这种东西。
它利用深度信息,一方面把深度信息存放在mvDepth中;另一方面,通过深度换算到双目的视差值,得到右图的x值。
只看这个函数,你也是不知道它为什么要这样做。接着往下看…
今天的最后一个函数
UnprojectStereo(const int &i)
传参进来的是某一特征点的索引,获取该index下的深度值,如果是有效的深度值(>0),获取其像素坐标以及相机坐标系下的(x,y),最后组合成相机坐标系下的三维坐标x3Dc,最后转换到世界坐标系下。
整个Frame.cpp的理解就差不多了,现在只是单个cpp的总结和自己的理解,有不到位的可以直接提出来。本人之前也大致看过ORB_SLAM的代码,这次想从每一个cpp入手,最后来一个大框架总结,搞懂更细节的一些问题。
时间:2019年08月07日
作者:hhuchen
机构:河海大学机电工程学院