【翻译:OpenCV-Python教程】SIFT(Scale-Invariant Feature Transform) 介绍

⚠️由于自己的拖延症,3.4.3翻到一半,OpenCV发布了4.0.1了正式版,所以接下来是按照4.0.1翻译的。

⚠️除了版本之外,其他还是照旧,Introduction to SIFT (Scale-Invariant Feature Transform),原文

目标

在本章,

  • 我们将会学习 SIFT 算法的概念
  • 我们将学着找出 SIFT 的关键点以及描述符

理论

在前两章,我们看到了一些角点检测的算法,比如哈里斯等等。这些算法都是旋转-不变的,意思就是说,即使图像有旋转,我们也能找出相同的角点。这是明摆着的事,因为在旋转之后的图像中,角点还是角点,但如果图像放缩呢?一个角点有可能在放缩之后就不再是一个角点了。例如下面这个图,在一张小图片中的角被放大之后,从一个与之前图片同样大小的窗口中看上去就感觉是平的了。所以哈里斯角并不是放缩-不变的。

sift_scale_invariant.jpg

于是,在2004年,英属哥伦比亚大学的D.Lowe,在他的论文 Distinctive Image Features from Scale-Invariant Keypoints (从比例不变的关键点获取的独特的图像特征)中想出了一个新的算法,Scale Invariant Feature Transform (SIFT)(译者注:放缩不变的特征转换,缩写是“筛选”),这个算法提取了一些关键点并且比较了他们的描述符。*(这篇论文非常简单易懂,被认为是学习 SIFT 可用的最佳教材。因此本篇的解释就仅仅是该论文的一个小小摘要)*。

在 SIFT 算法中主要涉及的步骤有4个。我们会一个一个的来看它们。

1. 放缩空间极值检测

从以上的图像,很明显的我们不能使用同样大小的窗口来检测不同放缩比例的关键点。对于一个“小”角点来说是没问题啦,但要检测更“大”的角点,我们需要更大的窗口。对此,使用放缩空间过滤。在其中,不同的σ值的图像对应的高斯-拉普拉斯算子被算出来。LoG(译者附:LoG,Laplacian of Gaussian,就是前文中说的高斯-拉普拉斯算子)作为一个 斑点检测器(译者附:blob-detector 参考opencv同名方法),它检测出由于σ变化引起的不同大小下的斑点。简单的说,σ 就是一个放缩比例的参数。例如,在上图中,有较小 σ 值的高斯内核对一个“小”角点返回一个较大的值,然而较大的 σ 值的高斯内核适合较“大”的角点。所以,我们可以在放缩空间中求出局部最大值,它给我们返回一个(x,y,σ)值的列表,它告诉我们,在(x,y) 点 σ 放缩比的情况下,有一个潜在的关键点。

但这个LoG有一点消耗性能,因此 SIFT 算法使用了高斯差分算子(译者附:高斯差分算子),它是LoG的近似值。高斯差是通过两个不同σ值计算出的同一张图像的高斯模糊图像相减得到的。令他们为 σ 和 kσ,这个过程为不同图像的高斯金字塔的阶进行了处理。

如下图所示:

sift_dog.jpg

一旦算出了这个 DoG (译者附:Difference of Gaussian,高斯差分算子),图像就开始在放缩空间中搜索局部极值。比方说,图像中的某像素点用来和它周围的8个邻居比较。例如,将一幅图像中的一个像素与它的8个相邻像素以及下一个比例中的9个像素和前一个比例中的9个像素进行比较,如果它是局部的极大值,它就是一个潜在的关键点。基本上意思就是说那个关键点,在此比例下是最有代表性的。如下图所示:

sift_local_extrema.jpg

对于不同的参数,论文给出了一些经验数据,可以概括为,阶数=4,放缩等级数=5,初始σ=1.6,k=\sqrt{2}等作为最优值。

2. 关键点定位

一旦潜在关键点的位置被算出来了,为了得到更精确的结果,它们必须被再精选一次。他们使用放缩空间中的泰勒级数展开式,得到了更精确的极值点位置。并且如果该极值处的强度小于阈值(该论文为0.03),则不判定为关键点。

这个阈值在OpenCV里叫做contrastThreshold

DoG 算法对于边缘也有很高的响应,所以我们得把边缘除去。对此,使用了一个与哈里斯角点检测相似的概念。他们使用一个 2x2 的黑塞矩阵(H)(译者附:黑塞矩阵)来计算主曲率。由Harris角检测器可知,对于边,一个特征值远大于另一个特征值。

这里用了一个简单的函数,如果该比例大于在OpenCV里被称为edgeThreshold的阈值,该关键点会被抛弃。在论文中该阈值被设为10。

因此,它消除了任何低对比度的关键点和边缘关键点,剩下的是强烈的兴趣点。

3. 方向指定

现在为每个关键点指定一个方向,以实现图像旋转的不变性。邻域取围绕关键点位置的区域,大小取决于放缩比例。在那个区域计算出梯度大小和梯度方向。一个有着36个抽屉、覆盖360度的方向直方图被创建了出来(它通过梯度幅值和高斯加权圆形窗口来加上了权重,其中σ取关键点的1.5倍放缩比)。取该直方图中最高的峰值,以及任何在最高峰值80%之上的峰值点,都用于计算方向。它创建的关键点具有相同的位置和放缩比,但方向不同。有利于匹配的稳定性。

4. 关键点描述

现在关键点的描述符已经创建好了。取围绕关键点的一个 16x16 邻域,它被分成16个4x4大小的子块。对于每个子块,创建8抽屉的方向直方图,这样总共就有了128个可以用的有值得抽屉。它用一个向量来表示,形成了关键点描述符。除此之外,还采取了一些措施来提升对抗光照变化、旋转等的健壮性。

5. 关键点匹配

两张图像之间的关键点,通过识别它们之间的最近的邻居来匹配。但在某些情况之下,第二相近的匹配度有可能和最相近的匹配度非常接近。这有可能是因为噪音或者其他某些原因引起的。如果是那样的话,取次接近的和最接近的比值。如果比值大于0.8,它们就被丢弃掉。在该论文中使用的这种方案,丢弃掉了大约 90% 的错误匹配,但只丢掉了5%的正确匹配。

这就是SIFT算法的一个简单的摘要。想要理解更多的细节,我们强烈推荐你阅读原论文。要记住一件事,这个算法是申请了专利的,它包含在 the opencv contrib repo 里面。

OpenCV里的SIFT

现在让我们看看OpenCV里 SIFT 可用的机制。让我们从关键点检测并绘制它们开始。首先我们要构造一个SIFT对象。我们可以传递不同的参数给它,这些参数是可选的,在文档中有很好的解释。

import numpy as np
import cv2 as cv
img = cv.imread('home.jpg')
gray= cv.cvtColor(img,cv.COLOR_BGR2GRAY)
sift = cv.xfeatures2d.SIFT_create()
kp = sift.detect(gray,None)
img=cv.drawKeypoints(gray,kp,img)
cv.imwrite('sift_keypoints.jpg',img)

sift.detect() 函数算出图像中的关键点。如果只想要搜索图像的一部分,你可以传入一个遮罩图层。每一个关键点都是一个特殊的结构,它有很多的属性,比如它的(x,y)坐标值,有意义的邻域的大小,指定它方向的角度值,关键点指定强度的响应等。

OpenCV 也提供了 cv.drawKeyPoints() 函数,它画了小圆圈在关键点上。

如果你传入一个参数,cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS 给它,它会画一个关键点大小的圆,它甚至会显示它的方向。参见下面的例子。

img=cv.drawKeypoints(gray,kp,img,flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv.imwrite('sift_keypoints.jpg',img)

看下面的两个结果:

sift_keypoints.jpg

现在,要计算出描述符,OpenCV提供了两种方案。

  1. 既然已经找到了关键点,你可以调用 sift.compute() 它比较了描述符和我们已经找到的关键点。例如:kp,des = sift.compute(gray,kp)
  2. 如果你没有找到关键点,那么可以用函数 sift.detectAndCompute() 只需一个步骤就直接找到关键点和描述符。

我们将会看到第二种方法:

sift = cv.xfeatures2d.SIFT_create()
kp, des = sift.detectAndCompute(gray,None)

这里的 kp 将会是一个关键点的列表,而 des 是一个numpy数组,包含128个关键点的形状。

接下来我们拿到了关键点,描述符等等。现在我们想要看看,如何在不同的图像中匹配关键点。我们将在接下来的章节中学到。

额外资源

练习


上篇:【翻译:OpenCV-Python教程】史-托马斯角点检测&用于追踪的好特征

下篇:【翻译:OpenCV-Python教程】SURF (Speeded-Up Robust Features) 介绍

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值