ORB特征点:
特征点是由关键点和描述子两部分组成,关键点是指特征点在图像中的位置,描述子是用来描述关键点周围的像素信息。ORB关键点是在FAST关键点的基础上进行改进给像素增加了一个主方向,称为Oriented FAST。描述子在BRIEF的基础上加入了上述的方向信息,称为Steered BRIEF。
FAST关键点:
FAST关键点是一种检测角点的方法。一个像素与它周围像素的差值达到一定阈值,并且达到一定的像素数量,这个像素就有可能是角点。
提取思想:
1.设定选取的像素值为Ip。
2.设定阈值为T,一般为Ip的20%
3.以选取的像素为中心,选取半径为3的圆上的16个像素点。
4.若选取的16个像素点中有N个像素灰度值大于/小于Ip+T/Ip-T,则可认为该像素点为角点。
5.遍历图像的每一个像素点,循环执行上述步骤。
图像金字塔:
FAST关键点存在尺度问题,ORB特征点使用构建图像金字塔的方式来规避这个问题。我们在对不同图像特征点进行特征匹配时,可以匹配不同图像的不同金字塔层级来提取到特征点,实现尺度不变性。
灰度质心法:
利用灰度质心法求出关键点的方向(形心指向质心),将所有图像都旋转到这个方向从而实现旋转不变性。
1.函数声明:
/*
1.image 是矩阵的形式代表某层金字塔的图像。
2.pt 特征点坐标
3.u_max 图像块每一行的最大边界。
*/
static float IC_Angle(const Mat& image, Point2f pt, const vector<int> & u_max)
//static 关键字表示该函数的作用域是局限于其定义的源文件内。
//cv::Point2i 是OpenCV库中的一个数据结构,表示二维平面上的一个点,使用的是整数坐标。也可以是 cv::Point2f cv::Point2d
2.函数定义:
我们处理的是一个圆形区域,圆具有对称性,我们利用对称性可以一次性的索引多行像素。从而可以实现加速计算圆的灰度质心。
步骤:
1.初始化两个一阶矩:
int m_01 = 0, m_10 = 0;//初始化一阶矩,前为y,后为x。
2.首先先索引中心线:先求出水平坐标轴上的所有一阶矩。
3.以水平坐标轴为轴线,一次性的索引上下两侧的坐标。
4.代码解析:
static float IC_Angle(const Mat& image, Point2f pt, const vector<int> & u_max//模板类point的别名,vector<int> 动态数组模板类。
{
int m_01 = 0, m_10 = 0;//初始化一阶矩,前为y,后为x。
//获取中心点灰度值
const uchar* center = &image.at<uchar> (cvRound(pt.y), cvRound(pt.x));
//<uchar>单通道灰度图像,<cv::Vec3b>三通道彩色图。
//cvRound 是 OpenCV 中的函数,它将浮点数四舍五入为最近的整数。
//at<uchar>(...) 是 cv::Mat 提供的一个成员函数,用于访问指定位置的像素值。
// Treat the center line differently, v=0。处理中心行。
for (int u = -HALF_PATCH_SIZE; u <= HALF_PATCH_SIZE; ++u)
m_10 += u * center[u];//中心线的一阶矩之和。
// Go line by line in the circuI853lar patch
int step = (int)image.step1();//.step1()图像一行包含的字节数
//v=0为中心线,然后对称的对成对的两行进行遍历,提升了计算速度。
for (int v = 1; v <= HALF_PATCH_SIZE; ++v)
{
// Proceed over the two lines
int v_sum = 0;
int d = u_max[v];
for (int u = -d; u <= d; ++u)
{
//val_plus中心线下方,val_minus中心线上方。
int val_plus = center[u + v*step], val_minus = center[u - v*step];
v_sum += (val_plus - val_minus);//图像坐标y方向向下,故求和要进行相减。
m_10 += u * (val_plus + val_minus);
}
m_01 += v * v_sum;
}
return fastAtan2((float)m_01, (float)m_10);//fastAtan2 是 OpenCV 提供的一个函数,用于计算给定两个坐标(x,𝑦)的反正切值。
}
3.调用IC_Angle函数
computeOrientation函数调用了IC_Angle函数通过遍历图像上的所有特征点计算其方向。
/*
计算特征点的方向
image 特征点所在当前金字塔的图像
keypoints 特征点向量
umax 每个特征点所在图像区块的每行的边界 u_max 组成的vector
*/
static void computeOrientation(const Mat& image, vector<KeyPoint>& keypoints, const vector<int>& umax)
{
// 遍历所有的特征点
for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),
keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)
{
// 调用IC_Angle 函数计算这个特征点的方向
keypoint->angle = IC_Angle(image, //特征点所在的图层的图像
keypoint->pt, //特征点在这张图像中的坐标
umax); //每个特征点所在图像区块的每行的边界 u_max 组成的vector
}
}