SIFT
Scale Invariant Feature Transform,尺度不变特征变换。SIFT特征对旋转、尺度缩放、亮度变化等保持不变性,是一种非常稳定的局部特征。
SIFT算法主要有以下几个步骤:
SURF
Speeded Up Robust Features。加速版的SIFT。
SURF的流程和SIFT比较类似,这些改进体现在以下几个方面:
FAST
Features From Accelerated Segment Test,以速度快而著称
检测局部像素灰度变化明显的地方,
FAST算法提取角点的步骤:
不可避免的也有一些缺点
上面的介绍的SIFT和SURF算法都包含有各自的特征点描述子的计算方法,而FAST不包含特征点描述子的计算,仅仅只有特征点的提取方法,这就需要一个特征点描述方法来描述FAST提取到的特征点,以方便特征点的匹配。下面介绍一个专门的特征点描述子的计算算法。
BRIEF描述子
BRIEF是一种二进制的描述子,其描述向量是0和1表示的二进制串。0和1表示特征点邻域内两个像素(p和q)灰度值的大小:如果p比q大则选择1,反正就取0。在特征点的周围选择128对这样的p和q的像素对,就得到了128维由0,1组成的向量。那么p和q的像素对是怎么选择的呢?通常都是按照某种概率来随机的挑选像素对的位置。
BRIEF使用随机选点的比较,速度很快,而且使用二进制串表示最终生成的描述子向量,在存储以及用于匹配的比较时都是非常方便的,其和FAST的搭配起来可以组成非常快速的特征点提取和描述算法。
ORB
Oriented FAST and Rotated BRIE
FAST-12算法:
添加预测试操作,于每个像素,直接检测在邻域圆上的第1,5,9,13个像素的亮度,只有当这四个像素当中有三个同时大于IP+T或者小于IP-T的时候,当前像素才有可能是是角点。
问题1:FAST特征点的数量很多,并且不是确定,而大多数情况下,我们希望能够固定特征点的数量。
解决方法:在ORB当中,我们可以指定要提取的特征点数量。对原始的FAST角点分别计算Harris的响应值,然后选取前N个点具有最大相应值的角点,作为最终角点的集合。
问题2:FAST角点不具有方向信息和尺度问题。
解决方法:尺度不变性构建的图像的金字塔,并且从每一层上面来检测角点。旋转性是由灰度质心法实现。
灰度质心法:质心是指以图像块灰度值作为权重的中心。(目标是为找找到方向)
1、在一个小的图像块B中,定义图像块的矩为:
2、通过矩找到图像块的质心
3、连接图像块的几何中心o与质心C,得到一个oc的向量,把这个向量的方向定义特征点的方向
1、OpenCV中封装了常用的特征点算法(如SIFT,SURF,ORB等),提供了统一的接口,便于调用。
#include
#include
using namespace std;
using namespace cv;
int main()
{
//0读取图像
Mat img1 = imread("1.png");
Mat img2 = imread("2.png");
// 1 初始化特征点和描述子,ORB
std::vector keypoints1, keypoints2;
Mat descriptors1, descriptors2;
Ptr orb = ORB::create();
// 2 提取 Oriented FAST 特征点
orb->detect(img1, keypoints1);
orb->detect(img1, keypoints1);
// 3 根据角点位置计算 BRIEF 描述子
orb->compute(img1, keypoints1, descriptors1);
orb->compute(img2, keypoints2, descriptors2);
// 4 对两幅图像中的BRIEF描述子进行匹配,使用 Hamming 距离
vector matches;
BFMatcher bfmatcher(NORM_HAMMING);
bfmatcher.match(descriptors1, descriptors2, matches);
// 5 绘制匹配结果
Mat img_match;
drawMatches(img1, keypoints1, img2, keypoints2, matches, img_match);
imshow("所有匹配点对", img_match);
waitKey(0);
return 0;
}
得到了描述子后,可调用匹配算法进行特征点的匹配。上面代码中,使用了opencv中封装后的暴力匹配算法BFMatcher
,该算法在向量空间中,将特征点的描述子一一比较,选择距离(上面代码中使用的是Hamming距离)较小的一对作为匹配点。
DMatch类存放匹配结果
struct DMatch
{
int queryIdx; //此匹配对应的查询图像的特征描述子索引
int trainIdx; //此匹配对应的训练(模板)图像的特征描述子索引
int imgIdx; //训练图像的索引(若有多个)
float distance; //两个特征向量之间的欧氏距离,越小表明匹配度越高。
bool operator < (const DMatch &m) const;
};
匹配函数match
match(descriptors1, descriptors2, matches);
matches中保存着匹配关系
画匹配图像drawMatches
drawMatches(img1, keypoints1, img2, keypoints2, matches, img_match);
img_match为目标图像。
2、筛选:汉明距离小于最小距离的两倍
选择已经匹配的点对的汉明距离小于最小距离的两倍作为判断依据,如果小于该值则认为是一个错误的匹配,过滤掉;大于该值则认为是一个正确的匹配。
// 匹配对筛选
double min_dist = 1000, max_dist = 0;
// 找出所有匹配之间的最大值和最小值
for (int i = 0; i < descriptors1.rows; i++)
{
double dist = matches[i].distance;//汉明距离在matches中
if (dist < min_dist) min_dist = dist;
if (dist > max_dist) max_dist = dist;
}
// 当描述子之间的匹配大于2倍的最小距离时,即认为该匹配是一个错误的匹配。
// 但有时描述子之间的最小距离非常小,可以设置一个经验值作为下限
vector good_matches;
for (int i = 0; i < descriptors1.rows; i++)
{
if (matches[i].distance <= max(2 * min_dist, 30.0))
good_matches.push_back(matches[i]);
}