ORB-SLAM2-----Frame()

在跟踪线程中,构造图像帧。

mCurrentFrame = Frame(
        mImGray,                //灰度图
        timestamp,              //时间戳
        mpORBextractorLeft,     //ORB特征提取器
        mpORBVocabulary,        //词典
        mK,                     //相机内参矩阵
        mDistCoef,              //相机的去畸变参数
        mbf,                    //相机基线*相机焦距
        mThDepth);              //内外点区分深度阈值

  在Frame的构造函数中,首先根据传入的相关参数给Frame类中的根金字塔相关的元素,然后提取ORB特征。
一、ORB特征提取

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);
}
  • 构建图像金字塔:ComputePyramid(image);
void ORBextractor::ComputePyramid(cv::Mat image)
{
	//开始遍历所有的图层
    for (int level = 0; level < nlevels; ++level)
    {
		//获取本层图像的缩放系数
        float scale = mvInvScaleFactor[level];
		//计算本层图像的像素尺寸大小
        Size sz(cvRound((float)image.cols*scale), cvRound((float)image.rows*scale));
		//全尺寸图像。包括无效图像区域的大小。将图像进行“补边”,EDGE_THRESHOLD区域外的图像不进行FAST角点检测
        Size wholeSize(sz.width + EDGE_THRESHOLD*2, sz.height + EDGE_THRESHOLD*2);
		// 定义了两个变量:temp是扩展了边界的图像,masktemp并未使用
        Mat temp(wholeSize, image.type()), masktemp;
        // mvImagePyramid 刚开始时是个空的vector<Mat>
		// 把图像金字塔该图层的图像指针mvImagePyramid指向temp的中间部分(这里为浅拷贝,内存相同)
        mvImagePyramid[level] = temp(Rect(EDGE_THRESHOLD, EDGE_THRESHOLD, sz.width, sz.height));
        // Compute the resized image
		//计算第0层以上resize后的图像
        if( level != 0 )
        {
			//将上一层金字塔图像根据设定sz缩放到当前层级
            resize(mvImagePyramid[level-1],	//输入图像
				   mvImagePyramid[level], 	//输出图像
				   sz, 						//输出图像的尺寸
				   0, 						//水平方向上的缩放系数,留0表示自动计算
				   0,  						//垂直方向上的缩放系数,留0表示自动计算
				   cv::INTER_LINEAR);		//图像缩放的差值算法类型,这里的是线性插值算法

            // //!  原代码mvImagePyramid 并未扩充,上面resize应该改为如下
            // resize(image,	                //输入图像
			// 	   mvImagePyramid[level], 	//输出图像
			// 	   sz, 						//输出图像的尺寸
			// 	   0, 						//水平方向上的缩放系数,留0表示自动计算
			// 	   0,  						//垂直方向上的缩放系数,留0表示自动计算
			// 	   cv::INTER_LINEAR);		//图像缩放的差值算法类型,这里的是线性插值算法

			//把源图像拷贝到目的图像的中央,四面填充指定的像素。图片如果已经拷贝到中间,只填充边界
			//这样做是为了能够正确提取边界的FAST角点
			//EDGE_THRESHOLD指的这个边界的宽度,由于这个边界之外的像素不是原图像素而是算法生成出来的,所以不能够在EDGE_THRESHOLD之外提取特征点			
            copyMakeBorder(mvImagePyramid[level], 					//源图像
						   temp, 									//目标图像(此时其实就已经有大了一圈的尺寸了)
						   EDGE_THRESHOLD, EDGE_THRESHOLD, 			//top & bottom 需要扩展的border大小
						   EDGE_THRESHOLD, EDGE_THRESHOLD,			//left & right 需要扩展的border大小
                           BORDER_REFLECT_101+BORDER_ISOLATED);     //扩充方式,opencv给出的解释:

        }
        else
        {
			//对于第0层未缩放图像,直接将图像深拷贝到temp的中间,并且对其周围进行边界扩展。此时temp就是对原图扩展后的图像
            copyMakeBorder(image,			//这里是原图像
						   temp, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD,
                           BORDER_REFLECT_101);            
        }
        // //! 原代码mvImagePyramid 并未扩充,应该添加下面一行代码
        // mvImagePyramid[level] = temp;
    }
}

mvImagePyramid[level]存储金字塔第level层的图像。

  • 计算金字塔每层的FAST角点,并将特征点均匀化:使用四叉树的方式计算每层图像的特征点,并进行分配ComputeKeyPointsOctTree(allKeypoints)。
1、首先在图像四周去掉长度为3个单位像素点的边界,这是因为提取FAST角点时,用的半径是3的圆。
2、对去掉边界的图像网格化,分成每个窗口的大小为30个像素的正方形(使得特征分布比较均匀),然后对每个图像块进行FAST角点的提取。
3、将每个窗口的关键点存入vToDistributeKeys容器中,暂时保存第level层图像的关键点。
4、将每层的vToDistributeKeys送入到DistributeOctTree()中进行关键点剔除和平分配。
5、分层计算关键点的方向
for (int level = 0; level < nlevels; ++level)
        computeOrientation(mvImagePyramid[level],	//对应的图层的图像
						   allKeypoints[level], 	//这个图层中提取并保留下来的特征点容器
						   umax);					//以及PATCH的横坐标边界
  • 描述子的计算
static void computeDescriptors(const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors,
                               const vector<Point>& pattern)
{
	//清空保存描述子信息的容器
    descriptors = Mat::zeros((int)keypoints.size(), 32, CV_8UC1);

	//开始遍历特征点
    for (size_t i = 0; i < keypoints.size(); i++)
		//计算这个特征点的描述子
        computeOrbDescriptor(keypoints[i], 				//要计算描述子的特征点
							 image, 					//以及其图像
							 &pattern[0], 				//随机点集的首地址
							 descriptors.ptr((int)i));	//提取出来的描述子的保存位置
}

二、检查是否成功提取了本帧特征点,如果没有就放弃本帧
三、对提取的特征点进行畸变矫正
四、初始化本帧的地图点
五、将特征点分配到图像网格中

参考
https://wym.netlify.app/slam/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值