• ORB-SLAM2 ---- computeOrbDescriptor函数


    1.computeOrbDescriptor函数的用处

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

    2.函数解析 

    2.1 数学基础

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

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

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

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

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

    2.2 代码 

    1. /**
    2. * @brief 计算ORB特征点的描述子。注意这个是全局的静态函数,只能是在本文件内被调用
    3. * @param[in] kpt 特征点对象
    4. * @param[in] img 提取特征点的图像
    5. * @param[in] pattern 预定义好的采样模板
    6. * @param[out] desc 用作输出变量,保存计算好的描述子,维度为32*8 = 256 bit
    7. */
    8. static void computeOrbDescriptor(const KeyPoint& kpt, const Mat& img, const Point* pattern, uchar* desc)
    1. //得到特征点的角度,用弧度制表示。其中kpt.angle是角度制,范围为[0,360)度
    2. float angle = (float)kpt.angle*factorPI;
    3. //计算这个角度的余弦值和正弦值
    4. float a = (float)cos(angle), b = (float)sin(angle);
    5. //获得图像中心指针
    6. const uchar* center = &img.at(cvRound(kpt.pt.y), cvRound(kpt.pt.x));
    7. //获得图像的每行的字节数
    8. 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)点的灰度值。

    1. //brief描述子由32*8位组成
    2. //其中每一位是来自于两个像素点灰度的直接比较,所以每比较出8bit结果,需要16个随机点,这也就是为什么pattern需要+=16的原因
    3. for (int i = 0; i < 32; ++i, pattern += 16)
    4. {
    5. int t0, //参与比较的第1个特征点的灰度值
    6. t1, //参与比较的第2个特征点的灰度值
    7. val; //描述子这个字节的比较结果,0或1
    8. t0 = GET_VALUE(0); t1 = GET_VALUE(1);
    9. val = t0 < t1; //描述子本字节的bit0
    10. t0 = GET_VALUE(2); t1 = GET_VALUE(3);
    11. val |= (t0 < t1) << 1; //描述子本字节的bit1
    12. t0 = GET_VALUE(4); t1 = GET_VALUE(5);
    13. val |= (t0 < t1) << 2; //描述子本字节的bit2
    14. t0 = GET_VALUE(6); t1 = GET_VALUE(7);
    15. val |= (t0 < t1) << 3; //描述子本字节的bit3
    16. t0 = GET_VALUE(8); t1 = GET_VALUE(9);
    17. val |= (t0 < t1) << 4; //描述子本字节的bit4
    18. t0 = GET_VALUE(10); t1 = GET_VALUE(11);
    19. val |= (t0 < t1) << 5; //描述子本字节的bit5
    20. t0 = GET_VALUE(12); t1 = GET_VALUE(13);
    21. val |= (t0 < t1) << 6; //描述子本字节的bit6
    22. t0 = GET_VALUE(14); t1 = GET_VALUE(15);
    23. val |= (t0 < t1) << 7; //描述子本字节的bit7
    24. //保存当前比较的出来的描述子的这个字节
    25. desc[i] = (uchar)val;
    26. }
    27. //为了避免和程序中的其他部分冲突在,在使用完成之后就取消这个宏定义
    28. #undef GET_VALUE

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

  • 相关阅读:
    Linux ps -ef|grep去除 grep --color=auto信息
    人体改造 VS 数字化身
    ROS-读取/map话题转化为pgm文件(代码版map_server)
    你和我的匆匆四年
    GNS3 vm 添加 H3C VSR1000 镜像、导入初始配置
    子2023
    机器学习笔记之EM算法(二)EM算法公式推导过程
    Thread线程启动的多种方式
    Mock.js的使用api讲解
    (6)Flink CEP SQL模拟账号短时间内异地登录风控预警
  • 原文地址:https://blog.csdn.net/qq_41694024/article/details/126311719