2021SC@SDUSC
- 1.ORBextractor特征提取与描述
- 2.代码分析
在ORBextractor中包括定义的两个类
- ExtractorNode
- ORBextractor
在ExtractorNode中,维护的是四叉树的数据结构,后面在关键点均匀化时,会用到其中的DvideNode函数。
具体可见这三篇博客:
ORBextractor.cc 代码原理分析
认真的虎ORBSLAM2源码解读(四):图解ORB特征提取ORBextractor
高斯图像金字塔
这个函数用于计算特征点的方向,这里是返回角度作为方向。
计算特征点方向是为了使得提取的特征点具有旋转不变性。
方法是灰度质心法:以几何中心和灰度质心的连线作为该特征点方向
static float IC_Angle(const Mat& image, Point2f pt, const vector& u_max) { //图像的矩,前者是按照图像块的y坐标加权,后者是按照图像块的x坐标加权 int m_01 = 0, m_10 = 0; //获得这个特征点所在的图像块的中心点坐标灰度值的指针center const uchar* center = &image.at (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); }
乘数因子,一度对应着多少弧度
const float factorPI = (float)(CV_PI/180.f);
计算ORB特征点的描述子。注意这个是全局的静态函数,只能是在本文件内被调用
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; //原始的BRIEF描述子不具有方向信息,通过加入特征点的方向来计算描述子,称之为Steer BRIEF,具有较好旋转不变特性 //具体地,在计算的时候需要将这里选取的随机点点集的x轴方向旋转到特征点的方向。 //获得随机“相对点集”中某个idx所对应的点的灰度,这里旋转前坐标为(x,y), 旋转后坐标(x',y')推导: // x'= xcos(θ) - ysin(θ), y'= xsin(θ) + ycos(θ) #define GET_VALUE(idx) center[cvRound(pattern[idx].x*b + pattern[idx].y*a)*step + cvRound(pattern[idx].x*a - pattern[idx].y*b)] // y'* step // x' //brief描述子由32*8位组成 //其中每一位是来自于两个像素点灰度的直接比较,所以每比较出8bit结果,需要16个随机点,这也就是为什么pattern需要+=16的原因 for (int i = 0; i < 32; ++i, pattern += 16) { int t0, //参与比较的一个特征点的灰度值 t1, //参与比较的另一个特征点的灰度值 val; //描述子这个字节的比较结果 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; }//通过对随机点像素灰度的比较,得出BRIEF描述子,一共是32*8=256位 //为了避免和程序中的其他部分冲突在,在使用完成之后就取消这个宏定义 #undef GET_VALUE }
下面就是预先定义好的随机点集,256是指可以提取出256bit的描述子信息,每个bit由一对点比较得来;4=2*2,前面的2是需要两个点(一对点)进行比较,后面的2是一个点有两个坐标
static int bit_pattern_31_[256*4] = { 8,-3, 9,5, 4,2, 7,-12, -11,9, -8,2, 7,-12, 12,-13, 2,-13, 2,12, 1,-7, 1,6, -2,-10, -2,-4, -13,-13, -11,-8, -13,-3, -12,-9, 10,4, 11,9, -13,-8, -8,-9, -11,7, -9,12, 7,7, 12,6, -4,-5, -3,0, -13,2, -12,-3, -9,0, -7,5, 12,-6, 12,-1, -3,6, -2,12, -6,-13, -4,-8, 11,-13, 12,-8, 4,7, 5,1, 5,-3, 10,-3, 3,-7, 6,12, -8,-7, -6,-2, -2,11, -1,-10, -13,12, -8,10, -7,3, -5,-3, -4,2, -3,7, -10,-12, -6,11, 5,-12, 6,-7, 5,-6, 7,-1, 1,0, 4,-5, 9,11, 11,-13, 4,7, 4,12, 2,-1, 4,4, -4,-12, -2,7, -8,-5, -7,-10, 4,11, 9,12, 0,-8, 1,-13, -13,-2, -8,2, -3,-2, -2,3, -6,9, -4,-9, 8,12, 10,7, 0,9, 1,3, 7,-5, 11,-10, -13,-6, -11,0, 10,7, 12,1, -6,-3, -6,12, 10,-9, 12,-4, -13,8, -8,-12, -13,0, -8,-4, 3,3, 7,8, 5,7, 10,-7, -1,7, 1,-12, 3,-10, 5,6, 2,-4, 3,-10, -13,0, -13,5, -13,-7, -12,12, -13,3, -11,8, -7,12, -4,7, 6,-10, 12,8, -9,-1, -7,-6, -2,-5, 0,12, -12,5, -7,5, 3,-10, 8,-13, -7,-7, -4,5, -3,-2, -1,-7, 2,9, 5,-11, -11,-13, -5,-13, -1,6, 0,-1, 5,-3, 5,2, -4,-13, -4,12, -9,-6, -9,6, -12,-10, -8,-4, 10,2, 12,-3, 7,12, 12,12, -7,-13, -6,5, -4,9, -3,4, 7,-1, 12,2, -7,6, -5,1, -13,11, -12,5, -3,7, -2,-6, 7,-8, 12,-7, -13,-7, -11,-12, 1,-3, 12,12, 2,-6, 3,0, -4,3, -2,-13, -1,-13, 1,9, 7,1, 8,-6, 1,-1, 3,12, 9,1, 12,6, -1,-9, -1,3, -13,-13, -10,5, 7,7, 10,12, 12,-5, 12,9, 6,3, 7,11, 5,-13, 6,10, 2,-12, 2,3, 3,8, 4,-6, 2,6, 12,-13, 9,-12, 10,3, -8,4, -7,9, -11,12, -4,-6, 1,12, 2,-8, 6,-9, 7,-4, 2,3, 3,-2, 6,3, 11,0, 3,-3, 8,-8, 7,8, 9,3, -11,-5, -6,-4, -10,11, -5,10, -5,-8, -3,12, -10,5, -9,0, 8,-1, 12,-6, 4,-6, 6,-11, -10,12, -8,7, 4,-2, 6,7, -2,0, -2,12, -5,-8, -5,2, 7,-6, 10,12, -9,-13, -8,-8, -5,-13, -5,-2, 8,-8, 9,-13, -9,-11, -9,0, 1,-8, 1,-2, 7,-4, 9,1, -2,1, -1,-4, 11,-6, 12,-11, -12,-9, -6,4, 3,7, 7,12, 5,5, 10,8, 0,-4, 2,8, -9,12, -5,-13, 0,7, 2,12, -1,2, 1,7, 5,11, 7,-9, 3,5, 6,-8, -13,-4, -8,9, -5,9, -3,-3, -4,-7, -3,-12, 6,5, 8,0, -7,6, -6,12, -13,6, -5,-2, 1,-10, 3,10, 4,1, 8,-4, -2,-2, 2,-13, 2,-12, 12,12, -2,-13, 0,-6, 4,1, 9,3, -6,-10, -3,-5, -3,-13, -1,1, 7,5, 12,-11, 4,-2, 5,-7, -13,9, -9,-5, 7,1, 8,6, 7,-8, 7,6, -7,-4, -7,1, -8,11, -7,-8, -13,6, -12,-8, 2,4, 3,9, 10,-5, 12,3, -6,-5, -6,7, 8,-3, 9,-8, 2,-12, 2,8, -11,-2, -10,3, -12,-13, -7,-9, -11,0, -10,-5, 5,-3, 11,8, -2,-13, -1,12, -1,-8, 0,9, -13,-11, -12,-5, -10,-2, -10,11, -3,9, -2,-13, 2,-3, 3,2, -9,-13, -4,0, -4,6, -3,-10, -4,12, -2,-7, -6,-11, -4,9, 6,-3, 6,11, -13,11, -5,5, 11,11, 12,6, 7,-5, 12,-2, -1,12, 0,7, -4,-8, -3,-2, -7,1, -6,7, -13,-12, -8,-13, -7,-2, -6,-8, -8,5, -6,-9, -5,-1, -4,5, -13,7, -8,10, 1,5, 5,-13, 1,0, 10,-13, 9,12, 10,-1, 5,-8, 10,-9, -1,11, 1,-13, -9,-3, -6,2, -1,-10, 1,12, -13,1, -8,-10, 8,-11, 10,-6, 2,-13, 3,-6, 7,-13, 12,-9, -10,-10, -5,-7, -10,-8, -8,-13, 4,-6, 8,5, 3,12, 8,-13, -4,2, -3,-3, 5,-13, 10,-12, 4,-13, 5,-1, -9,9, -4,3, 0,3, 3,-9, -12,1, -6,1, 3,2, 4,-8, -10,-10, -10,9, 8,-13, 12,12, -8,-12, -6,-5, 2,2, 3,7, 10,6, 11,-8, 6,8, 8,-12, -7,10, -6,5, -3,-9, -3,9, -1,-13, -1,5, -3,-7, -3,4, -8,-2, -8,3, 4,2, 12,12, 2,-5, 3,11, 6,-9, 11,-13, 3,-1, 7,12, 11,-1, 12,4, -3,0, -3,6, 4,-11, 4,12, 2,-4, 2,1, -10,-6, -8,1, -13,7, -11,1, -13,12, -11,-13, 6,0, 11,-13, 0,-1, 1,4, -13,3, -9,-2, -9,8, -6,-3, -13,-6, -8,-2, 5,-9, 8,10, 2,7, 3,-9, -1,-6, -1,-1, 9,5, 11,-2, 11,-3, 12,-8, 3,0, 3,5, -1,4, 0,10, 3,-6, 4,5, -13,0, -10,5, 5,8, 12,11, 8,9, 9,-6, 7,-4, 8,-12, -10,4, -10,9, 7,3, 12,4, 9,-7, 10,-2, 7,0, 12,-2, -1,-6, 0,-11 };
特征点提取器的构造函数
ORBextractor::ORBextractor(int _nfeatures, //指定要提取的特征点数目 float _scaleFactor, //指定图像金字塔的缩放系数 int _nlevels, //指定图像金字塔的层数 int _iniThFAST, //指定初始的FAST特征点提取参数,可以提取出最明显的角点 int _minThFAST): //如果因为图像纹理不丰富提取出的特征点不多,为了达到想要的特征点数目, //就使用这个参数提取出不是那么明显的角点 nfeatures(_nfeatures), scaleFactor(_scaleFactor), nlevels(_nlevels), iniThFAST(_iniThFAST), minThFAST(_minThFAST)//设置这些参数 { //存储每层图像缩放系数的vector调整为符合图层数目的大小 mvScaleFactor.resize(nlevels); //存储这个sigma^2,其实就是每层图像相对初始图像缩放因子的平方 mvLevelSigma2.resize(nlevels); //对于初始图像,这两个参数都是1 mvScaleFactor[0]=1.0f; mvLevelSigma2[0]=1.0f; //然后逐层计算图像金字塔中图像相当于初始图像的缩放系数 for(int i=1; i= vmin; --v) { while (umax[v0] == umax[v0 + 1]) ++v0; umax[v] = v0; ++v0; } }
计算特征点的方向
static void computeOrientation(const Mat& image, vector& keypoints, const vector & umax) { // 遍历所有的特征点 for (vector ::iterator keypoint = keypoints.begin(), keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint) { // 调用IC_Angle 函数计算这个特征点的方向 keypoint->angle = IC_Angle(image, //特征点所在的图层的图像 keypoint->pt, //特征点在这张图像中的坐标 umax); //每个特征点所在图像区块的每行的边界 u_max 组成的vector } }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)