目录
1 什么是SIFT
SIFT 即尺度不变特征变换,用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值点,并提取出其位置、尺度、旋转不变量,此算法由 David Lowe在1999年所发表,2004年完善总结。 其应用范围包含物体辨识、机器人地图感知与导航、影像缝合、3D模型建立、手势辨识、影像追踪和动作比对。
SIFT的特点:
1)SIFT特征是图像的局部特征,其对旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变化、噪声也保持一定程度的稳定性。
2)独特性好、信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配。
3)多量性,即使少数几个物体也可以产生大量的SIFT特征向量。
4)可扩展性,可以很方便的与其他形式的特征向量进行融合。
SIFT可以解决的问题:
1)目标的旋转、缩放、平移
2)图像的仿射、投影变换
3)光照影响
4)目标遮挡
2 sift特征点检测原理
2.1 原理讲解
1)尺度空间极值检测:
一副图像中的特征点一般都是图像中的极值点,因此特征点的定位首先是从寻找图像极值点开始。
生成如果DoG金字塔,如果该点在其DOG尺度空间中本层和上下两层的26个比较点中是最大或最小值,就认为该点是图像在该尺度下的一个特征点。
根据极值点的检测方式我们可以知道,每组图像的首末两端无法进行极值比较,因此为了满足尺度变化的连续性,需要使高斯金字塔的每组有S+3层图像,DOG金子塔每组有S+2层图像。
2)求取特征点的主方向:
经过上面的步骤已经找到了在不同尺度下都存在的特征点,为了实现图像旋转不变性,需要给特征点的方向进行赋值。利用特征点邻域像素的梯度分布特性来确定其方向参数,再利用图像的梯度直方图求取关键点局部结构的稳定方向。 计算以特征点为中心、采集以3×1.5σ为半径区域图像的幅角和幅值,每个点L(x,y)的梯度的模m(x,y)以及方向θ(x,y)由下面公式求得:
在计算得到梯度向量后,就要使用直方图来统计特征点邻域内像素对应的梯度方向和幅值。梯度直方图范围为0~360°,其中每45度设置一个bin,共8个bin。纵轴是梯度方向对应梯度幅值的叠加,在直方图上的峰值就是特征点的主方向。
3)生成特征描述:
通过以上的步骤已经找到了SIFT特征点位置、尺度和方向信息,下面就需要使用一组向量来描述这些关键点也就是生成特征点描述子,这个描述符不只包含特征点,也含有特征点周围对其有贡献的像素点。描述子应具有较高的独立性,以保证匹配率。 特征描述符生成大致有以下三个步骤:
1、校正旋转主方向,确保旋转不变性。
2、生成描述子,最终形成一个128维的特征向量。
3、归一化处理,将特征向量长度进行归一化,进一步去除光照的影响。
为了保证特征矢量的旋转不变性,要以特征点为中心,在附近邻域内将坐标轴旋转θ(特征点的主方向)角度,即将坐标轴旋转为特征点的主方向。旋转后邻域内像素的新坐标为:
旋转后以主方向为中心取 8×8的窗口,如下图。对中央关键点邻域求取每个像素的梯度幅值与梯度方向,箭头方向代表该像素的梯度方向,长度代表梯度幅值,然后利用高斯窗口对其进行加权运算(特征点越近权值越大)。
最后在每个4×4的小块上绘制8个方向的梯度直方图,计算每个梯度方向的累加值,即可形成一个种子点,如右图所示。每个特征点由4个种子点组成,每个种子点有8个方向的向量信息。这种邻域的方向性信息联合增强了算法的抗噪声能力。
在实际的计算过程中,为了增强匹配的稳健性,对每个关键点使用4x4共16个种子点来描述,这样一个关键点就可以产生128维的SIFT向量。
2.2 SIFT检测流程总结
1)DoG尺度空间极值检测: 构造DoG尺度空间,在DoG尺度空间下检测极值点。
2)确定特征点的主方向: 以特征点的为中心、以3×1.5σ为半径的邻域内计算各个像素点的梯度的幅角和幅值,然后使用直方图对梯度的幅角进行统计。直方图的横轴是梯度的方向,纵轴为梯度方向对应梯度幅值的累加值,直方图中最高峰所对应的方向即为特征点的方向。
3)生成特征点的描述子: 首先将坐标轴旋转为特征点的方向,以特征点为中心的16×16的窗口的像素的梯度幅值和方向,将窗口内的像素分成16块,每块是其像素内8个方向的直方图统计,共可形成128维的特征向量。
3 API函数详解
3.1 SIFT::create()函数
SIFT::create()是OpenCV中创建SIFT特征点检测对象的静态函数。它返回一个指向cv::Ptr<cv::xfeatures2d::SIFT>类型的智能指针,用于创建和管理SIFT特征点检测器。具体来说,这个函数用于创建SIFT特征点检测器的实例。SIFT特征点检测器是通过调用该函数来创建的。
static Ptr<SIFT> cv::SIFT::create(int nfeatures = 0, int nOctaveLayers = 3,
double contrastThreshold = 0.04, double edgeThreshold = 10,
double sigma = 1.6 )
参数含义:
Nfeatures -- 保留的最好特征数量,如果为默认值0,则返回找到的所有特征。
nOctaveLayers -- DoG金字塔每组内的层数。
contrastThreshold -- 用于过滤掉低对比度区域中的弱特征的对比度阈值。 阈值越大,
检测器产生的特征越少。
edgeThreshold -- 用于过滤边缘特征的阈值。 阈值越大,滤除的特征越少(保留的特征越多)。
Sigma -- 高斯模糊参数σ,输入图像第0个组使用。
3.2 detectAndCompute()函数
detectAndCompute是SIFT特征点检测器中的一个函数,用于在给定图像上检测特征点并计算描述子。
void detectAndCompute( InputArray image, InputArray mask,
CV_OUT std::vector<KeyPoint>& keypoints, OutputArray descriptors,
bool useProvidedKeypoints=false );
参数含义:
image:输入图像,可以是灰度图或彩色图。
mask:可选参数,用于指定感兴趣区域的掩码。
keypoints:输出参数,检测到的特征点将存储在这个向量中。
descriptors:输出参数,计算得到的特征描述子将存储在这个数组中。
useProvidedKeypoints:可选参数,默认为false。如果设置为true,
则表示使用提供的特征点进行计算,而不是检测新的特征点。
3.3 drawKeypoints()函数
drawKeypoints是OpenCV中的一个函数,用于在图像上绘制特征点。
void drawKeypoints( InputArray image, const std::vector<KeyPoint>& keypoints,
InputOutputArray outImage, const Scalar& color=Scalar::all(-1),
DrawMatchesFlags flags=DrawMatchesFlags::DEFAULT );
参数含义:
image -- 原始图像,可以是三通道或单通道图像。
keypoints -- 特征点向量,向量内每一个元素是一个KeyPoint对象,包含特征点各种属性信息。
outImage -- 特征点绘制的画布图像,可以是原始图像。
color -- 绘制的特征点的颜色信息,默认是随机彩色。
flag -- 特征点的绘制方式(设置特征点哪些绘制,哪些不绘制),包括:
DrawMatchesFlags::DEFAULT:创建输出图像,只绘制特征点坐标点。
DrawMatchesFlags:: DRAW_OVER_OUTIMG:不创建输出图像。
DrawMatchesFlags:: NOT_DRAW_SINGLE_POINTS:不绘制单点的特征点。
DrawMatchesFlags:: DRAW_RICH_KEYPOINTS:绘制时绘制带有方向的圆。
4 用C++编写代码进行实现
下面是使用OpenCV库进行SIFT特征点检测的示例代码:
#include <opencv2/opencv.hpp>
int main()
{
cv::Mat image = cv::imread("image.jpg");
cv::Mat gray;
cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
std::vector<cv::KeyPoint> keypoints;
cv::Mat descriptors;
// 创建SIFT特征点检测对象
cv::Ptr<cv::SIFT> sift = cv::SIFT::create();
// 检测特征点和计算描述子
sift->detectAndCompute(gray, cv::noArray(), keypoints, descriptors);
// 在图像上绘制特征点
cv::Mat output;
cv::drawKeypoints(image, keypoints, output);
cv::imshow("SIFT Features", output);
cv::waitKey(0);
return 0;
}