OpenCV 4.11.0
开源计算机视觉库
|
在本章中,
在前面几章中,我们看到了一些角点检测器,例如Harris等。它们是旋转不变的,这意味着即使图像旋转,我们也能找到相同的角点。这是显而易见的,因为角点在旋转后的图像中仍然是角点。但是尺度呢?如果图像缩放,角点可能就不再是角点了。例如,检查下面的简单图像。小图像中一个小窗口内的角点,在相同窗口放大后就变平了。因此,Harris角点不是尺度不变的。
2004年,不列颠哥伦比亚大学的D.Lowe在他的论文《从尺度不变关键点提取显著图像特征》中提出了一种新的算法——尺度不变特征变换(SIFT),该算法提取关键点并计算其描述符。(这篇论文很容易理解,被认为是关于SIFT最好的资料。本解释只是这篇论文的一个简短总结)。
SIFT算法主要包含四个步骤。我们将逐一了解它们。
从上图可以看出,我们不能使用相同的窗口来检测不同尺度的关键点。对于小角点来说是可以的。但是要检测较大的角点,我们需要更大的窗口。为此,使用尺度空间滤波。其中,使用不同的σ值对图像进行高斯-拉普拉斯变换(LoG)。LoG充当斑点检测器,由于σ的变化,它可以检测不同大小的斑点。简而言之,σ充当比例参数。例如,在上图中,σ较低的高斯核对小角点给出较高的值,而σ较高的核则更适合较大的角点。因此,我们可以找到跨尺度和空间的局部最大值,这将给我们一个(x,y,σ)值的列表,这意味着在σ尺度下(x,y)处存在一个潜在的关键点。
但是这个LoG计算成本略高,因此SIFT算法使用高斯差分(DoG),它是LoG的近似值。高斯差分是使用两个不同的σ(设为σ和kσ)对图像进行高斯模糊后的差值。这个过程是在高斯金字塔的不同八度上进行的。它在下图中表示
一旦找到DoG,就会在尺度和空间上搜索局部极值点。例如,图像中的一个像素与其8个邻居以及下一尺度的9个像素和上一尺度的9个像素进行比较。如果它是一个局部极值,它就是一个潜在的关键点。这基本上意味着关键点在该尺度上表示得最好。如下图所示
关于不同的参数,论文给出了一些经验数据,可以总结为:八度数=4,尺度级别数=5,初始σ=1.6,k=√2等作为最佳值。
一旦找到潜在的关键点位置,就必须对其进行细化以获得更准确的结果。他们使用尺度空间的泰勒级数展开来获得极值点的更精确位置,如果该极值点处的强度小于阈值(论文中为0.03),则将其拒绝。这个阈值在OpenCV中称为contrastThreshold
DoG对边缘的响应较高,因此也需要去除边缘。为此,使用了类似于Harris角点检测器的概念。他们使用2x2 Hessian矩阵(H)来计算主曲率。我们从Harris角点检测器中知道,对于边缘,一个特征值大于另一个特征值。因此,在这里他们使用了一个简单的函数:
如果该比率大于一个阈值(在OpenCV中称为edgeThreshold),则丢弃该关键点。论文中给定为10。
因此,它消除了任何低对比度的关键点和边缘关键点,剩下的就是强兴趣点。
现在为每个关键点分配一个方向以实现对图像旋转的不变性。根据尺度,在关键点位置周围取一个邻域,并计算该区域的梯度幅度和方向。创建一个包含36个bin的360度方向直方图(它由梯度幅度和具有σ等于关键点尺度的1.5倍的高斯加权圆形窗口加权)。直方图中的最高峰值被采用,并且任何高于其80%的峰值也被考虑用于计算方向。它创建具有相同位置和尺度但方向不同的关键点。这有助于匹配的稳定性。
现在创建关键点描述符。取关键点周围16x16的邻域。将其划分为16个4x4大小的子块。对于每个子块,创建一个8 bin方向直方图。因此,共有128个bin值可用。它表示为一个向量来形成关键点描述符。除此之外,还采取了一些措施来提高对光照变化、旋转等的鲁棒性。
通过识别最近邻来匹配两幅图像之间的关键点。但在某些情况下,第二近邻可能非常接近第一个。这可能是由于噪声或其他原因造成的。在这种情况下,取最近距离与第二近距离的比率。如果它大于0.8,则将其拒绝。根据论文,它消除了大约90%的错误匹配,而只丢弃了5%的正确匹配。
这是SIFT算法的总结。为了更详细地了解,强烈建议阅读原始论文。
现在让我们看看OpenCV中可用的SIFT功能。请注意,这些以前仅在opencv contrib库中可用,但专利于2020年过期。因此,它们现在包含在主库中。让我们从关键点检测开始,并绘制它们。首先,我们必须构造一个SIFT对象。我们可以向其中传递不同的可选参数,这些参数在文档中有很好的解释。
sift.detect() 函数查找图像中的关键点。如果只想搜索图像的一部分,可以传入掩码。每个关键点都是一个特殊的结构,具有许多属性,例如其 (x,y) 坐标、有意义的邻域的大小、指定其方向的角度、指定关键点强度的响应等。
OpenCV 还提供cv.drawKeyPoints() 函数,该函数在关键点的位置绘制小圆圈。如果传入标志cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS,它将绘制一个与关键点大小相同的圆圈,甚至会显示其方向。请参见下面的示例。
请参见下面的两个结果
现在,要计算描述符,OpenCV 提供了两种方法。
我们将看到第二种方法
这里 kp 将是关键点列表,des 是形状为\(\text{(关键点数)} \times 128\) 的 numpy 数组。
因此,我们得到了关键点、描述符等。现在我们想看看如何在不同的图像中匹配关键点。这将在接下来的章节中学习。