opencv(30)---特征检测与匹配(1)---SIFT特征点提取

基本概念

特征点的检测和匹配是计算机视觉中非常重要的技术之一, 在物体识别、视觉跟踪、三维重建等领域都有很广泛的应用。OpenCV提供了如下几种特征检测方法:

  • “FAST”——FastFeatureDetector
  • “STAR”——StarFeatureDetector
  • “SIFT”——SIFT(nonfree module)
  • “SURF”——SURF(nonfree module)
  • “ORB”——ORB
  • “MSER”——MSER
  • “GFTT”——GoodFeaturesToTrackDetector
  • “HARRIS”——(配合Harris detector)
  • “Dense”——DenseFeatureDetector
  • “SimpleBlob”——SimpleBlobDetector

  • 注意

在OpenCV3版本中, 许多著名的特征检测算子(如SIFT、SURF、ORB)稳定版的源代码已从官方发行的OpenCV3中移除, 而转移到一个名为xfeature2d的第三方库中, 所以使用OpenCV3时需要特别注意!

SIFT特征检测的原理

SIFT(Scale Invariant Feature Transform)—尺度不变特征变换,是由David G. Lowe在1999年(《Object Recognition from Local Scale-Invariant Features》)提出的高效区域检测算法,在2004年(《Distinctive Image Features from Scale-Invariant Keypoints》)得以完善。SIFT特征对旋转、尺度缩放、亮度变化等保持不变性, 是非常稳定的局部特征, 现在应用很广泛。

这里写图片描述

前面我们学过一些角点检测方法, 比如Harris角点检测, 特闷具有旋转不变性, 即使图像发生旋转, 我们也能找到同样的角点。但是, 如果对图像进行缩放, 角点就可能不是角点了, 所以D.Lowe提出了尺度不变特征变换检测算法—SIFT。

原理介绍如下

尺度空间极值检测

从上图我们可以很明显的看出来在不同的尺度空间不能使用相同的窗口检测极值点, 对小的角点要用小的窗口, 对大的角点只能使用大的窗口。为了达到这个目的我们要使用尺度空间滤波器(尺度空间滤波器可以使用一些列具有不同方差 σ 的高斯卷积核构成), 使用具有不同方差值 σ 的高斯拉普拉斯算子(LoG)对图像进行卷积, LoG 由于具有不同的方差值 σ 所以可以用来检测不同大小的斑点(当 LoG 的方差 σ 与斑点直径相等时能够使斑点完全平滑)。简单来说方差 σ 就是一个尺度变换因子。例如,上图中使用一个小方差 σ 的高斯卷积核是可以很好的检测出小的角点, 而使用大方差 σ 的高斯卷积核时可以很好的检测出大的角点。所以我们可以在尺度空间和二维平面中检测到局部最大值, 如(x, y, σ), 这表示在 σ 尺度中(x, y)点可能是一个关键点。(高斯方差的大小与窗口的大小存在一个倍数关系: 窗口大小等于 6 倍方差加 1, 所以方差的大小也决定了窗口大小)但是这个 LoG 的计算量非常大,所以 SIFT 算法使用高斯差分算子(DoG)来对LoG做近似。

这里写图片描述
在 DoG 搞定之后, 就可以在不同的尺度空间和 2D 平面中搜索局部最大值了, 对于图像中的一个像素点而言,它需要与自己周围的8邻域, 以及尺度空间中上下两层中的相邻的18(2x9)个点相比, 如果是局部最大值, 它就可能是一个关键点, 基本上来说关键点是图像在相应尺度空间中的最好代表,该算法的作者在文章中给出了 SIFT 参数的经验值: octaves=4(通过降低采样从而减小图像尺寸, 构成尺寸减小的图像金字塔(4层), 尺度空间为5, 也就是每个尺寸使用 5 个不同方差的高斯核进行卷积, 初始方差是 1.6, k等于 这里写图片描述

关键点(极值点)定位

一旦找到关键点, 我们就要对它们进行修正从而得到更准确的结果, 作者使用尺度空间的泰勒级数展开来获得极值的准确位置, 如果极值点的灰度值小于阈值(0.03)就会被忽略掉, 在 OpenCV 中这种阈值被称为contrastThreshold。DoG 算法对边界非常敏感, 所以我们必须要把边界去除。Harris算法除了可以用于角点检测之外还可以用于检测边界, 作者就是使用了同样的思路, 作者使用 2x2 的 Hessian 矩阵计算主曲率。从 Harris 角点检测的算法中, 我们知道当一个特征值远远大于另外一个特征值时检测到的是边界, 所以他们使用了一个简单的函数, 如果比例高于阈值(OpenCV中称为边界阈值), 这个关键点就会被忽略,文章中给出的边界阈值为10。所以低对比度的关键点和边界关键点都会被去除掉, 剩下的就是我们感兴趣的关键点了。

为关键点(极值点)指定方向参数

现在我们要为每一个关键点赋予一个反向参数, 这样它才会具有旋转不变性。获取关键点(所在尺度空间)的邻域, 然后计算这个区域的梯度级和方向, 根据计算得到的结果创建一个含有 36 个 bins(每10度一个 bin)的方向直方图。(使用当前尺度空间 σ 值的 1.5 倍为方差的圆形高斯窗口和梯度级做权重), 直方图中的峰值为主方向参数, 如果其他的任何柱子的高度高于峰值的80% 被认为是辅方向, 这就会在相同的尺度空间相同的位置构建除具有不同方向的关键点, 这对应匹配的稳定性会有帮助。

关键点描述符

新的关键点描述符被创建了, 选取与关键点周围一个16x16 的邻域, 把它分成16个 4x4 的小方块, 为每个小方块创建一个具有8个bin的方向直方图。总共加起来有128个 bin, 由此组成长为 128 的向量就构成了关键点描述符。除此之外还要进行几个测量以达到对光照变化,旋转等的稳定性。

关键点匹配

下一步就可以采用关键点特征向量的欧式距离来作为两幅图像中关键点的相似性判定度量。取第一个图的某个关键点, 通过遍历找到第二幅图像中的距离最近的那个关键点。但有些情况下, 第二个距离最近的关键点与第一个距离最近的关键点靠的太近,这可能是由于噪声等引起的, 此时要计算最近距离与第二近距离的比值, 如果比值大于 0.8, 就忽略掉, 这会去除 90% 的错误匹配, 同时只去除 5%的正确匹配(文献所说)。
请记住这个算法是受专利保护的, 所以这个算法包含在 OpenCV中的收费模块中。

SIFT特征检测代码以及应用

函数原型

这里写图片描述

  • nfeatures: 特征点数目(算法对检测出的特征点排名, 返回最好的nfeatures个特征点)
  • nOctaveLayers: 金字塔中每组的层数
  • contrastThreshold: 过滤掉较差的特征点的对阈值, contrastThreshold越大, 返回的特征点越少
  • edgeThreshold: 过滤掉边缘效应的阈值, edgeThreshold越大, 特征点越多(被多滤掉的越少)
  • sigma: 金字塔第0层图像高斯滤波系数, 也就是σ

重载操作符

这里写图片描述
这里写图片描述

  • img: 8位灰度图像
  • mask: 需要检测图像的掩码(可选)
  • keypoints: 检测到的特征点
  • descriptors: 特征点描述的输出向量(如果不需要输出, 需要传cv::noArray())
  • useProvidedKeypoints: 是否进行特征点检测, ture则检测特征点, false只计算图像特征描述

KeyPoint类—特征点检测相关的数据结构, 用于表示特征点

这里写图片描述

绘制关键点: drawKeypoints()函数

函数原型

这里写图片描述

  • image: 输入图像
  • keypoints: 根据原图像得到的特征点
  • outImage: 输出图像, 其内容取决于第5个参数标识符flag
  • color: 所绘制关键点的颜色
  • flags: 绘制关键点的特征标识符, 默认值为DrawMatchsFlags::DEFAULT, 其他:

    这里写图片描述

应用实例

这里写图片描述

这里写图片描述

代码

Mat srcImg1 = imread("00.jpg");
Mat srcImg2 = imread("01.jpg");
//定义SIFT特征检测类对象
SiftFeatureDetector siftDetector1;
SiftFeatureDetector siftDetector2;
//定义KeyPoint变量
vector<KeyPoint>keyPoints1;
vector<KeyPoint>keyPoints2;
//特征点检测
siftDetector1.detect(srcImg1, keyPoints1);
siftDetector2.detect(srcImg2, keyPoints2);
//绘制特征点(关键点)
Mat feature_pic1, feature_pic2;
//drawKeypoints(srcImg1, keyPoints1, feature_pic1, Scalar(0, 0, 255));
//drawKeypoints(srcImg2, keyPoints2, feature_pic2, Scalar(0, 0, 255));
drawKeypoints(srcImg1, keyPoints1, feature_pic1, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
drawKeypoints(srcImg2, keyPoints2, feature_pic2, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//显示原图
imshow("src1", srcImg1);
imshow("src2", srcImg2);
//显示结果
imshow("feature1", feature_pic1);
imshow("feature2", feature_pic2);
waitKey(0);
  • 1
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值