ORB-SLAM2学习笔记8之特征点提取、生成描述子的ORBextractor

news2024/11/17 10:04:22

文章目录

  • 0 引言
  • 1 特征点提取
    • 1.1 提取流程
    • 1.2 ORBextractor.cc
      • 1.2.1 成员函数
      • 1.2.2 成员变量
    • 1.3 构建图像金字塔
      • 1.3.1 为什么要构建图像金字塔
      • 1.3.2 金字塔参数设置
    • 1.4 提取ORB特征点
      • 1.4.1 Fast角点检测
      • 1.4.2 特征点提取流程
      • 1.4.3 八叉树筛选及非极大值抑制
  • 2 描述子生成
    • 2.1 特征点的方向
    • 2.2 高斯模糊
    • 2.3 计算描述子

0 引言

ORB-SLAM2学习笔记7了解了System主类和多线程,如下图,本文主要学习ORB-SLAM2中的图像预处理的ORB部分(Oriented FAST and Rotated BRIEF),也就是视觉特征点提取器ORBextractor,从全称来看,ORB-SLAM2ORB部分主要涉及ORB特征点提取和描述子生成。

  1. 特征点提取
  • ORB特征点提取使用了FASTFeatures from Accelerated Segment Test)算法。FAST算法是一种高效的特征点检测算法,通过比较像素点与其周围邻域像素的亮度差异来确定特征点。
  • ORB中,FAST算法被扩展成了Oriented FAST,它能够在检测到的特征点周围计算出一个主方向。这个主方向在后续的描述子生成中起到了重要作用。
  • 通过对图像的不同尺度(图像金字塔)进行处理,ORB-SLAM2还能够提取多尺度的特征点,以适应不同距离的场景。
  1. 描述子生成
  • ORB中,每个特征点都有一个对应的局部区域。描述子的生成是在这个局部区域内进行的。
  • 首先,计算特征点周围邻域像素的灰度差异。ORB使用了一个256维的灰度差向量,它记录了周围像素与特征点的灰度差异情况。
  • 然后,利用灰度差向量生成一个固定长度的二进制位串,即ORB描述子。生成描述子的方式是将灰度差向量中的每个元素与零进行比较,将比零大的元素置为1,否则置为0
  • 最后,每个特征点都会生成一个固定长度的二进制描述子,用于表示其局部特征信息。

请添加图片描述

1 特征点提取

1.1 提取流程

ORB-SLAM2中的特征点提取前大致流程分以下几个步骤:

  1. main函数中先建立slamTracking线程,以ORB_SLAM2/Examples/Monocular/mono_tum.cc单目为例,main函数中的SLAM.TrackMonocular(im,tframe);建立跟踪线程;
  2. 然后跳转到ORB_SLAM2/src/System.cc进入到TrackMonocular函数中,通过cv::Mat Tcw = mpTracker->GrabImageMonocular(im,timestamp);获取相机位姿;
  3. 再然后跳转到ORB_SLAM2/src/Tracking.cc进入到GrabImageMonocular函数中,输入灰度图,构造mCurrentFrame关键帧;
  4. 再通过Frame跳转到ORB_SLAM2/src/Frame.cc进入到实现帧检测的函数Frame::Frame,其中就调用ExtractORB(0,imGray);来对单目图像进行特征点提取;
  5. 最后跳转到ORB_SLAM2/src/ORBextractor.cc特征点提取的函数实现文件(包含描述子生成)。

1.2 ORBextractor.cc

直接查看ORB_SLAM2/src/ORBextractor.cc文件,或者也可以查看ORB_SLAM2/include/ORBextractor.h头文件也可以清晰的看出特征点提取器的成员函数和成员变量定义。

1.2.1 成员函数

成员函数类型定义
static float IC_Angle(const Mat& image, Point2f pt, const vector<int> & u_max)static计算特征点的方向
static void computeOrbDescriptor(const KeyPoint& kpt, const Mat& img, const Point* pattern, uchar* desc)static计算ORB特征点的描述子
ORBextractor::ORBextractor(...)public特征点提取器的构造函数
static void computeOrientation(const Mat& image, vector<KeyPoint>& keypoints, const vector<int>& umax)static计算特征点的方向
vector<cv::KeyPoint> ORBextractor::DistributeOctTree(...)protected使用四叉树法对一个图像金字塔图层中的特征点进行平均和分发
void ORBextractor::ComputeKeyPointsOctTree(vector<vector<KeyPoint> >& allKeypoints)protected计算四叉树的特征点
void ORBextractor::ComputeKeyPointsOld(std::vector<std::vector<KeyPoint> > &allKeypoints)protected计算特征点的旧方法
static void computeDescriptors(const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors,const vector<Point>& pattern)static计算某层金字塔图像上特征点的描述子
void ORBextractor::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _keypoints,OutputArray _descriptors)public用仿函数(重载括号运算符)方法来计算图像特征点
void ORBextractor::ComputePyramid(cv::Mat image)protected构建图像金字塔

1.2.2 成员变量

成员变量类型定义
std::vector<cv::Mat> mvImagePyramidpublic存储图像金字塔的变量,一个元素存储一层图像
std::vector<cv::Point> patternprotected计算描述子的随机采样点集合
int nfeaturesprotected整个图像金字塔中,要提取的特征点数目
double scaleFactorprotected图像金字塔层与层之间的缩放因子
int nlevelsprotected图像金字塔的层数
int iniThFASTprotected初始的FAST响应值阈值
int minThFASTprotected最小的FAST响应值阈值
std::vector<int> mnFeaturesPerLevelprotected分配到每层图像中,要提取的特征点数目
std::vector<int> umaxprotected计算特征点方向的时候,有个圆形的图像区域,这个vector中存储了每行u轴的边界(四分之一,其他部分通过对称获得)
std::vector<float> mvScaleFactorprotected每层图像的缩放因子
std::vector<float> mvInvScaleFactorprotected以及每层缩放因子的倒数
std::vector<float> mvLevelSigma2protected存储每层的sigma^2,即上面每层图像相对于底层图像缩放倍数的平方
std::vector<float> mvInvLevelSigma2protectedsigma^2的倒数

1.3 构建图像金字塔

1.3.1 为什么要构建图像金字塔

ORB-SLAM2中构造图像金字塔的目的是为了实现多尺度的特征点提取和匹配,以增强系统的鲁棒性和适应性。图像金字塔是通过对原始图像进行不同尺度的降采样得到的一系列图像,每个图像都具有不同的分辨率。

图像金字塔把具有最高级别分辨率的图像放在底部,以金字塔形状排列,往上是一系列像素(尺寸)逐渐降低的图像,一直到金字塔的顶部只包含一个像素点的图像,这就构成了传统意义上的图像金字塔。

提醒:向下与向上采样,是对图像的尺寸而言的(和金字塔的方向相反),向下取样(缩小图像),向上取样(放大图像),向下取样是分辨率越来越小(往金字塔塔尖方向),向上取样是增加像素。

请添加图片描述
构造图像金字塔的好处包括:

  • 尺度不变性:在图像中,物体的尺度可能随着距离的变化而变化。通过构建图像金字塔,可以在不同尺度上检测和描述特征点,使ORB-SLAM2对于不同距离的场景具有尺度不变性。这意味着即使物体在图像中的大小不同,ORB-SLAM2仍能够检测到相应的特征点。

  • 增强鲁棒性:构建图像金字塔可以在不同分辨率上进行特征提取,从而使ORB-SLAM2对于图像中的噪声、遮挡和光照变化等干扰因素具有更强的鲁棒性。在低分辨率图像上提取的特征点通常对这些干扰因素更具有抵抗能力。

  • 速度优化:通过在较高分辨率的图像上进行特征点提取,可以提高ORB-SLAM2的精度。然而,在计算资源有限的情况下,仅在高分辨率图像上进行特征提取可能会导致计算效率低下。通过构建图像金字塔,ORB-SLAM2可以根据需要选择适当的分辨率进行特征点提取,从而在保持一定精度的同时提高计算速度。

综上所述,通过构建图像金字塔,ORB-SLAM2能够实现多尺度的特征点提取和匹配,提高系统的鲁棒性、适应性和计算效率。这对于处理不同尺度和复杂度的场景是非常重要的。

1.3.2 金字塔参数设置

下面成员变量从配置文件TUM1.yaml中读入:

成员变量类型定义配置文件变量名
int nfeaturesprotected所有层级提取到的特征点数之和金字塔层数ORBextractor.nFeatures1000
double scaleFactorprotected图像金字塔相邻层级间的缩放系数ORBextractor.scaleFactor1.2
int nlevelsprotected金字塔层级数ORBextractor.nLevels8
int iniThFASTprotected提取特征点的描述子门槛(响应值高)ORBextractor.iniThFAST20
int minThFASTprotected提取特征点的描述子门槛(响应值低)ORBextractor.minThFAST7

其中参数中的响应值和要计算的描述子需要重点解释下:

特征点响应值和描述子的区别

  1. 响应值:该特征点的区分度大小,响应值越大的点越应该被用作特征点。类似于分数,学生分数越高,越应该被录取;
  2. 描述子:特征点的哈希运算,其大小无意义,仅用来再数据库中快速找回某特征点。类似学生的学号,系统随机运算出的一串数,用于快速找到学生。

根据上述变量的值计算出下述成员变量:

成员变量类型定义
std::vector<int> mnFeaturesPerLevelprotected金字塔每层级中提取的特征点数,正比于图层边长,总和为nfeatures{61, 73, 87, 105, 126, 151, 181, 216}
std::vector<float> mvScaleFactorprotected各层级的缩放系数{1, 1.2, 1.44, 1.728, 2.074, 2.488, 2.986, 3.583}
std::vector<float> mvInvScaleFactorprotected各层级缩放系数的倒数{1, 0.833, 0.694, 0.579, 0.482, 0.402, 0.335, 0.2791}
std::vector<float> mvLevelSigma2protected各层级缩放系数的平方{1, 1.44, 2.074, 2.986, 4.300, 6.190, 8.916, 12.838}
std::vector<float> mvInvLevelSigma2protected各层级缩放系数的平方倒数{1, 0.694, 0.482, 0.335, 0.233, 0.162, 0.112, 0.078}

1.4 提取ORB特征点

1.4.1 Fast角点检测

Fast角点检测以速度著称,主要检测局部像素灰度变化明显的地方。核心思想:如果一个像素与它邻域的像素差值较大(过暗或过亮),那它极有可能是角点。

提取步骤:

  1. 在图像中选取像素 p,假设它的亮度为 Ip
  2. 设置一个阈值 T(比如 Ip20%)。
  3. 以像素 p 为中心, 选取半径为 3 的圆上的 16 个像素点。
  4. 假如选取的圆上,有连续的 N 个点的亮度大于 Ip + T 或小于 Ip −T,那么像素 p 可以被认为是特征点 (N 通常取 12,即为 FAST-12。其它常用的 N 取值为 911, 他们分别被称为 FAST-9FAST-11)。
  5. 循环以上四步,对每一个像素执行相同的操作。

请添加图片描述
👉 Fast角点提取文献参考:Machine Learning for High-Speed Corner Detection

1.4.2 特征点提取流程

OpenCV中的FAST角点检测容易出现扎堆现象,后面会结合非极大值抑制(Non-maximal suppression)来优化这点;而且Fast角点本不具有方向,但是由于特征点匹配需要,ORB-SLAM2Fast角点进行了改进,改进后的 FAST 被称为 Oriented FAST,不仅进一步提高了检测效率,还具有旋转和尺度的描述。

前面已经定义并初始化了图像金字塔,Oriented FAST角点检测前,先对金字塔每一层的图像遍历进行特征点检测,而提取特征点最重要的就是希望特征点均匀地分布在图像的所有部分,所以实现的核心的有两步:

  1. 分cell搜索特征点,若某cell内特征点响应值普遍较小的话就按照低响应值int minThFAST(类似降低分数线)再搜索一遍;
  2. 对得到的所有特征点进行八叉树(其实图像算是四叉树)筛选,每个cell区域取响应值最大的那个特征点。

如下流程图和详细实现代码,ORB-SLAM2中的特征点检测时设置每个cell大小为30*30,先用高响应值int iniThFAST检测,如果检测不到,再用低响应值int minThFAST检测,最后把所有检测到特征点再统一做八叉树筛选,比较密集的部分特征点用非极大值抑制,选出最优的特征点。

请添加图片描述

void ORBextractor::ComputeKeyPointsOctTree(vector<vector<KeyPoint> >& allKeypoints)	{
    for (int level = 0; level < nlevels; ++level)
        // 计算图像边界
        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;
        const float width = (maxBorderX-minBorderX);
        const float height = (maxBorderY-minBorderY);
        const int nCols = width/W;				// 每一列有多少cell
        const int nRows = height/W;				// 每一行有多少cell
        const int wCell = ceil(width/nCols);	// 每个cell的宽度
        const int hCell = ceil(height/nRows);	// 每个cell的高度

        // 存储需要进行平均分配的特征点
        vector<cv::KeyPoint> vToDistributeKeys;
		
    	// step1. 遍历每行和每列,依次分别用高低阈值搜索FAST特征点
        for(int i=0; i<nRows; i++) {
            const float iniY = minBorderY + i * hCell;
            const float maxY = iniY + hCell + 6;
            for(int j=0; j<nCols; j++) {
                const float iniX =minBorderX + j * wCell;
                const float maxX = iniX + wCell + 6;
                vector<cv::KeyPoint> vKeysCell;
				
                // 先用高阈值搜索FAST特征点
                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);
                }
				// 把 vKeysCell 中提取到的特征点全添加到 容器vToDistributeKeys 中
                for(KeyPoint point :vKeysCell) {
                    point.pt.x+=j*wCell;
                    point.pt.y+=i*hCell;
                    vToDistributeKeys.push_back(point);
                }
            }
        }
		
    	// step2. 对提取到的特征点进行八叉树筛选,见 DistributeOctTree() 函数
        keypoints = DistributeOctTree(vToDistributeKeys, minBorderX, maxBorderX, minBorderY, maxBorderY, mnFeaturesPerLevel[level], level);
    }				
}

1.4.3 八叉树筛选及非极大值抑制

主要通过函数DistributeOctTree()进行八叉树筛选(非极大值抑制),如下图,不断将记录特征点的图像区域进行4等分,直到分出了足够多的分区,每个分区内只保留响应值最大的特征点

请添加图片描述

2 描述子生成

2.1 特征点的方向

利用非极大值抑制得到更加均分分布的优质特征点之后,还需要用灰度质心法来求特征点的方向。灰度质心(Gray-level centroid)是一个用于计算图像或图像区域灰度分布中心的指标。详细的数学原理定义如下:

假设选择图像块B,定义图像块B的矩 m p q m_{pq} mpq定义为:
m p q = ∑ x , y ∈ B x p y q I ( x , y ) , p , q = 0 , 1 (1) m_{pq}=\sum_{{x,y}\in B}x^py^qI(x,y),\quad\quad p,q={0,1} \tag{1} mpq=x,yBxpyqI(x,y)p,q=0,1(1)
其中 x , y x,y x,y表示像素坐标, I ( x , y ) I(x,y) I(x,y)表示此像素坐标的灰度值;

图像块B的质心C可以通过公式(1)中的矩来进一步计算:
m 00 = ∑ x , y ∈ B I ( x , y ) , m 10 = ∑ x , y ∈ B x ∗ I ( x , y ) , m 01 = ∑ x , y ∈ B y ∗ I ( x , y ) (2) m_{00}=\sum_{{x,y}\in B}I(x,y), \quad m_{10}=\sum_{{x,y}\in B}x*I(x,y), \quad m_{01}=\sum_{{x,y}\in B}y*I(x,y) \tag{2} m00=x,yBI(x,y),m10=x,yBxI(x,y),m01=x,yByI(x,y)(2)

C = ( c x , c y ) = ( m 10 m 00 , m 01 m 00 ) (3) C=(c_x,c_y)=\left (\frac{m_{10}}{m_{00}}, \frac{m_{01}}{m_{00}} \right ) \tag{3} C=(cx,cy)=(m00m10,m00m01)(3)

而我们要求的特征点方向,即是图像块B的几何中心O和质心C连接在一起的方向向量 O C ⃗ \vec{OC} OC ,所以定义特征点的方向 θ \theta θ为:
θ = a r c t a n 2 ( c y , c x ) = a r c t a n 2 ( m 01 m 10 ) (4) \theta =arctan2(cy,cx)=arctan2(\frac{m_{01}}{m_{10}}) \tag{4} θ=arctan2(cy,cx)=arctan2(m10m01)(4)

特别说明:ORB-SLAM2中的图像块B不是矩形块,是圆形块,因为相对于矩形,圆形才能保证关键点的旋转不变性

详细的实现代码如下,主要是 computeOrientation()IC_Angle两个函数:

// 计算特征点的方向
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)
    {
		// 调用IC_Angle 函数计算这个特征点的方向
        keypoint->angle = IC_Angle(image, 			//特征点所在的图层的图像
								   keypoint->pt, 	//特征点在这张图像中的坐标
								   umax);			//每个特征点所在图像区块的每行的边界 u_max 组成的vector
    }
}

// 这个函数用于计算特征点的方向,这里是返回角度作为方向。
static float IC_Angle(const Mat& image, Point2f pt,  const vector<int> & u_max)
{
	//图像的矩,前者是按照图像块的y坐标加权,后者是按照图像块的x坐标加权
    int m_01 = 0, m_10 = 0;

	//获得这个特征点所在的图像块的中心点坐标灰度值的指针center
    const uchar* center = &image.at<uchar> (cvRound(pt.y), cvRound(pt.x));

    // Treat the center line differently, v=0
	//这条v=0中心线的计算需要特殊对待
    //后面是以中心行为对称轴,成对遍历行数,所以PATCH_SIZE必须是奇数
    for (int u = -HALF_PATCH_SIZE; u <= HALF_PATCH_SIZE; ++u)
		//注意这里的center下标u可以是负的!中心水平线上的像素按x坐标(也就是u坐标)加权
        m_10 += u * center[u];

    // Go line by line in the circular patch  
	//这里的step1表示这个图像一行包含的字节总数。参考[https://blog.csdn.net/qianqing13579/article/details/45318279]
    int step = (int)image.step1();
	//注意这里是以v=0中心线为对称轴,然后对称地每成对的两行之间进行遍历,这样处理加快了计算速度
    for (int v = 1; v <= HALF_PATCH_SIZE; ++v)
    {
        // Proceed over the two lines
		//本来m_01应该是一列一列地计算的,但是由于对称以及坐标x,y正负的原因,可以一次计算两行
        int v_sum = 0;
		// 获取某行像素横坐标的最大范围,注意这里的图像块是圆形的!
        int d = u_max[v];
		//在坐标范围内挨个像素遍历,实际是一次遍历2个
        // 假设每次处理的两个点坐标,中心线下方为(x,y),中心线上方为(x,-y) 
        // 对于某次待处理的两个点:m_10 = Σ x*I(x,y) =  x*I(x,y) + x*I(x,-y) = x*(I(x,y) + I(x,-y))
        // 对于某次待处理的两个点:m_01 = Σ y*I(x,y) =  y*I(x,y) - y*I(x,-y) = y*(I(x,y) - I(x,-y))
        for (int u = -d; u <= d; ++u)
        {
			//得到需要进行加运算和减运算的像素灰度值
			//val_plus:在中心线下方x=u时的的像素灰度值
            //val_minus:在中心线上方x=u时的像素灰度值
            int val_plus = center[u + v*step], val_minus = center[u - v*step];
			//在v(y轴)上,2行所有像素灰度值之差
            v_sum += (val_plus - val_minus);
			//u轴(也就是x轴)方向上用u坐标加权和(u坐标也有正负符号),相当于同时计算两行
            m_10 += u * (val_plus + val_minus);
        }
        //将这一行上的和按照y坐标加权
        m_01 += v * v_sum;
    }

    //为了加快速度还使用了fastAtan2()函数,输出为[0,360)角度,精度为0.3°
    return fastAtan2((float)m_01, (float)m_10);
}

2.2 高斯模糊

为了让尺度体现其连续性,还需对图像金字塔每层图像使用不同参数做高斯模糊,主要抑制图像中的噪声和细节信息,更好的计算描述子。提取特征点时使用的是清晰的原图像,而计算描述子是需要先进行高斯模糊。

高斯模糊的代码如下,主要是GaussianBlur函数来实现:

        // preprocess the resized image 
        //  Step 5 对图像进行高斯模糊
		// 深拷贝当前金字塔所在层级的图像
        Mat workingMat = mvImagePyramid[level].clone();

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

2.3 计算描述子

👉 Brief描述子文献参考:https://www.cs.ubc.ca/~lowe/525/papers/calonder_eccv10.pdf

计算BRIEF描述子的核心步骤是在特征点周围半径为16的圆域内选取256对点对,在圆内随机挑选(挑选策略:均匀分布采样、高斯分布采样、完全随机采样等)点对,点对里第一个点像素值如果大于第二个点的像素值,则描述子对应位的值为1,否则为0,共得到256位的描述子,为保计算的一致性,工程上使用特定设计的点对pattern,在程序里被硬编码为成员变量了。

//下面就是预先定义好的随机点集,256是指可以提取出256bit的描述子信息,每个bit由一对点比较得来;4=2*2,前面的2是需要两个点(一对点)进行比较,后面的2是一个点有两个坐标
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)*/,
    ...

请添加图片描述
但如下图所示,Brief描述子对旋转敏感,可在做特征点匹配中,希望的是特征描述子是与旋转无关的,这就用到了刚刚灰度质心法计算的特征点主方向,计算描述子时,会把特征点周围像素旋转到特征点主方向上来计算,为了编程方便,代码中是对pattern进行旋转。

请添加图片描述
计算描述子的代码如下,主要是通过computeOrientation()函数来实现:

/**
 * @brief 计算某层金字塔图像上特征点的描述子
 * 
 * @param[in] image                 某层金字塔图像
 * @param[in] keypoints             特征点vector容器
 * @param[out] descriptors          描述子
 * @param[in] pattern               计算描述子使用的固定随机点集
 */
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));	//提取出来的描述子的保存位置
}

至此,详细学习了ORB-SLAM2中的ORBextractor特征点提取器的核心原理和实现细节,后续在此基础上继续学习ORB-SLAM2中的特征点匹配、地图点、关键帧及三大线程等。

Reference:

  • https://github.com/raulmur/ORB_SLAM2
  • https://github.com/electech6/ORB_SLAM2_detailed_comments/tree/master
  • Machine Learning for High-Speed Corner Detection
  • https://www.cs.ubc.ca/~lowe/525/papers/calonder_eccv10.pdf
  • https://blog.csdn.net/ncepu_Chen/article/details/116784563



须知少时凌云志,曾许人间第一流。



⭐️👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍🌔

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/890161.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据集】GRNWRZ V2.0(全球河流网络和相应的水资源分区)介绍及下载

数据介绍 论文&#xff1a;J2022-A data set of global river networks and corresponding water resources zones divisions v2 论文下载地址 GRNWRZV1.0由严登华教授&#xff08;中国水利水电科学研究院&#xff09;团队开发&#xff0c;以全球90mDEM数据为基础&#xff0c;结…

SpringBoot-Mybatis 入门(数据库增删改查)

创建SpringBoot-Mybatis项目 创建新项目&#xff0c;注意Type要选择Maven Spring Boot的版本没啥硬性要求&#xff0c;一般开开发环境 依赖必选MySQL Driver、MyBatis Framework&#xff1b; 选Spring Web是为了辅助未来的web开发&#xff1b; Lombok是个人开发习惯。 配置 …

【C++精华铺】8.C++模板初阶

目录 1. 泛型编程 2. 函数模板 2.1 函数模板的概念及格式 2.2 函数模板的原理 2.3 模板的实例化 2.4 模板参数的匹配原则 3. 类模板 3.1 类模板格式 3.2 类模板的实例化 1. 泛型编程 什么是泛型编程&#xff1f;泛型编程是避免使用某种具体类型而去使用某种通用类型来进行…

FT2000+低温情况下RTC守时问题

1、背景介绍 飞腾2000芯片通过I2C连接一块RTC时钟芯片&#xff08;BellingBL5372&#xff09;来实现麒麟信安系统下后的守时功能。目前BIOS支持UEFI功能&#xff0c;BIOS上电后能获取RTC时间&#xff0c;并将时间写入相应的UEFI变量或内存区域&#xff0c;操作系统上电后使用U…

大模型基础02:GPT家族与提示学习

大模型基础&#xff1a;GPT 家族与提示学习 从 GPT-1 到 GPT-3.5 GPT(Generative Pre-trained Transformer)是 Google 于2018年提出的一种基于 Transformer 的预训练语言模型。它标志着自然语言处理领域从 RNN 时代进入 Transformer 时代。GPT 的发展历史和技术特点如下: GP…

产业园区数字孪生3d可视化全景展示方案

随着数字经济的发展&#xff0c;数字技术给企业发展带来了机遇的同时&#xff0c;也为企业管理带来挑战。比如园区运维&#xff0c;不仅体量大&#xff0c;复杂的运维管理系统&#xff0c;落地难度也较高。那么如何通过数字化手段重塑园区运营&#xff0c;打通园区各业务数据孤…

LimeReport设置当前打印时间

拉一个文本控件&#xff0c;然后填入下面信息&#xff1a; $S{ var curDate new Date(); var strYear curDate.getFullYear().toString(); var strMonth (curDate.getMonth() 1).toString(); var strDay curDate.getDate().toString(); var strHour curDate.getHours().…

句子变形金刚:变相的含义

一、说明 变形金刚完全重建了自然语言处理&#xff08;NLP&#xff09;的格局。在变形金刚出现之前&#xff0c;由于循环神经网络&#xff08;RNN&#xff09;&#xff0c;我们的翻译和语言分类很好——它们的语言理解能力有限&#xff0c;导致许多小错误&#xff0c;而且在大块…

mybatis-x插件的使用

mybatis-x能够帮助我们快速通过数据库生成实体类&#xff0c;mapper层&#xff0c;service层等 下面就带大家如何使用 首先我们去idea里面下载mybatis-x&#xff0c;如下图 我们还需要使用idea连接mysql数据库。效果如下图 接下来我们右键选中表&#xff0c;再选择第一项这个mm…

Panorama SCADA软件在无人值守水泵房的应用

应用背景 城市自来水公司、工矿企业的水泵房普遍数量较多&#xff0c;分布也较分散&#xff0c;在管理上通常会存在需要不定时开关泵、水泵故障不能及时发现、操作人员需24小时轮班值守、人员管理成本高等问题。 虹科Panorama是一个用于构建数据采集、SCADA 和历史解决方案的软…

华为云零代码新手教学-体验通过Astro Zero快速搭建微信小程序

您将会学到 您将学会如何基于Astro零代码能力&#xff0c;DIY开发&#xff0c;完成问卷、投票、信息收集、流程处理等工作&#xff0c;还能够在线筛选、分析数据。实现一站式快速开发个性化应用&#xff0c;体验轻松拖拽开发的乐趣。 您需要什么 环境准备 注册华为云账号、实…

【socket编程简述】TCP UDP 通信总结、TCP连接的三次握手、TCP断开的四次挥手

Socket&#xff1a;Socket被称做 套接字&#xff0c;是网络通信中的一种约定。 Socket编程的应用无处不在&#xff0c;我们平时用的QQ、微信、浏览器等程序.都与Socket编程有关。 三次握手 四次断开 面试可…

Android Selector 的使用

什么是 Selector&#xff1f; Selector 和 Shape 相似&#xff0c;是Drawable资源的一种&#xff0c;可以根据不同的状态&#xff0c;设置不同的图片效果&#xff0c;关键节点 < selector > &#xff0c;例如&#xff1a;我们只需要将Button的 background 属性设置为该dr…

k8s v1.27.4二进制部署记录

记录二进制部署过程 #!/bin/bash#升级内核 update_kernel() {rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.orgyum -y install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpmyum --disablerepo"*" --enablerepo"elrepo-kernel&q…

男装已成越南电商红海赛道,品牌如何突围?

据Metric最新数据&#xff0c;在越南电商市场&#xff0c;男装类目竞争相对激烈&#xff0c;在各大电商平台都已出现饱和迹象。然而&#xff0c;在这片竞争激烈的红海中&#xff0c;仍有品牌找准机会成功突围&#xff0c;为其他品牌提供经验借鉴。 越南男装电商竞争激烈&#…

每天一道leetcode:433. 最小基因变化(图论中等广度优先遍历)

今日份题目&#xff1a; 基因序列可以表示为一条由 8 个字符组成的字符串&#xff0c;其中每个字符都是 A、C、G 和 T 之一。 假设我们需要调查从基因序列 start 变为 end 所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。 例如&#xff0c;&quo…

基于MFCC特征提取和GMM训练的语音信号识别matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 MFCC特征提取 4.2 Gaussian Mixture Model&#xff08;GMM&#xff09; 4.3. 实现过程 4.4 应用领域 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3…

Kubernetes网络模型

Kubernetes 用来在集群上运行分布式系统。分布式系统的本质使得网络组件在 Kubernetes 中是至关重要也不可或缺的。理解 Kubernetes 的网络模型可以帮助你更好的在 Kubernetes 上运行、监控、诊断你的应用程序。 网络是一个很宽泛的领域&#xff0c;其中有许多成熟的技术。对于…

iPhone(iPad)安装deb文件

最简单的方法就是把deb相关的文件拖入手机对应的目录&#xff0c;一般是DynamicLibraries文件夹 参考&#xff1a;探讨手机越狱和安装deb文件的几种方式研究 1、在 Mac 上安装 dpkg 命令 打包 deb 教程之在 Mac 上安装 dpkg 命令_xcode打包root权限deb_qq_34810996的博客-CS…

shopee物流怎么发货?Shopee新手发货流程解析!

在Shopee平台上开店是许多卖家的第一次尝试&#xff0c;因此他们可能会遇到shopee物流店铺长时间没有订单的情况&#xff0c;但一旦有订单&#xff0c;他们可能又不知道如何发货。下面我介绍shopee物流发货流程&#xff0c;帮助卖家更好地处理订单和发货。 ​首先&#xff0c;让…