ORB-SLAM2 ---- ORBextractor::operator()仿函数

ORB-SLAM2 ---- ORBextractor::operator()仿函数_第1张图片

1.这个仿函数做了什么?

        对输入的图像提取特征点、计算描述子。

2.代码解析

/**
 * @brief 用仿函数(重载括号运算符)方法来计算图像特征点
 * 
 * @param[in] _image                    输入原始图的图像
 * @param[in] _mask                     掩膜mask
 * @param[in & out] _keypoints                存储特征点关键点的向量
 * @param[in & out] _descriptors              存储特征点描述子的矩阵
 */
void ORBextractor::operator()( InputArray _image, InputArray _mask, vector& _keypoints,
                      OutputArray _descriptors)

2.1 检查图像有效性。如果图像为空,那么就直接返回

    if(_image.empty())
        return;

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

2.2 构建图像金字塔 

ComputePyramid(image);

ORB-SLAM2 ---- ORBextractor::ComputePyramid函数_Courage2022的博客-CSDN博客

2.3 计算图像的特征点,并且将特征点进行均匀化。均匀的特征点可以提高位姿计算精度 

    // 存储所有的特征点,注意此处为二维的vector,第一维存储的是金字塔的层数,第二维存储的是那一层金字塔图像里提取的所有特征点
    vector < vector > allKeypoints; 
    //使用四叉树的方式计算每层图像的特征点并进行分配
    ComputeKeyPointsOctTree(allKeypoints);

ORB-SLAM2 ---- ORBextractor::ComputeKeyPointsOctTree函数_Courage2022的博客-CSDN博客

2.4 拷贝图像描述子到新的矩阵descriptors 

    Mat descriptors;

	//统计整个图像金字塔中的特征点
    int nkeypoints = 0;
	//开始遍历每层图像金字塔,并且累加每层的特征点个数
    for (int level = 0; level < nlevels; ++level)
        nkeypoints += (int)allKeypoints[level].size();
	
	//如果本图像金字塔中没有任何的特征点
    if( nkeypoints == 0 )
		//通过调用cv::mat类的.realse方法,强制清空矩阵的引用计数,这样就可以强制释放矩阵的数据了
		//参考[https://blog.csdn.net/giantchen547792075/article/details/9107877]
        _descriptors.release();
    else
    {
		//如果图像金字塔中有特征点,那么就创建这个存储描述子的矩阵,注意这个矩阵是存储整个图像金字塔中特征点的描述子的
        _descriptors.create(nkeypoints,		//矩阵的行数,对应为特征点的总个数
							32, 			//矩阵的列数,对应为使用32*8=256位描述子
							CV_8U);			//矩阵元素的格式
		//获取这个描述子的矩阵信息
		// ?为什么不是直接在参数_descriptors上对矩阵内容进行修改,而是重新新建了一个变量,复制矩阵后,在这个新建变量的基础上进行修改?
        descriptors = _descriptors.getMat();
    }

    //清空用作返回特征点提取结果的vector容器
    _keypoints.clear();
	//并预分配正确大小的空间
    _keypoints.reserve(nkeypoints);

	//因为遍历是一层一层进行的,但是描述子那个矩阵是存储整个图像金字塔中特征点的描述子,所以在这里设置了Offset变量来保存“寻址”时的偏移量,
	//辅助进行在总描述子mat中的定位
    int offset = 0;
	//开始遍历每一层图像
    for (int level = 0; level < nlevels; ++level)
    {
		//获取在allKeypoints中当前层特征点容器的句柄
        vector& keypoints = allKeypoints[level];
		//本层的特征点数
        int nkeypointsLevel = (int)keypoints.size();

		//如果特征点数目为0,跳出本次循环,继续下一层金字塔
        if(nkeypointsLevel==0)
            continue;

        // preprocess the resized image 

        这里我们新建了一个描述子矩阵descriptors,统计所有层的已提取特征点数目存放在nkeypoints里(为什么要建立这个变量,配置文件里面不是已经有要分配的特征点数量了吗?ORBextractor.nFeatures: 1000?因为可能由于图片原因未必能找出那么多特征点....), 在判断特征点数目是否为空?(SLAM系统BUG或者图片原因);

        若为空则清空用作返回特征点提取结果的vector容器,若不为空那么就创建这个存储描述子的矩阵,注意这个矩阵是存储整个图像金字塔中特征点的描述子的。

        对每层图像金子塔进行遍历,取出当前层的特征点存储到keypoints容器中,并存储该层金字塔中特征点的数量到变量nkeypointsLevel 中,如果特征点数目 = 0,跳过该层金字塔。

2.5 对图像进行高斯模糊

         Mat workingMat = mvImagePyramid[level].clone();

		// 注意:提取特征点的时候,使用的是清晰的原图像;这里计算描述子的时候,为了避免图像噪声的影响,使用了高斯模糊
        GaussianBlur(workingMat, 		//源图像
					 workingMat, 		//输出图像
					 Size(7, 7), 		//高斯滤波器kernel大小,必须为正的奇数
					 2, 				//高斯滤波在x方向的标准差
					 2, 				//高斯滤波在y方向的标准差
					 BORDER_REFLECT_101);//边缘拓展点插值类型

        高斯模糊意义:去掉干扰点

        参数意义:我们高斯模糊原图像(金字塔第i层的图像,深拷贝到了workingMat 变量中),并将高斯模糊后的图像也放入到该变量中。

2.6 计算高斯模糊后图像的描述子 

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

		// 更新偏移量的值 
        offset += nkeypointsLevel;

ORB-SLAM2 ---- computeDescriptors函数_Courage2022的博客-CSDN博客

2.7 对非第0层图像中的特征点的坐标恢复到第0层图像(原图像)的坐标系下 

// 对于第0层的图像特征点,他们的坐标就不需要再进行恢复了
        if (level != 0)
        {
			// 获取当前图层上的缩放系数
            float scale = mvScaleFactor[level];
            // 遍历本层所有的特征点
            for (vector::iterator keypoint = keypoints.begin(),
                 keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)
				// 特征点本身直接乘缩放倍数就可以了
                keypoint->pt *= scale;
        }
        
        // And add the keypoints to the output
        // 将keypoints中内容插入到_keypoints 的末尾
        // keypoint其实是对allkeypoints中每层图像中特征点的引用,这样allkeypoints中的所有特征点在这里被转存到输出的_keypoints
        _keypoints.insert(_keypoints.end(), keypoints.begin(), keypoints.end());

        将特征点乘以缩放系数得到对应层的特征点坐标。

        将提取到的特征点放入_keypoints容器交付上层。

你可能感兴趣的:(orb-slam2,计算机视觉,c++,orb-slam,slam,图像处理)