ORB-SLAM2-Brief描述子方向计算
代码
在计算完特征点的方向后,开始计算描述子
Mat descriptors;//首先定义了描述子
int nkeypoints = 0;//统计金字塔中所有特征点
for (int level = 0; level < nlevels; ++level)//开始遍历每一层图像金字塔,然后将每一层金字塔上特征点进行总和
nkeypoints += (int)allKeypoints[level].size();
if( nkeypoints == 0 )//如果没有特征点那么就删除描述子
_descriptors.release();
else
{
_descriptors.create(nkeypoints, 32, CV_8U);//如果有就创建描述子
descriptors = _descriptors.getMat();
}
_keypoints.clear();//关键点清空,预分配内存
_keypoints.reserve(nkeypoints);
int offset = 0;
for (int level = 0; level < nlevels; ++level)//开始遍历每一层
{
vector<KeyPoint>& keypoints = allKeypoints[level];//将当前层的描述子传入keypoints
int nkeypointsLevel = (int)keypoints.size();//本层特征点数目
if(nkeypointsLevel==0)//没有特征点
continue;
// preprocess the resized image计算描述子进行了高斯模糊,防止噪声的影响
Mat workingMat = mvImagePyramid[level].clone();//存储当前层的图像
GaussianBlur(workingMat, workingMat, Size(7, 7), 2, 2, BORDER_REFLECT_101);//进行高斯模糊
// Compute the descriptors
Mat desc = descriptors.rowRange(offset, offset + nkeypointsLevel);//存储当前层的描述子
computeDescriptors(workingMat, keypoints, desc, pattern);//计算秒狮子
offset += nkeypointsLevel;//更新偏移量
// Scale keypoint coordinates//将特征点统一到第0层
if (level != 0)
{
float scale = mvScaleFactor[level]; //getScale(level, firstLevel, scaleFactor);
for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),
keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)
keypoint->pt *= scale;
}
// And add the keypoints to the output
_keypoints.insert(_keypoints.end(), keypoints.begin(), keypoints.end());
}
}
进入computeDescriptors函数,计算描述子
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)//提取出的描述子的保存位置
);
}
进入computeOrbDescriptor函数
const float factorPI = (float)(CV_PI/180.f);//转弧度。
static void computeOrbDescriptor(const KeyPoint& kpt,
const Mat& img, const Point* pattern,
uchar* desc)
{
float angle = (float)kpt.angle*factorPI;//得到特征点的角度,并且将其转换为弧度
float a = (float)cos(angle), b = (float)sin(angle);//计算余弦值与正弦值,这部分原理看图一
const uchar* center = &img.at<uchar>(cvRound(kpt.pt.y), cvRound(kpt.pt.x));//获取中心指针
const int step = (int)img.step;//获取每行字节数
#define GET_VALUE(idx) \ //将随机点点集的x轴方向,旋转到特征点方向
center[cvRound(pattern[idx].x*b + pattern[idx].y*a)*step + \ //y'*step
cvRound(pattern[idx].x*a - pattern[idx].y*b)] //x'
for (int i = 0; i < 32; ++i, pattern += 16)
{
int t0, t1, val;
t0 = GET_VALUE(0); t1 = GET_VALUE(1);
val = t0 < t1;
t0 = GET_VALUE(2); t1 = GET_VALUE(3);
val |= (t0 < t1) << 1;
t0 = GET_VALUE(4); t1 = GET_VALUE(5);
val |= (t0 < t1) << 2;
t0 = GET_VALUE(6); t1 = GET_VALUE(7);
val |= (t0 < t1) << 3;
t0 = GET_VALUE(8); t1 = GET_VALUE(9);
val |= (t0 < t1) << 4;
t0 = GET_VALUE(10); t1 = GET_VALUE(11);
val |= (t0 < t1) << 5;
t0 = GET_VALUE(12); t1 = GET_VALUE(13);
val |= (t0 < t1) << 6;
t0 = GET_VALUE(14); t1 = GET_VALUE(15);
val |= (t0 < t1) << 7;
desc[i] = (uchar)val;
}
#undef GET_VALUE
}
图 1