算法描述
SIFT特征不只具有尺度不变性,即使改变旋转角度,图像亮度或拍摄视角,仍然能够得到好的检测效果。整个算法分为以下几个部分:
建立尺度空间,寻找极值
构建图像高斯金字塔,求取DOG,在每一级发现最大与最小值
构建的高斯金字塔,每一层根据sigma的值不同,可以分为几个待级,最少有4个。
关键点定位
我们在像素级别获得了极值点的位置,但是更准确的值应该在亚像素位置,如何得到--这个过程称为关键点(准确/精确)定位。
删除弱边缘--通过Hassian矩阵特征值实现,小于阈值自动舍弃。
关键点方向指定
求得每一层对应图像的梯度,根据给定的窗口大小
计算每个高斯权重,sigma=scale*1.5, 0-360之间建立36个直方图Bins
找最高峰对应的Bin,大于max*80%的都保留
这样就实现了旋转不变性,提高了匹配时候的稳定性
大约有15%的关键点会有多个方向
关键点描述子
拟合多项式插值寻找最大Peak
得到描述子 = 4*4*8=128
哪些点是SIFT中要查找的关键点(特征点)?
这些点是一些十分突出的点不会因光照条件的改变而消失,比如角点、边缘点、暗区域的亮点以及亮区域的暗点,既然两幅图像中有相同的景物,那么使用某种方法分别提取各自的稳定点,这些点之间会有相互对应的匹配点。
所谓关键点,就是在不同尺度空间的图像下检测出的具有方向信息的局部极值点。
根据归纳,我们可以看出特征点具有的三个特征:
尺度 方向 大小
SIFT特征提取的缺点
- 实时性不高,因为要不断地要进行下采样和插值等操作;
- 有时特征点较少(比如模糊图像);
- 对边缘光滑的目标无法准确提取特征(比如边缘平滑的图像,检测出的特征点过少,对圆更是无能为力)。
SIFT特征提取可以解决的问题:
目标的自身状态、场景所处的环境和成像器材的成像特性等因素影响图像配准/目标识别跟踪的性能。而SIFT算法在一定程度上可解决:
- 目标的旋转、缩放、平移(RST)
- 图像仿射/投影变换(视点viewpoint)
- 光照影响(illumination)
- 目标遮挡(occlusion)
- 杂物场景(clutter)
API
SIFT对象创建函数API
static Ptr<SIFT> features2d::SIFT::create (
int nfeatures = 0,
int nOctaveLayers = 3,
double contrastThreshold = 0.04,
double edgeThreshold = 10,
double sigma = 1.6
)
nfeatures 特征值数量保留的最佳功能的数量。这些特征按其分数排名(在SIFT算法中作为局部对比度测量)
nOctaveLayers 高斯计算的层数
contrastThreshold 用于过滤掉半均匀(低对比度)区域中的弱特征的对比度阈值。 阈值越大,检测器产生的特征越少。论文给出的值是0.3,opencv默认0.4区别不大
edgeThreshold 用于过滤边缘特征的阈值。请注意,其含义与contrastThreshold不同,即
edgeThreshold越大,滤除的特征越少(保留的特征越多)一般默认10
sigma 高斯的sigma应用于每层#0的输入图像。如果使用图像较弱,则可能需要减少数量。默认1.6
SIFT提取关键点函数API
features2d::SIFT::detect(
InputArray image,
vector<KeyPoint>& keypoints,
InputArray mask=noArray()
);
- image 待检测的图像
- keypoints 检测到的关键点集合
- mask 指定在哪里寻找关键点的掩码(必须是在感兴趣区域中具有非零值的8位整数矩阵)
SIFT绘制关键点函数API
drawKeypoints(
InputArray image,
vector<KeyPoint>& keypoints,
InputOutputArray outImage,
const Scalar& color=Scalar::all(-1),
int flags=DrawMatchesFlags::DEFAULT
);
- image 进行绘制的图像
- keypoints 来自源图像的关键点
- outImage 绘制完成后输出图像
- color 关键点的颜色 默认值随机值
- DrawMatchesFlags 设置绘图功能的标志 默认圆圈
#include<iostream>
#include<opencv2/opencv.hpp>
#include<opencv2/features2d.hpp>
#include<vector>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("tahiti.jpg");
Ptr<Feature2D> sift = SIFT::create();
vector<KeyPoint> keypoints;
Mat descriptors;
sift->detectAndCompute(img, noArray(), keypoints, descriptors);
drawKeypoints(img, keypoints, descriptors, cv::Scalar::all(-1));
imshow("Result", descriptors);
waitKey(0);
return 0;
}
代码示例:
SIFT特征提取的缺点暴露无余,对于过于平滑的边缘无法提取. 些许模糊的图像也会造成很大的影响。(例如下面的草地,远处的山和蓝天)