首页 > 编程语言 >ORB-SLAM2源码学习:ORBextractor.cc 逻辑梳理

ORB-SLAM2源码学习:ORBextractor.cc 逻辑梳理

时间:2024-10-10 23:17:31浏览次数:16  
标签:10 13 12 ORBextractor cc 源码 lNodes correlation mean

前言 

温馨提示:请在看过源代码的情况下看此文章学习效果更好。

ORBextractor.cc这个源文件定义函数众多,要是按照源码的顺序进行学习会抓不住重点,下面是我梳理的四个核心函数。

一、主入口函数:operator()

说明:主入口函数直接调用了ComputePyramid()、ComputeKeyPointsOctTree()、computeDescriptors()三大核心函数,间接调用了这三大核心函数所调用的函数,可以说是覆盖了所有的函数。

主要逻辑:

1.接收输入图像(_image)和可选的_mask

2.调用ComputePyramid() 函数生成图像金字塔。

3.调用ComputeKeyPointsOctTree()函数(老版采用ComputeKeyPointsOld())来计算关键(Keypoints)点并存储在 allKeypoints 中。

4.根据每层的特征点数量计算总特征点数量,并为描述子(Descriptors)分配内存。

5. 遍历金字塔的每一层。

   a.对当前层图像应用高斯模糊GaussianBlur(),得到workingMat。

   b.调用 computeDescriptors() 函数计算当前层的描述子(Descriptors)。

   c.根据层级缩放特征点坐标,并将其添加到输出的 _keypoints 向量中。

二、构建金字塔函数:ComputePyramid()

1.为每一层计算并存储缩小后的图像。

2.使用resize() 函数进行图像缩放,并使用 copyMakeBorder() 函数添加边框,以处理边缘效应。

三、提取关键点函数:ComputeKeyPointsOctTree

1.根据预设的特征点数量和图像边界,均匀地在图像区域中分配特征点。

2.对特征点进行后处理,包括设置特征点的坐标、尺寸等信息,以便在多层金字塔中存储。

3.调用 computeOrientation() 函数为特征点计算方向。

四、描述子计算函数:computeDescriptors()

1.接收图像、特征点列表和一个预先分配的描述符矩阵。

2.对每个特征点调用computeOrbDescriptor()函数,以计算其描述符,并存储在对应的行中。

五、已标注的代码

注意:这部分的代码是我根据四大核心函数调用的逻辑更改了原始代码的函数位置(目的是能够梳理的更加通常些),请注意甄别。

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <vector>
#include "ORBextractor.h"
using namespace cv;
using namespace std;
namespace ORB_SLAM2
{


/*主入口函数operator()*/


// InputArray是OpenCV 提供的一个轻量级封装类,主要用于简化函数参数的处理。
// 它允许函数接受多种数据类型,例如 cv::Mat、std::vector、cv::UMat、甚至是自定义类型。通过这个封装,OpenCV 函数可以更灵活地处理输入。
// 用户可以直接传递 cv::Mat、std::vector 或其他支持的数据结构,而不需要进行额外的转换。
void ORBextractor::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _keypoints,
                      OutputArray _descriptors)
{
    if(_image.empty()) // empty() 是 cv::Mat 类的方法,检查输入图像是否为空
        return;// 如果 _image 是空的,函数将直接返回,不会执行后续的代码。

    Mat image = _image.getMat();// getMat() 是 InputArray 类的一个方法,用于将封装的图像数据提取为 cv::Mat 类型。

    // assert 是一个断言语句,如果条件为真,程序将继续执行;如果条件为假,程序会中断,并输出错误信息。
    // type() 是 cv::Mat 类的一个成员函数,它返回图像的类型(即图像的深度和通道数)。
    assert(image.type() == CV_8UC1 );

    // Pre-compute the scale pyramid 预先计算尺度金字塔
    ComputePyramid(image);// 调用成员函数ComputePyramid()构建图像金字塔,具体实现在下方。

    vector < vector<KeyPoint> > allKeypoints;// 定义一个二维向量,外层的 vector 存储不同层级的特征点,每个内层的 vector<KeyPoint> 存储在该层级中提取到的特征点。

    ComputeKeyPointsOctTree(allKeypoints);// 调用成员函数ComputeKeyPointsOctTree()计算keypoints,具体实现在下方。
    //ComputeKeyPointsOld(allKeypoints);// 老版计算关键点的成员函数

    Mat descriptors;// Mat 是 OpenCV 中的一个主要数据结构,用于表示图像和矩阵。声明一个变量descriptors用于存储提取的特征点描述子。

    int nkeypoints = 0;// 声明一个变量nkeypoints用于存储所有层级的关键点总数。nkeypoints储存的是关键点数量,allKeypoints储存的是关键点的向量。
    for (int level = 0; level < nlevels; ++level)// 遍历金子塔的每一层(nlevels)。
        nkeypoints += (int)allKeypoints[level].size();//.size() 是用于获取容器中元素数量的方法。将每一层的关键点累加到nkeypoints上。
    if( nkeypoints == 0 )
        _descriptors.release();// release() 是 OpenCV 的 cv::Mat 类中的一个方法,用于释放分配给该矩阵的内存。
    else
    {
        _descriptors.create(nkeypoints, 32, CV_8U);// create() 是 OpenCV 中 cv::Mat 类的方法,用于分配内存并初始化矩阵。
        //这里nkeypoints作为矩阵的行数。32作为矩阵的列数,表示每个特征点的描述子大小为 32 字节。

        descriptors = _descriptors.getMat();// getMat() 是 OpenCV 中cv::OutputArray 类的一个方法,用于返回一个 cv::Mat 对象。
    }

    _keypoints.clear();// clear() 是 std::vector 的成员函数,用于清空向量的内容。调用这个函数会移除 _keypoints 向量中所有的元素,但不会释放已分配的内存。
    // 使用 clear() 是为了确保 _keypoints 在被重新使用前是空的,避免因之前的内容干扰到后续的操作。特别是在多次调用 operator() 函数时

    _keypoints.reserve(nkeypoints);// reserve() 是 std::vector 的一个成员函数,其目的是为向量预留空间。

    int offset = 0;// offset用于记录当前描述子矩阵中的偏移量。
    for (int level = 0; level < nlevels; ++level)
    {
        vector<KeyPoint>& keypoints = allKeypoints[level];// keypoints是当前层(level)检测到的特征点的引用。
        // keypoints 是一个局部引用变量,它的生命周期仅限于当前的循环迭代。
        // 在每次循环开始时,keypoints 会被更新为 allKeypoints 中当前 level 层的特征点集合。
        // 一旦循环迭代结束,keypoints 引用的特征点集合不会被释放,实际上仍然存在于 allKeypoints 中。
        // 下一次循环迭代时,keypoints 将会被赋值为新的引用,但不会影响先前层级的特征点数据。

        int nkeypointsLevel = (int)keypoints.size();// nkeypointsLevel 是当前层的特征点数量。

        if(nkeypointsLevel==0)// 如果当前层没有检测到特征点,则跳过该层,继续下一层的处理。
            continue;

        // preprocess the resized image
        // 预处理调整后的图像
        Mat workingMat = mvImagePyramid[level].clone();// 将图像金字塔中当前层的图像拷贝到 workingMat。

        // GaussianBlur 函数是 OpenCV 库中提供的一个函数,用于对图像应用高斯模糊。
        // 对 workingMat 进行高斯模糊处理,滤波器的大小为 7x7,标准差为 2。BORDER_REFLECT_101 确保边界像素的处理不会受到边缘影响。
        // 这一步的目的是减少噪声,使得特征点检测和描述符计算更加稳定。
        GaussianBlur(workingMat, workingMat, Size(7, 7), 2, 2, BORDER_REFLECT_101);

        // Compute the descriptors 计算描述子。
        Mat desc = descriptors.rowRange(offset, offset + nkeypointsLevel);// 定义了一个子矩阵 desc,它从 descriptors 矩阵中选取当前层所有特征点对应的行。
        //rowRange(offset, offset + nkeypointsLevel): 这是 OpenCV 中 cv::Mat 类的一个成员函数,返回 descriptors 矩阵中从 offset 行到 offset + nkeypointsLevel - 1 行的所有行。

        computeDescriptors(workingMat, keypoints, desc, pattern);// 调用 computeDescriptors 函数,具体定义在下方。
        // 利用 1.经过处理(高斯模糊)的当前层图像workingMat 2.当前层中检测到的关键点keypoints 3.用于存储当前层特征点的描述子矩阵desc 4.定义了描述符计算过程中使用的特征点邻域的模式pattern 作为参数。

        offset += nkeypointsLevel;// 更新 offset,将其增加当前层特征点的数量,以便下一层特征点的描述符可以存储在正确的矩阵位置。

        // Scale keypoint coordinates 缩放特征点的坐标
        if (level != 0)// 第0层的特征点坐标已经与原始图像的坐标一致,因此不需要进行缩放。
        {
            float scale = mvScaleFactor[level]; //getScale(level, firstLevel, scaleFactor);
            for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),
                 keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)
                 keypoint->pt *= scale;// 对于每个特征点,使用scale对其坐标(pt)进行缩放。
                // pt 是 KeyPoint 结构体的一个成员,通常是一个 cv::Point2f 类型,表示特征点在图像中的位置。
        }
        // And add the keypoints to the output 将所有关键点添加到输出中。
        _keypoints.insert(_keypoints.end(), keypoints.begin(), keypoints.end());// insert 是 STL(标准模板库)中的一个成员函数,用于向容器中插入元素。
    }
}


/*构建金字塔函数ComputePyramid()*/


// 构建图像金字塔。
void ORBextractor::ComputePyramid(cv::Mat image)//输入图像cv::Mat image。不一定是名为image的变量,但一定是cv::Mat类型的图像信息的矩阵。
{
    for (int level = 0; level < nlevels; ++level)//遍历每一层。
    {
        float scale = mvInvScaleFactor[level];//这里应该是0-1之间的小数。
        Size sz(cvRound((float)image.cols*scale), cvRound((float)image.rows*scale));// Size 是 OpenCV 中的一个数据结构,用于表示矩形区域的宽度和高度。
        // 通过 (float)image.cols * scale 和 (float)image.rows * scale 计算出缩放后图像的宽度和高度,并使用 cvRound() 对计算结果进行四舍五入。

        Size wholeSize(sz.width + EDGE_THRESHOLD*2, sz.height + EDGE_THRESHOLD*2);// 计算包含边缘扩展后的图像尺寸。
        Mat temp(wholeSize, image.type()), masktemp;// 定义了一个名为temp的Mat对象,并使用了图像的类型和 wholeSize 初始化它。

        mvImagePyramid[level] = temp(Rect(EDGE_THRESHOLD, EDGE_THRESHOLD, sz.width, sz.height));// Rect 是 OpenCV 中的一个类,用于表示一个矩形区域。
        // 第一个和第二个参数是矩形的左上角的坐标,均为 EDGE_THRESHOLD。
        // 第三个和第四个参数分别是矩形的宽度和高度,均为 sz.width 和 sz.height。

        // Compute the resized image 在图像金字塔的不同层级上计算和生成缩小版本的图像。
        if( level != 0 )
        {
            resize(mvImagePyramid[level-1], mvImagePyramid[level], sz, 0, 0, INTER_LINEAR);// resize 函数是 OpenCV 中用于调整图像大小的函数。

            copyMakeBorder(mvImagePyramid[level], temp, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD,
                           BORDER_REFLECT_101+BORDER_ISOLATED);
            // copyMakeBorder 函数被用来为每一层的图像添加边框。即使在后续的处理(如滤波或特征检测)过程中,金字塔图像的边缘部分也会有足够的邻域信息,从而避免边缘效应带来的计算误差。
            //BORDER_ISOLATED 选项确保边缘区域不会受到外部环境(即边界外的像素)的影响。即使在图像的边缘添加了边框,边缘的计算也只会依赖于图像的有效区域,不会被外部像素干扰。
        }
        else
        {
            copyMakeBorder(image, temp, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD,
                           BORDER_REFLECT_101);
        }
    }

}

/*
    _nfeatures:ORB提取器要检测的特征点数量。
    _scaleFactor:图像金字塔中相邻层之间的缩放因子。
    _nlevels:图像金字塔的层数。
    _iniThFAST:初始的FAST角点检测阈值。
    _minThFAST:最低的FAST角点检测阈值。
*/
ORBextractor::ORBextractor(int _nfeatures, float _scaleFactor, int _nlevels,
         int _iniThFAST, int _minThFAST):
    nfeatures(_nfeatures), scaleFactor(_scaleFactor), nlevels(_nlevels),
    iniThFAST(_iniThFAST), minThFAST(_minThFAST)
{
    //resize 函数是 C++ 标准库中的 std::vector 类的一个成员函数,用于调整 vector 的大小。
    mvScaleFactor.resize(nlevels);//调整缩放因子容器的大小。
    mvLevelSigma2.resize(nlevels);//调整缩放因子平方容器的大小。
    mvScaleFactor[0]=1.0f;//将 mvScaleFactor 的第一个元素(索引为 0)设置为 1.0f。基准层缩放因子。
    mvLevelSigma2[0]=1.0f;//将 mvLevelSigma2 的第一个元素设置为 1.0f。基准层缩放因子。

    // 遍历金字塔每一层获取每一层的缩放因子和相应的缩放平方值。
    for(int i=1; i<nlevels; i++)
    {
        mvScaleFactor[i]=mvScaleFactor[i-1]*scaleFactor;
        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];
    }

    mvImagePyramid.resize(nlevels);// 调整图像金字塔容器的大小。
    mnFeaturesPerLevel.resize(nlevels);// 存储每个金字塔层级中检测到的特征点数量。
    float factor = 1.0f / scaleFactor;
    /*
      图像的金字塔层数越高,对应层的图像分辨率越低,面积越小,提取到的特征点越少。分配策略按面积决定。
      特征点分配计算(按照面积的开方来均摊特征点,若按面积则factor应该要平方)
     */
    // 计算第0层的特征点数。
    float nDesiredFeaturesPerScale = nfeatures*(1 - factor)/(1 - (float)pow((double)factor, (double)nlevels));

    int sumFeatures = 0;//初始化一个整型变量 sumFeatures,用于计算累计前面几层提取的特征数量。
    for( int level = 0; level < nlevels-1; level++ )//遍历每一层,计算除最后一层的特征点数。
    {
        mnFeaturesPerLevel[level] = cvRound(nDesiredFeaturesPerScale);// 四舍五入每一层的特征点。
        sumFeatures += mnFeaturesPerLevel[level];// 累加特征点。
        nDesiredFeaturesPerScale *= factor;// 在0层的基础上×factor即可得到下一层。
    }
    /*
      由于最后一层没有缩放因子,不能通过公式进行计算,要通过总数间接计算最后一层应该提取的特征数量。
      mnFeaturesPerLevel 是一个数组,所以 nlevels - 1 指向最后一层。
      如果已经在其他层提取的特征数量 (sumFeatures) 小于总特征数量 (nfeatures),则最后一层提取的特征数量就是 nfeatures - sumFeatures。
      如果已经提取的特征数量已经达到了总数量,那么最后一层不需要再提取任何特征,此时返回值为 0。使用 std::max 确保不会出现负数的情况。使用 std::max 确保不会出现负数的情况
    */
    mnFeaturesPerLevel[nlevels-1] = std::max(nfeatures - sumFeatures, 0);

    const int npoints = 512;
    const Point* pattern0 = (const Point*)bit_pattern_31_;
    std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern));

    //This is for orientation
    //说明接下来的代码是为了计算关键点的方向。关键点的方向通常用于描述关键点的旋转不变性,确保在不同的图像旋转条件下能正确匹配。
    //pre-compute the end of a row in a circular patch
    //先计算一个圆形补丁中每一行的结束位置。圆形补丁通常用于图像特征提取,在关键点周围生成一个圆形区域用于描述。
    //HALF_PATCH_SIZE + 1 的选择是为了确保 umax 向量能够正确存储与补丁相关的所有可能的 u 值,覆盖从 0 到 HALF_PATCH_SIZE 的范围。
    umax.resize(HALF_PATCH_SIZE + 1);//即容量为16(储存了0-15共16个元素)。

    /*
      cvFloor 是 OpenCV 库中的一个函数,用于对浮点数进行向下取整操作。
      cvCeil 是 OpenCV 库中的一个函数,用于对给定的浮点数进行向上取整。
      sqrt 是一个数学函数,属于 <cmath> 头文件,用于计算给定数值的平方根。
    */
    // 计算四分之一圆周长一半附近的y轴坐标( vmax 、vmin )。
    int v, v0, vmax = cvFloor(HALF_PATCH_SIZE * sqrt(2.f) / 2 + 1);// 确保 vmax 是一个整数。
    int vmin = cvCeil(HALF_PATCH_SIZE * sqrt(2.f) / 2);// 确保 vmin 是一个整数。
    const double hp2 = HALF_PATCH_SIZE*HALF_PATCH_SIZE;// 计算半径平方。

    //分别从四分之一圆的两边向中心遍历
    for (v = 0; v <= vmax; ++v)
        umax[v] = cvRound(sqrt(hp2 - v * v));//计算umax 数组的第 v 个位置能存贮的u的最大值(四舍五入)。
    // Make sure we are symmetric 确保对称。
    for (v = HALF_PATCH_SIZE, v0 = 0; v >= vmin; --v)
    {
        while (umax[v0] == umax[v0 + 1])
            ++v0;
        umax[v] = v0;
        ++v0;
    }
}

static int bit_pattern_31_[256*4] =
{
    8,-3, 9,5/*mean (0), correlation (0)*/,
    4,2, 7,-12/*mean (1.12461e-05), correlation (0.0437584)*/,
    -11,9, -8,2/*mean (3.37382e-05), correlation (0.0617409)*/,
    7,-12, 12,-13/*mean (5.62303e-05), correlation (0.0636977)*/,
    2,-13, 2,12/*mean (0.000134953), correlation (0.085099)*/,
    1,-7, 1,6/*mean (0.000528565), correlation (0.0857175)*/,
    -2,-10, -2,-4/*mean (0.0188821), correlation (0.0985774)*/,
    -13,-13, -11,-8/*mean (0.0363135), correlation (0.0899616)*/,
    -13,-3, -12,-9/*mean (0.121806), correlation (0.099849)*/,
    10,4, 11,9/*mean (0.122065), correlation (0.093285)*/,
    -13,-8, -8,-9/*mean (0.162787), correlation (0.0942748)*/,
    -11,7, -9,12/*mean (0.21561), correlation (0.0974438)*/,
    7,7, 12,6/*mean (0.160583), correlation (0.130064)*/,
    -4,-5, -3,0/*mean (0.228171), correlation (0.132998)*/,
    -13,2, -12,-3/*mean (0.00997526), correlation (0.145926)*/,
    -9,0, -7,5/*mean (0.198234), correlation (0.143636)*/,
    12,-6, 12,-1/*mean (0.0676226), correlation (0.16689)*/,
    -3,6, -2,12/*mean (0.166847), correlation (0.171682)*/,
    -6,-13, -4,-8/*mean (0.101215), correlation (0.179716)*/,
    11,-13, 12,-8/*mean (0.200641), correlation (0.192279)*/,
    4,7, 5,1/*mean (0.205106), correlation (0.186848)*/,
    5,-3, 10,-3/*mean (0.234908), correlation (0.192319)*/,
    3,-7, 6,12/*mean (0.0709964), correlation (0.210872)*/,
    -8,-7, -6,-2/*mean (0.0939834), correlation (0.212589)*/,
    -2,11, -1,-10/*mean (0.127778), correlation (0.20866)*/,
    -13,12, -8,10/*mean (0.14783), correlation (0.206356)*/,
    -7,3, -5,-3/*mean (0.182141), correlation (0.198942)*/,
    -4,2, -3,7/*mean (0.188237), correlation (0.21384)*/,
    -10,-12, -6,11/*mean (0.14865), correlation (0.23571)*/,
    5,-12, 6,-7/*mean (0.222312), correlation (0.23324)*/,
    5,-6, 7,-1/*mean (0.229082), correlation (0.23389)*/,
    1,0, 4,-5/*mean (0.241577), correlation (0.215286)*/,
    9,11, 11,-13/*mean (0.00338507), correlation (0.251373)*/,
    4,7, 4,12/*mean (0.131005), correlation (0.257622)*/,
    2,-1, 4,4/*mean (0.152755), correlation (0.255205)*/,
    -4,-12, -2,7/*mean (0.182771), correlation (0.244867)*/,
    -8,-5, -7,-10/*mean (0.186898), correlation (0.23901)*/,
    4,11, 9,12/*mean (0.226226), correlation (0.258255)*/,
    0,-8, 1,-13/*mean (0.0897886), correlation (0.274827)*/,
    -13,-2, -8,2/*mean (0.148774), correlation (0.28065)*/,
    -3,-2, -2,3/*mean (0.153048), correlation (0.283063)*/,
    -6,9, -4,-9/*mean (0.169523), correlation (0.278248)*/,
    8,12, 10,7/*mean (0.225337), correlation (0.282851)*/,
    0,9, 1,3/*mean (0.226687), correlation (0.278734)*/,
    7,-5, 11,-10/*mean (0.00693882), correlation (0.305161)*/,
    -13,-6, -11,0/*mean (0.0227283), correlation (0.300181)*/,
    10,7, 12,1/*mean (0.125517), correlation (0.31089)*/,
    -6,-3, -6,12/*mean (0.131748), correlation (0.312779)*/,
    10,-9, 12,-4/*mean (0.144827), correlation (0.292797)*/,
    -13,8, -8,-12/*mean (0.149202), correlation (0.308918)*/,
    -13,0, -8,-4/*mean (0.160909), correlation (0.310013)*/,
    3,3, 7,8/*mean (0.177755), correlation (0.309394)*/,
    5,7, 10,-7/*mean (0.212337), correlation (0.310315)*/,
    -1,7, 1,-12/*mean (0.214429), correlation (0.311933)*/,
    3,-10, 5,6/*mean (0.235807), correlation (0.313104)*/,
    2,-4, 3,-10/*mean (0.00494827), correlation (0.344948)*/,
    -13,0, -13,5/*mean (0.0549145), correlation (0.344675)*/,
    -13,-7, -12,12/*mean (0.103385), correlation (0.342715)*/,
    -13,3, -11,8/*mean (0.134222), correlation (0.322922)*/,
    -7,12, -4,7/*mean (0.153284), correlation (0.337061)*/,
    6,-10, 12,8/*mean (0.154881), correlation (0.329257)*/,
    -9,-1, -7,-6/*mean (0.200967), correlation (0.33312)*/,
    -2,-5, 0,12/*mean (0.201518), correlation (0.340635)*/,
    -12,5, -7,5/*mean (0.207805), correlation (0.335631)*/,
    3,-10, 8,-13/*mean (0.224438), correlation (0.34504)*/,
    -7,-7, -4,5/*mean (0.239361), correlation (0.338053)*/,
    -3,-2, -1,-7/*mean (0.240744), correlation (0.344322)*/,
    2,9, 5,-11/*mean (0.242949), correlation (0.34145)*/,
    -11,-13, -5,-13/*mean (0.244028), correlation (0.336861)*/,
    -1,6, 0,-1/*mean (0.247571), correlation (0.343684)*/,
    5,-3, 5,2/*mean (0.000697256), correlation (0.357265)*/,
    -4,-13, -4,12/*mean (0.00213675), correlation (0.373827)*/,
    -9,-6, -9,6/*mean (0.0126856), correlation (0.373938)*/,
    -12,-10, -8,-4/*mean (0.0152497), correlation (0.364237)*/,
    10,2, 12,-3/*mean (0.0299933), correlation (0.345292)*/,
    7,12, 12,12/*mean (0.0307242), correlation (0.366299)*/,
    -7,-13, -6,5/*mean (0.0534975), correlation (0.368357)*/,
    -4,9, -3,4/*mean (0.099865), correlation (0.372276)*/,
    7,-1, 12,2/*mean (0.117083), correlation (0.364529)*/,
    -7,6, -5,1/*mean (0.126125), correlation (0.369606)*/,
    -13,11, -12,5/*mean (0.130364), correlation (0.358502)*/,
    -3,7, -2,-6/*mean (0.131691), correlation (0.375531)*/,
    7,-8, 12,-7/*mean (0.160166), correlation (0.379508)*/,
    -13,-7, -11,-12/*mean (0.167848), correlation (0.353343)*/,
    1,-3, 12,12/*mean (0.183378), correlation (0.371916)*/,
    2,-6, 3,0/*mean (0.228711), correlation (0.371761)*/,
    -4,3, -2,-13/*mean (0.247211), correlation (0.364063)*/,
    -1,-13, 1,9/*mean (0.249325), correlation (0.378139)*/,
    7,1, 8,-6/*mean (0.000652272), correlation (0.411682)*/,
    1,-1, 3,12/*mean (0.00248538), correlation (0.392988)*/,
    9,1, 12,6/*mean (0.0206815), correlation (0.386106)*/,
    -1,-9, -1,3/*mean (0.0364485), correlation (0.410752)*/,
    -13,-13, -10,5/*mean (0.0376068), correlation (0.398374)*/,
    7,7, 10,12/*mean (0.0424202), correlation (0.405663)*/,
    12,-5, 12,9/*mean (0.0942645), correlation (0.410422)*/,
    6,3, 7,11/*mean (0.1074), correlation (0.413224)*/,
    5,-13, 6,10/*mean (0.109256), correlation (0.408646)*/,
    2,-12, 2,3/*mean (0.131691), correlation (0.416076)*/,
    3,8, 4,-6/*mean (0.165081), correlation (0.417569)*/,
    2,6, 12,-13/*mean (0.171874), correlation (0.408471)*/,
    9,-12, 10,3/*mean (0.175146), correlation (0.41296)*/,
    -8,4, -7,9/*mean (0.183682), correlation (0.402956)*/,
    -11,12, -4,-6/*mean (0.184672), correlation (0.416125)*/,
    1,12, 2,-8/*mean (0.191487), correlation (0.386696)*/,
    6,-9, 7,-4/*mean (0.192668), correlation (0.394771)*/,
    2,3, 3,-2/*mean (0.200157), correlation (0.408303)*/,
    6,3, 11,0/*mean (0.204588), correlation (0.411762)*/,
    3,-3, 8,-8/*mean (0.205904), correlation (0.416294)*/,
    7,8, 9,3/*mean (0.213237), correlation (0.409306)*/,
    -11,-5, -6,-4/*mean (0.243444), correlation (0.395069)*/,
    -10,11, -5,10/*mean (0.247672), correlation (0.413392)*/,
    -5,-8, -3,12/*mean (0.24774), correlation (0.411416)*/,
    -10,5, -9,0/*mean (0.00213675), correlation (0.454003)*/,
    8,-1, 12,-6/*mean (0.0293635), correlation (0.455368)*/,
    4,-6, 6,-11/*mean (0.0404971), correlation (0.457393)*/,
    -10,12, -8,7/*mean (0.0481107), correlation (0.448364)*/,
    4,-2, 6,7/*mean (0.050641), correlation (0.455019)*/,
    -2,0, -2,12/*mean (0.0525978), correlation (0.44338)*/,
    -5,-8, -5,2/*mean (0.0629667), correlation (0.457096)*/,
    7,-6, 10,12/*mean (0.0653846), correlation (0.445623)*/,
    -9,-13, -8,-8/*mean (0.0858749), correlation (0.449789)*/,
    -5,-13, -5,-2/*mean (0.122402), correlation (0.450201)*/,
    8,-8, 9,-13/*mean (0.125416), correlation (0.453224)*/,
    -9,-11, -9,0/*mean (0.130128), correlation (0.458724)*/,
    1,-8, 1,-2/*mean (0.132467), correlation (0.440133)*/,
    7,-4, 9,1/*mean (0.132692), correlation (0.454)*/,
    -2,1, -1,-4/*mean (0.135695), correlation (0.455739)*/,
    11,-6, 12,-11/*mean (0.142904), correlation (0.446114)*/,
    -12,-9, -6,4/*mean (0.146165), correlation (0.451473)*/,
    3,7, 7,12/*mean (0.147627), correlation (0.456643)*/,
    5,5, 10,8/*mean (0.152901), correlation (0.455036)*/,
    0,-4, 2,8/*mean (0.167083), correlation (0.459315)*/,
    -9,12, -5,-13/*mean (0.173234), correlation (0.454706)*/,
    0,7, 2,12/*mean (0.18312), correlation (0.433855)*/,
    -1,2, 1,7/*mean (0.185504), correlation (0.443838)*/,
    5,11, 7,-9/*mean (0.185706), correlation (0.451123)*/,
    3,5, 6,-8/*mean (0.188968), correlation (0.455808)*/,
    -13,-4, -8,9/*mean (0.191667), correlation (0.459128)*/,
    -5,9, -3,-3/*mean (0.193196), correlation (0.458364)*/,
    -4,-7, -3,-12/*mean (0.196536), correlation (0.455782)*/,
    6,5, 8,0/*mean (0.1972), correlation (0.450481)*/,
    -7,6, -6,12/*mean (0.199438), correlation (0.458156)*/,
    -13,6, -5,-2/*mean (0.211224), correlation (0.449548)*/,
    1,-10, 3,10/*mean (0.211718), correlation (0.440606)*/,
    4,1, 8,-4/*mean (0.213034), correlation (0.443177)*/,
    -2,-2, 2,-13/*mean (0.234334), correlation (0.455304)*/,
    2,-12, 12,12/*mean (0.235684), correlation (0.443436)*/,
    -2,-13, 0,-6/*mean (0.237674), correlation (0.452525)*/,
    4,1, 9,3/*mean (0.23962), correlation (0.444824)*/,
    -6,-10, -3,-5/*mean (0.248459), correlation (0.439621)*/,
    -3,-13, -1,1/*mean (0.249505), correlation (0.456666)*/,
    7,5, 12,-11/*mean (0.00119208), correlation (0.495466)*/,
    4,-2, 5,-7/*mean (0.00372245), correlation (0.484214)*/,
    -13,9, -9,-5/*mean (0.00741116), correlation (0.499854)*/,
    7,1, 8,6/*mean (0.0208952), correlation (0.499773)*/,
    7,-8, 7,6/*mean (0.0220085), correlation (0.501609)*/,
    -7,-4, -7,1/*mean (0.0233806), correlation (0.496568)*/,
    -8,11, -7,-8/*mean (0.0236505), correlation (0.489719)*/,
    -13,6, -12,-8/*mean (0.0268781), correlation (0.503487)*/,
    2,4, 3,9/*mean (0.0323324), correlation (0.501938)*/,
    10,-5, 12,3/*mean (0.0399235), correlation (0.494029)*/,
    -6,-5, -6,7/*mean (0.0420153), correlation (0.486579)*/,
    8,-3, 9,-8/*mean (0.0548021), correlation (0.484237)*/,
    2,-12, 2,8/*mean (0.0616622), correlation (0.496642)*/,
    -11,-2, -10,3/*mean (0.0627755), correlation (0.498563)*/,
    -12,-13, -7,-9/*mean (0.0829622), correlation (0.495491)*/,
    -11,0, -10,-5/*mean (0.0843342), correlation (0.487146)*/,
    5,-3, 11,8/*mean (0.0929937), correlation (0.502315)*/,
    -2,-13, -1,12/*mean (0.113327), correlation (0.48941)*/,
    -1,-8, 0,9/*mean (0.132119), correlation (0.467268)*/,
    -13,-11, -12,-5/*mean (0.136269), correlation (0.498771)*/,
    -10,-2, -10,11/*mean (0.142173), correlation (0.498714)*/,
    -3,9, -2,-13/*mean (0.144141), correlation (0.491973)*/,
    2,-3, 3,2/*mean (0.14892), correlation (0.500782)*/,
    -9,-13, -4,0/*mean (0.150371), correlation (0.498211)*/,
    -4,6, -3,-10/*mean (0.152159), correlation (0.495547)*/,
    -4,12, -2,-7/*mean (0.156152), correlation (0.496925)*/,
    -6,-11, -4,9/*mean (0.15749), correlation (0.499222)*/,
    6,-3, 6,11/*mean (0.159211), correlation (0.503821)*/,
    -13,11, -5,5/*mean (0.162427), correlation (0.501907)*/,
    11,11, 12,6/*mean (0.16652), correlation (0.497632)*/,
    7,-5, 12,-2/*mean (0.169141), correlation (0.484474)*/,
    -1,12, 0,7/*mean (0.169456), correlation (0.495339)*/,
    -4,-8, -3,-2/*mean (0.171457), correlation (0.487251)*/,
    -7,1, -6,7/*mean (0.175), correlation (0.500024)*/,
    -13,-12, -8,-13/*mean (0.175866), correlation (0.497523)*/,
    -7,-2, -6,-8/*mean (0.178273), correlation (0.501854)*/,
    -8,5, -6,-9/*mean (0.181107), correlation (0.494888)*/,
    -5,-1, -4,5/*mean (0.190227), correlation (0.482557)*/,
    -13,7, -8,10/*mean (0.196739), correlation (0.496503)*/,
    1,5, 5,-13/*mean (0.19973), correlation (0.499759)*/,
    1,0, 10,-13/*mean (0.204465), correlation (0.49873)*/,
    9,12, 10,-1/*mean (0.209334), correlation (0.49063)*/,
    5,-8, 10,-9/*mean (0.211134), correlation (0.503011)*/,
    -1,11, 1,-13/*mean (0.212), correlation (0.499414)*/,
    -9,-3, -6,2/*mean (0.212168), correlation (0.480739)*/,
    -1,-10, 1,12/*mean (0.212731), correlation (0.502523)*/,
    -13,1, -8,-10/*mean (0.21327), correlation (0.489786)*/,
    8,-11, 10,-6/*mean (0.214159), correlation (0.488246)*/,
    2,-13, 3,-6/*mean (0.216993), correlation (0.50287)*/,
    7,-13, 12,-9/*mean (0.223639), correlation (0.470502)*/,
    -10,-10, -5,-7/*mean (0.224089), correlation (0.500852)*/,
    -10,-8, -8,-13/*mean (0.228666), correlation (0.502629)*/,
    4,-6, 8,5/*mean (0.22906), correlation (0.498305)*/,
    3,12, 8,-13/*mean (0.233378), correlation (0.503825)*/,
    -4,2, -3,-3/*mean (0.234323), correlation (0.476692)*/,
    5,-13, 10,-12/*mean (0.236392), correlation (0.475462)*/,
    4,-13, 5,-1/*mean (0.236842), correlation (0.504132)*/,
    -9,9, -4,3/*mean (0.236977), correlation (0.497739)*/,
    0,3, 3,-9/*mean (0.24314), correlation (0.499398)*/,
    -12,1, -6,1/*mean (0.243297), correlation (0.489447)*/,
    3,2, 4,-8/*mean (0.00155196), correlation (0.553496)*/,
    -10,-10, -10,9/*mean (0.00239541), correlation (0.54297)*/,
    8,-13, 12,12/*mean (0.0034413), correlation (0.544361)*/,
    -8,-12, -6,-5/*mean (0.003565), correlation (0.551225)*/,
    2,2, 3,7/*mean (0.00835583), correlation (0.55285)*/,
    10,6, 11,-8/*mean (0.00885065), correlation (0.540913)*/,
    6,8, 8,-12/*mean (0.0101552), correlation (0.551085)*/,
    -7,10, -6,5/*mean (0.0102227), correlation (0.533635)*/,
    -3,-9, -3,9/*mean (0.0110211), correlation (0.543121)*/,
    -1,-13, -1,5/*mean (0.0113473), correlation (0.550173)*/,
    -3,-7, -3,4/*mean (0.0140913), correlation (0.554774)*/,
    -8,-2, -8,3/*mean (0.017049), correlation (0.55461)*/,
    4,2, 12,12/*mean (0.01778), correlation (0.546921)*/,
    2,-5, 3,11/*mean (0.0224022), correlation (0.549667)*/,
    6,-9, 11,-13/*mean (0.029161), correlation (0.546295)*/,
    3,-1, 7,12/*mean (0.0303081), correlation (0.548599)*/,
    11,-1, 12,4/*mean (0.0355151), correlation (0.523943)*/,
    -3,0, -3,6/*mean (0.0417904), correlation (0.543395)*/,
    4,-11, 4,12/*mean (0.0487292), correlation (0.542818)*/,
    2,-4, 2,1/*mean (0.0575124), correlation (0.554888)*/,
    -10,-6, -8,1/*mean (0.0594242), correlation (0.544026)*/,
    -13,7, -11,1/*mean (0.0597391), correlation (0.550524)*/,
    -13,12, -11,-13/*mean (0.0608974), correlation (0.55383)*/,
    6,0, 11,-13/*mean (0.065126), correlation (0.552006)*/,
    0,-1, 1,4/*mean (0.074224), correlation (0.546372)*/,
    -13,3, -9,-2/*mean (0.0808592), correlation (0.554875)*/,
    -9,8, -6,-3/*mean (0.0883378), correlation (0.551178)*/,
    -13,-6, -8,-2/*mean (0.0901035), correlation (0.548446)*/,
    5,-9, 8,10/*mean (0.0949843), correlation (0.554694)*/,
    2,7, 3,-9/*mean (0.0994152), correlation (0.550979)*/,
    -1,-6, -1,-1/*mean (0.10045), correlation (0.552714)*/,
    9,5, 11,-2/*mean (0.100686), correlation (0.552594)*/,
    11,-3, 12,-8/*mean (0.101091), correlation (0.532394)*/,
    3,0, 3,5/*mean (0.101147), correlation (0.525576)*/,
    -1,4, 0,10/*mean (0.105263), correlation (0.531498)*/,
    3,-6, 4,5/*mean (0.110785), correlation (0.540491)*/,
    -13,0, -10,5/*mean (0.112798), correlation (0.536582)*/,
    5,8, 12,11/*mean (0.114181), correlation (0.555793)*/,
    8,9, 9,-6/*mean (0.117431), correlation (0.553763)*/,
    7,-4, 8,-12/*mean (0.118522), correlation (0.553452)*/,
    -10,4, -10,9/*mean (0.12094), correlation (0.554785)*/,
    7,3, 12,4/*mean (0.122582), correlation (0.555825)*/,
    9,-7, 10,-2/*mean (0.124978), correlation (0.549846)*/,
    7,0, 12,-2/*mean (0.127002), correlation (0.537452)*/,
    -1,-6, 0,-11/*mean (0.127148), correlation (0.547401)*/
};


/*提取关键点函数ComputeKeyPointsOctTree()*/


void ORBextractor::ComputeKeyPointsOctTree(vector<vector<KeyPoint> >& allKeypoints)
{
    allKeypoints.resize(nlevels);// allKeypoints是一个二维向量。

    const float W = 30;//定义网格单元数量,划分成30*30的网格。

    for (int level = 0; level < nlevels; ++level)// 遍历每一层图像金字塔。
    {
        // EDGE_THRESHOLD = 19
        // 提取FAST特征点而预留的计算半径3。
        const int minBorderX = EDGE_THRESHOLD-3;
        const int minBorderY = minBorderX;
        const int maxBorderX = mvImagePyramid[level].cols-EDGE_THRESHOLD+3;
        const int maxBorderY = mvImagePyramid[level].rows-EDGE_THRESHOLD+3;

        vector<cv::KeyPoint> vToDistributeKeys;
        vToDistributeKeys.reserve(nfeatures*10);

        // 提取FAST特征点设定的那个特定区域。
        const float width = (maxBorderX-minBorderX);
        const float height = (maxBorderY-minBorderY);

        const int nCols = width/W;
        const int nRows = height/W;
        // 计算实际的宽度和高度
        const int wCell = ceil(width/nCols);
        const int hCell = ceil(height/nRows);

        for(int i=0; i<nRows; i++)// 遍历在特定区域中划分的每一行网格单元。
        {
            const float iniY =minBorderY+i*hCell;// 计算初始 Y 坐标
            float maxY = iniY+hCell+6;

            // 确保不会超出规划好的网格边界。
            if(iniY>=maxBorderY-3)
                continue;
            if(maxY>maxBorderY)
                maxY = maxBorderY;

            for(int j=0; j<nCols; j++)
            {
                const float iniX =minBorderX+j*wCell;
                float maxX = iniX+wCell+6;
                if(iniX>=maxBorderX-6)
                    continue;
                if(maxX>maxBorderX)
                    maxX = maxBorderX;

                vector<cv::KeyPoint> vKeysCell;// 定义用于存储在当前网格单元中检测到的关键点。
                // 从图像金字塔的当前层(level)中提取出一个特定的矩形区域,矩形区域的左上角坐标为 (iniX, iniY),右下角坐标为 (maxX, maxY)。
                // 这代表了当前网格单元的区域。
                // iniThFAST这是FAST算法的初始阈值,用于决定是否将某个像素点作为关键点。
                // 将参数设置为 true 意味着在检测每个关键点时,算法不仅会确定其是否为关键点,还会计算出其强度。
                // 这可以帮助后续的特征描述和匹配过程选择更具代表性的特征点,提高整体性能和效果。
                FAST(mvImagePyramid[level].rowRange(iniY,maxY).colRange(iniX,maxX),
                     vKeysCell,iniThFAST,true);// 调用FAST算法

                // 如果第一次检测到的关键点为空(即没有找到有效的关键点),则会使用较低的阈值进行第二次检测。
                if(vKeysCell.empty())
                {
                    FAST(mvImagePyramid[level].rowRange(iniY,maxY).colRange(iniX,maxX),
                         vKeysCell,minThFAST,true);
                }

                if(!vKeysCell.empty())// 对 vKeysCell 中的每个关键点进行坐标偏移,然后将这些偏移后的关键点存入 vToDistributeKeys 中。
                {
                    for(vector<cv::KeyPoint>::iterator vit=vKeysCell.begin(); vit!=vKeysCell.end();vit++)//迭代器指到vKeysCell.begin();
                    {
                        (*vit).pt.x+=j*wCell;
                        (*vit).pt.y+=i*hCell;
                        vToDistributeKeys.push_back(*vit);
                    }
                }

            }
        }

        vector<KeyPoint> & keypoints = allKeypoints[level];
        keypoints.reserve(nfeatures);

        keypoints = DistributeOctTree(vToDistributeKeys, minBorderX, maxBorderX,
                                      minBorderY, maxBorderY,mnFeaturesPerLevel[level], level);//调用DistributeOctTree()函数,具体定义在下边。

        const int scaledPatchSize = PATCH_SIZE*mvScaleFactor[level];// 当前层级下,补丁的实际大小。PATCH_SIZE=31

        // Add border to coordinates and scale information
        const int nkps = keypoints.size();
        for(int i=0; i<nkps ; i++)
        {
            keypoints[i].pt.x+=minBorderX;// 对于每个关键点,更新其 x 坐标,添加一个最小边界(minBorderX)。
            keypoints[i].pt.y+=minBorderY;// 对于每个关键点,更新其 Y 坐标,添加一个最小边界(minBorderY)。
            keypoints[i].octave=level;// 设置当前关键点的层级(octave)
            keypoints[i].size = scaledPatchSize;// 更新每个关键点的大小,使用之前计算的 scaledPatchSize。
        }
    }

    // compute orientations 计算方向
    for (int level = 0; level < nlevels; ++level)
        computeOrientation(mvImagePyramid[level], allKeypoints[level], umax);// 调用computeOrientation()函数,具体定义在下边。
}

//基于八叉树(OctTree)对图像关键点进行分布式聚类的算法实现,用于分布和筛选一组图像关键点,以用于ORB(Oriented FAST and Rotated BRIEF)特征提取器。
vector<cv::KeyPoint> ORBextractor::DistributeOctTree(const vector<cv::KeyPoint>& vToDistributeKeys, const int &minX,
                                       const int &maxX, const int &minY, const int &maxY, const int &N, const int &level)
{
    // Compute how many initial nodes
    // 计算初始节点(initial nodes)的数量。
    // round:这是一个标准的数学函数,用于将浮点数四舍五入为最接近的整数。
    const int nIni = round(static_cast<float>(maxX-minX)/(maxY-minY));// nIni 通过图像的宽高比来决定,注意如果nIni不小心为0,则hx则会出错。

    const float hX = static_cast<float>(maxX-minX)/nIni;// hX 是每个初始节点的宽度。

    list<ExtractorNode> lNodes;// 创建了一个名为 lNodes 的双向链表,来存储和管理 ExtractorNode 对象。
    vector<ExtractorNode*> vpIniNodes;// 创建了一个名为 vpIniNodes 的动态数组(向量),该数组可以存储指向ExtractorNode类型的指针。
    vpIniNodes.resize(nIni);// resize 是 vector 的成员函数,用于改变 vector 的大小。

    for(int i=0; i<nIni; i++)
    {
        ExtractorNode ni;// 创建一个ExtractorNode(提取节点)对象ni。
        // 这块的区域和提取FAST角点的区域一样,均为“半径扩充图像”。
        ni.UL = cv::Point2i(hX*static_cast<float>(i),0);
        ni.UR = cv::Point2i(hX*static_cast<float>(i+1),0);
        ni.BL = cv::Point2i(ni.UL.x,maxY-minY);
        ni.BR = cv::Point2i(ni.UR.x,maxY-minY);
        ni.vKeys.reserve(vToDistributeKeys.size());// vToDistributeKeys.size():这是需要分配给该节点的关键点的数量

        // .push_back() 是 C++ 标准库中用于容器(如 vector, list, deque 等)的一个成员函数,用于在容器的末尾添加一个元素。
        lNodes.push_back(ni); //将新创建的 ExtractorNode (ni) 添加到 lNodes 列表中,以便存储所有的节点。
        vpIniNodes[i] = &lNodes.back(); //将该节点的指针保存到 vpIniNodes 向量中,以便后续对该节点进行快速访问。
    }

    //Associate points to childs 把关键点与其对应的子节点关联。
    /*
      size_t 是无符号整型,意味着它只能表示非负整数。这使得它适合用于表示内存大小、数组的索引等不能为负的值。
	  size_t 的具体大小取决于编译器和平台。
	  size_t 被广泛用于表示数组的索引、向量的大小和其他容器的元素计数。
	  cv::KeyPoint:表示 OpenCV 中的一个关键点,包含了该点的位置信息(pt)、响应值、大小、角度等信息。
    */
    for(size_t i=0;i<vToDistributeKeys.size();i++)// 遍历输入的关键点集合 vToDistributeKeys。
    {
        const cv::KeyPoint &kp = vToDistributeKeys[i];
        vpIniNodes[kp.pt.x/hX]->vKeys.push_back(kp);// ->vKeys:访问该节点的 vKeys 向量,这个向量用于存储与该节点相关联的关键点。
    }

    list<ExtractorNode>::iterator lit = lNodes.begin();

    while(lit!=lNodes.end())
    {
        if(lit->vKeys.size()==1)//检查当前节点(lit 指向的节点)中 vKeys 向量的大小是否等于 1。也就是说,当前节点只包含一个关键点。
        {
            lit->bNoMore=true;
            lit++;
        }
        else if(lit->vKeys.empty())// 如果当前节点的 vKeys 向量为空,则调用 lNodes.erase(lit) 从链表中删除当前节点。
            lit = lNodes.erase(lit);
        else
            lit++;
    }

    bool bFinish = false;// 定义一个布尔变量 bFinish,用于控制后续的 while 循环。当条件满足时,可以将其设置为 true,以终止循环。

    int iteration = 0;// 定义一个整型变量 iteration,初始化为 0,用于计数,跟踪循环的迭代次数。
	// std::pair 是一个模板类,用于存储一对相关联的数据。它的模板参数分别表示这对数据的类型。
    vector<pair<int,ExtractorNode*> > vSizeAndPointerToNode;
    // 为 vSizeAndPointerToNode 预留空间。reserve 函数不会改变 vector 的大小,但会为它分配足够的内存,以便能够容纳 lNodes.size() * 4 个元素。
    vSizeAndPointerToNode.reserve(lNodes.size()*4);// 初始化节点将分裂成4个。

    while(!bFinish)
    {
        iteration++;//循环迭代次数+1.

        int prevSize = lNodes.size();// 保存当前节点列表 lNodes 的大小,以便后续比较。

        lit = lNodes.begin();// 初始化一个迭代器 lit,指向 lNodes 的开始位置,用于遍历所有节点。

        int nToExpand = 0;// 初始化一个整型变量 nToExpand,用于记录需要扩展的子节点的数量。

        vSizeAndPointerToNode.clear();// 清空之前存储节点信息的 vSizeAndPointerToNode 容器,以便在当前迭代中重新填充。

        while(lit!=lNodes.end())// 遍历 lNodes 中的每一个节点。
        {
            if(lit->bNoMore)// 检查当前节点 lit 是否标记为不再分割。如果是,只需继续处理下一个节点。
            {
                // If node only contains one point do not subdivide and continue
                lit++;//lit++:将迭代器移动到下一个节点。
                continue;
            }
            else// 如果当前节点包含多个特征点,则需要进行分割。
            {
                // If more than one point, subdivide
                ExtractorNode n1,n2,n3,n4;// 创建四个新的 ExtractorNode 实例,用于存储分割后的子节点。
                lit->DivideNode(n1,n2,n3,n4);// 调用 lit 所指节点的 DivideNode 方法,分割当前节点为四个子节点。

                // Add childs if they contain points
                if(n1.vKeys.size()>0)
                {
                    lNodes.push_front(n1);// 检查第一个子节点 n1 是否有特征点。如果有,添加到lNodes列表中。将 n1 添加到 lNodes 的前面。
                    if(n1.vKeys.size()>1)
                    {
                        nToExpand++;// 如果 n1 包含多个特征点,增加扩展计数。
                        vSizeAndPointerToNode.push_back(make_pair(n1.vKeys.size(),&lNodes.front()));
                        lNodes.front().lit = lNodes.begin();
                    }
                }
                if(n2.vKeys.size()>0)
                {
                    lNodes.push_front(n2);
                    if(n2.vKeys.size()>1)
                    {
                        nToExpand++;
                        vSizeAndPointerToNode.push_back(make_pair(n2.vKeys.size(),&lNodes.front()));
                        lNodes.front().lit = lNodes.begin();
                    }
                }
                if(n3.vKeys.size()>0)
                {
                    lNodes.push_front(n3);
                    if(n3.vKeys.size()>1)
                    {
                        nToExpand++;
                        vSizeAndPointerToNode.push_back(make_pair(n3.vKeys.size(),&lNodes.front()));
                        lNodes.front().lit = lNodes.begin();
                    }
                }
                if(n4.vKeys.size()>0)
                {
                    lNodes.push_front(n4);
                    if(n4.vKeys.size()>1)
                    {
                        nToExpand++;
                        vSizeAndPointerToNode.push_back(make_pair(n4.vKeys.size(),&lNodes.front()));
                        lNodes.front().lit = lNodes.begin();
                    }
                }

                lit=lNodes.erase(lit);// 在处理完当前节点后,将其从 lNodes 中移除,并更新 lit 迭代器以指向下一个节点。
                continue;
            }
        }

        // Finish if there are more nodes than required features 如果节点数量超过所需特征数量,
        // or all nodes contain just one point 或者所有节点只包含一个点,则结束。
        if((int)lNodes.size()>=N || (int)lNodes.size()==prevSize)
        {
            bFinish = true;
        }
        else if(((int)lNodes.size()+nToExpand*3)>N)// nToExpand*3一分四后要删除原来节点。
        {

            while(!bFinish)
            {

                prevSize = lNodes.size();

                vector<pair<int,ExtractorNode*> > vPrevSizeAndPointerToNode = vSizeAndPointerToNode;
                vSizeAndPointerToNode.clear();

                sort(vPrevSizeAndPointerToNode.begin(),vPrevSizeAndPointerToNode.end());
                for(int j=vPrevSizeAndPointerToNode.size()-1;j>=0;j--)
                {
                    ExtractorNode n1,n2,n3,n4;
                    vPrevSizeAndPointerToNode[j].second->DivideNode(n1,n2,n3,n4);

                    // Add childs if they contain points
                    if(n1.vKeys.size()>0)
                    {
                        lNodes.push_front(n1);
                        if(n1.vKeys.size()>1)
                        {
                            vSizeAndPointerToNode.push_back(make_pair(n1.vKeys.size(),&lNodes.front()));
                            lNodes.front().lit = lNodes.begin();
                        }
                    }
                    if(n2.vKeys.size()>0)
                    {
                        lNodes.push_front(n2);
                        if(n2.vKeys.size()>1)
                        {
                            vSizeAndPointerToNode.push_back(make_pair(n2.vKeys.size(),&lNodes.front()));
                            lNodes.front().lit = lNodes.begin();
                        }
                    }
                    if(n3.vKeys.size()>0)
                    {
                        lNodes.push_front(n3);
                        if(n3.vKeys.size()>1)
                        {
                            vSizeAndPointerToNode.push_back(make_pair(n3.vKeys.size(),&lNodes.front()));
                            lNodes.front().lit = lNodes.begin();
                        }
                    }
                    if(n4.vKeys.size()>0)
                    {
                        lNodes.push_front(n4);
                        if(n4.vKeys.size()>1)
                        {
                            vSizeAndPointerToNode.push_back(make_pair(n4.vKeys.size(),&lNodes.front()));
                            lNodes.front().lit = lNodes.begin();
                        }
                    }

                    lNodes.erase(vPrevSizeAndPointerToNode[j].second->lit);

                    if((int)lNodes.size()>=N)
                        break;
                }

                if((int)lNodes.size()>=N || (int)lNodes.size()==prevSize)
                    bFinish = true;

            }
        }
    }

    // Retain the best point in each node
    vector<cv::KeyPoint> vResultKeys;
    vResultKeys.reserve(nfeatures);
    for(list<ExtractorNode>::iterator lit=lNodes.begin(); lit!=lNodes.end(); lit++)
    {
        vector<cv::KeyPoint> &vNodeKeys = lit->vKeys;
        cv::KeyPoint* pKP = &vNodeKeys[0];
        float maxResponse = pKP->response;

        for(size_t k=1;k<vNodeKeys.size();k++)
        {
            if(vNodeKeys[k].response>maxResponse)
            {
                pKP = &vNodeKeys[k];
                maxResponse = vNodeKeys[k].response;
            }
        }

        vResultKeys.push_back(*pKP);
    }

    return vResultKeys;
}

//将当前节点分割成四个子节点,并将关键点分配给相应的子节点。(x轴向右y轴向下。)
void ExtractorNode::DivideNode(ExtractorNode &n1, ExtractorNode &n2, ExtractorNode &n3, ExtractorNode &n4)
{
    const int halfX = ceil(static_cast<float>(UR.x-UL.x)/2);//计算当前节点宽度的一半(向上取整)。
    const int halfY = ceil(static_cast<float>(BR.y-UL.y)/2);//计算当前节点高度的一半(向上取整)。

    //Define boundaries of childs
    //定义四个子节点(子区域)的边界。
    n1.UL = UL;//将当前节点的左上角(UL)赋值给子节点 n1 的左上角。
    n1.UR = cv::Point2i(UL.x+halfX,UL.y);//子节点 n1 的右上角(UR)是当前节点左上角的 x 坐标加上 halfX(当前节点宽度的一半),y 坐标保持不变。
    n1.BL = cv::Point2i(UL.x,UL.y+halfY);//子节点 n1 的左下角(BL)是当前节点左上角的 x 坐标保持不变,y 坐标加上 halfY(当前节点高度的一半)。
    n1.BR = cv::Point2i(UL.x+halfX,UL.y+halfY);//子节点 n1 的右下角(BR)是由左上角的 x 和 y 坐标加上 halfX 和 halfY 得到的。
    n1.vKeys.reserve(vKeys.size());// // 为 n1 预留vKeys.size()个特征点的空间

    n2.UL = n1.UR;
    n2.UR = UR;
    n2.BL = n1.BR;
    n2.BR = cv::Point2i(UR.x,UL.y+halfY);
    n2.vKeys.reserve(vKeys.size());

    n3.UL = n1.BL;
    n3.UR = n1.BR;
    n3.BL = BL;
    n3.BR = cv::Point2i(n1.BR.x,BL.y);
    n3.vKeys.reserve(vKeys.size());

    n4.UL = n3.UR;
    n4.UR = n2.BR;
    n4.BL = n3.BR;
    n4.BR = BR;
    n4.vKeys.reserve(vKeys.size());

    //Associate points to childs
    //将当前节点的特征点(vKeys)根据其坐标分配到四个子节点(n1、n2、n3 和 n4)中。
    //push_back 是 C++ STL(标准模板库)中 std::vector 类的一个成员函数,用于在向量的末尾添加一个新元素。
    for(size_t i=0;i<vKeys.size();i++)
    {
        const cv::KeyPoint &kp = vKeys[i];
        if(kp.pt.x<n1.UR.x)
        {
            if(kp.pt.y<n1.BR.y)
                n1.vKeys.push_back(kp);
            else
                n3.vKeys.push_back(kp);
        }
        else if(kp.pt.y<n1.BR.y)
            n2.vKeys.push_back(kp);
        else
            n4.vKeys.push_back(kp);
    }

    /*检查四个子节点(n1, n2, n3, n4)中各自的关键点(vKeys)数量是否为 1。
      如果某个子节点的关键点数量正好为 1,则将该子节点的 bNoMore 成员变量设置为 true。
      这里的逻辑可以帮助决定每个子节点是否可以进一步分割
    */
    if(n1.vKeys.size()==1)
        n1.bNoMore = true;
    if(n2.vKeys.size()==1)
        n2.bNoMore = true;
    if(n3.vKeys.size()==1)
        n3.bNoMore = true;
    if(n4.vKeys.size()==1)
        n4.bNoMore = true;

}

// 遍历所有关键点计算其方向。
static void computeOrientation(const Mat& image, vector<KeyPoint>& keypoints, const vector<int>& umax)
{
    for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),
         keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)
    {
        keypoint->angle = IC_Angle(image, keypoint->pt, umax);// 调用IC_Angle()函数,具体定义在下边。
    }
}

const int PATCH_SIZE = 31;//补丁(patch),灰度质心法计算特征点方向信息时,图像块的大小或者说是直径。
const int HALF_PATCH_SIZE = 15;
const int EDGE_THRESHOLD = 19;//THRESHOLD阈值,边界点。为了进行高斯模糊而预留的区域。
//利用灰度值质心法求关键点的方向。
/*
  1.image 某层金字塔的图像。
  2.pt 特征点坐标
  3.u_max 图像块每一行的边界。
*/
//static 关键字表示该函数的作用域是局限于其定义的源文件内。这意味着 IC_Angle 函数不能在其他源文件中被调用或链接。
static float IC_Angle(const Mat& image, Point2f pt,  const vector<int> & u_max)//模板类point的别名,vector<int> 动态数组模板类。
{
    int m_01 = 0, m_10 = 0;//初始化一阶矩,前为y,后为x。

    //获取中心点灰度值
    const uchar* center = &image.at<uchar> (cvRound(pt.y), cvRound(pt.x));//<uchar>单通道灰度图像,<cv::Vec3b>三通道彩色图。
    //cvRound 是 OpenCV 中的函数,它将浮点数四舍五入为最近的整数。
    //at<uchar>(...) 是 cv::Mat 提供的一个成员函数,用于访问指定位置的像素值。

    // Treat the center line differently, v=0。处理中心行。
    for (int u = -HALF_PATCH_SIZE; u <= HALF_PATCH_SIZE; ++u)
        m_10 += u * center[u];//中心线的一阶矩之和。

    // Go line by line in the circuI853lar patch
    int step = (int)image.step1();//.step1()图像一行包含的字节数
    //v=0为中心线,然后对称的对成对的两行进行遍历,提升了计算速度。
    for (int v = 1; v <= HALF_PATCH_SIZE; ++v)
    {
        // Proceed over the two lines
        int v_sum = 0;
        int d = u_max[v];
        for (int u = -d; u <= d; ++u)
        {
            //val_plus中心线下方,val_minus中心线上方。
            int val_plus = center[u + v*step], val_minus = center[u - v*step];
            v_sum += (val_plus - val_minus);//图像坐标y方向向下,故求和要进行相减。
            m_10 += u * (val_plus + val_minus);
        }
        m_01 += v * v_sum;
    }

    return fastAtan2((float)m_01, (float)m_10);//fastAtan2 是 OpenCV 提供的一个函数,用于计算给定两个坐标(x,

标签:10,13,12,ORBextractor,cc,源码,lNodes,correlation,mean
From: https://blog.csdn.net/2301_76831056/article/details/142765781

相关文章