ORB-SLAM2 ---- computeOrbDescriptor函数

1.computeOrbDescriptor函数的用处

        计算computeDescriptors函数传进来的每个特征点对应的描述子。

2.函数解析 

2.1 数学基础

        点v 绕 原点旋转θ 角,得到点v’,假设 v点的坐标是(x, y) ,那么可以推导得到 v’点的坐标(x’, y’)

ORB-SLAM2 ---- computeOrbDescriptor函数_第1张图片

        计算描述子时要考虑旋转不变性:比如同一幅图像在不同事件光照效果不同可能英雄特征点的提取,但是相对亮度一般不变(相对暗的地方永远是相对暗的)。

        在特征点匹配时,主要是根据描述子的汉明距离进行匹配,我们要保证两个特征点是相同角度进行匹配的(转到相同的角度),但如何确定这个角度呢?ORBSLAM2中采用了都让特征点的angle属性指向灰度质心的办法实现,我们通过前面已经通过AC_Angle函数求得了angle的值,这个角度值即PQ与x轴的夹角,我们要旋转这个angle角度使特征点指向灰度质心的向量与x轴重合。

ORB-SLAM2 ---- computeOrbDescriptor函数_第2张图片

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

        我们观察上图推导得到旋转后的坐标关系变换。

2.2 代码 

/**
 * @brief 计算ORB特征点的描述子。注意这个是全局的静态函数,只能是在本文件内被调用
 * @param[in] kpt       特征点对象
 * @param[in] img       提取特征点的图像
 * @param[in] pattern   预定义好的采样模板
 * @param[out] desc     用作输出变量,保存计算好的描述子,维度为32*8 = 256 bit
 */
static void computeOrbDescriptor(const KeyPoint& kpt, const Mat& img, const Point* pattern, uchar* desc)
//得到特征点的角度,用弧度制表示。其中kpt.angle是角度制,范围为[0,360)度
    float angle = (float)kpt.angle*factorPI;
	//计算这个角度的余弦值和正弦值
    float a = (float)cos(angle), b = (float)sin(angle);

	//获得图像中心指针
    const uchar* center = &img.at(cvRound(kpt.pt.y), cvRound(kpt.pt.x));
	//获得图像的每行的字节数
    const int step = (int)img.step;
const float factorPI = (float)(CV_PI/180.f);

        我们先将角度转化为弧度制方便计算,并计算cos angle = a,sin angle = b。

        获取特征点kpt中心指针center,获取每行的字节数step

 #define GET_VALUE(idx) center[cvRound(pattern[idx].x*b + pattern[idx].y*a)*step + cvRound(pattern[idx].x*a - pattern[idx].y*b)] 

       

          观察这个宏定义,cvRound(pattern[idx].x*b + pattern[idx].y*a)*step + cvRound(pattern[idx].x*a - pattern[idx].y*b)其实是在算y'* step + x',即pattern点集下的(x,y)点的坐标。center取其灰度值,这个其实就是算(x,y)点的灰度值。

//brief描述子由32*8位组成
	//其中每一位是来自于两个像素点灰度的直接比较,所以每比较出8bit结果,需要16个随机点,这也就是为什么pattern需要+=16的原因
    for (int i = 0; i < 32; ++i, pattern += 16)
    {
		
        int t0, 	//参与比较的第1个特征点的灰度值
			t1,		//参与比较的第2个特征点的灰度值		
			val;	//描述子这个字节的比较结果,0或1
		
        t0 = GET_VALUE(0); t1 = GET_VALUE(1);
        val = t0 < t1;							//描述子本字节的bit0
        t0 = GET_VALUE(2); t1 = GET_VALUE(3);
        val |= (t0 < t1) << 1;					//描述子本字节的bit1
        t0 = GET_VALUE(4); t1 = GET_VALUE(5);
        val |= (t0 < t1) << 2;					//描述子本字节的bit2
        t0 = GET_VALUE(6); t1 = GET_VALUE(7);
        val |= (t0 < t1) << 3;					//描述子本字节的bit3
        t0 = GET_VALUE(8); t1 = GET_VALUE(9);
        val |= (t0 < t1) << 4;					//描述子本字节的bit4
        t0 = GET_VALUE(10); t1 = GET_VALUE(11);
        val |= (t0 < t1) << 5;					//描述子本字节的bit5
        t0 = GET_VALUE(12); t1 = GET_VALUE(13);
        val |= (t0 < t1) << 6;					//描述子本字节的bit6
        t0 = GET_VALUE(14); t1 = GET_VALUE(15);
        val |= (t0 < t1) << 7;					//描述子本字节的bit7

        //保存当前比较的出来的描述子的这个字节
        desc[i] = (uchar)val;
    }

    //为了避免和程序中的其他部分冲突在,在使用完成之后就取消这个宏定义
    #undef GET_VALUE

        这里bit_pattern_31_[256*4]是256对点,每一对点要比较灰度值,因此一个特征点的描述子为256bit。这个一次循环计算8bit存储在desc[i]中,一共循环256/8=32次即可计算出该特征点的描述子。

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