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 } }



