6、ORB描述子steer brief计算方法
computeDescriptors函数分析
经过计算好灰度质心后,并且经过高斯模糊,对每一层的特征点计算描述子
//注意这是一个不属于任何类的全局静态函数,static修饰符限定其只能够被本文件中的函数调用
/**
* @brief 计算某层金字塔图像上特征点的描述子
*
* @param[in] image 某层金字塔图像
* @param[in] keypoints 特征点vector容器
* @param[out] descriptors 描述子
* @param[in] pattern 计算描述子使用的固定随机点集
*/
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)); //提取出来的描述子的保存位置
}
- 根据随机采样点计算steer BRIEF
///乘数因子,一度对应着多少弧度
const float factorPI = (float)(CV_PI/180.f);
/**
* @brief 计算ORB特征点的描述子。注意这个是全局的静态函数,只能是在本文件内被调用
* @param[in] kpt 特征点对象
* @param[in] img 提取出特征点的图像
* @param[in] pattern 预定义好的随机采样点集
* @param[out] desc 用作输出变量,保存计算好的描述子,长度为32*8bit
*/
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<uchar>(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, //参与比较的另一个特征点的灰度值 //TODO 检查一下这里灰度值为int型???
// val; //描述子这个字节的比较结果
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
}