欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

(四)OpenCV中的特征检测之SIFT(尺度不变换特征)

程序员文章站 2022-07-14 15:33:39
...

注释:本文翻译自OpenCV3.0.0 document->OpenCV-Python Tutorials,包括对原文档种错误代码的纠正

1.目标

  • 学习SIFT算法的概念
  • 学习如何找到SIFT关键点和描述符

2.理论

在最后几章,我们看到了一些角落探测器,如Harris Corner等。它们是旋转不变的,这意味着,即使图像旋转,我们也可以找到相同的角落。这是显而易见的,因为角落在旋转中也是角落。但是缩放呢?如果图像缩放,角落可能不是角落。例如,请看下面的简单图片。放大同一个窗口时,小窗内的小图像的一个角落是平坦的。所以Harris Corner的角落是规模不变的。

(四)OpenCV中的特征检测之SIFT(尺度不变换特征)

因此,2004年,不列颠哥伦比亚大学的D.Lower在他的论文中提出了一种新的算法——尺度不变特征变换(SIFT),该算法从尺度不变关键点获取图像的特征,提取这个关键点并计算其描述符。(本文很容易理解,并被认为是SIFT上最好的资料,所以这个解释只是本文的一个简短摘要)。

SIFT算法主要涉及四个步骤,我们将逐一看到它们:

   2.1 尺度空间极值检测(Scale-space Extrema Detection)

        从上图可以看出,我们不能使用相同的窗口来检测不同比例的关键点。小角落也行,但是要检测更大的角落,我们需要更大的窗口。为此,使用缩放空间滤波。其中高斯拉普拉斯是为具有各种(四)OpenCV中的特征检测之SIFT(尺度不变换特征)值的图像找到的。LoG作为一个斑点检测器,可以检测由于(四)OpenCV中的特征检测之SIFT(尺度不变换特征)变化而产生的各种大小的斑点。简而言之,(四)OpenCV中的特征检测之SIFT(尺度不变换特征)作为一个缩放参数,例如,在上面的图像中,低(四)OpenCV中的特征检测之SIFT(尺度不变换特征)的高斯核为小角赋值较高,而高斯(四)OpenCV中的特征检测之SIFT(尺度不变换特征)的高斯核适合较大的角。因此,我们可以找到跨尺度和空间的局部最大值,它给出了(四)OpenCV中的特征检测之SIFT(尺度不变换特征)值的列表,这意味着(四)OpenCV中的特征检测之SIFT(尺度不变换特征)尺度下(x,y)处存在潜在的关键点。

       但这个LoG花费代价有点大,所以SIFT算法使用高斯差分,这是LoG的近似值。随着这两个不同(四)OpenCV中的特征检测之SIFT(尺度不变换特征)图像的高斯模糊差异,可以得到高斯差分,让它为(四)OpenCV中的特征检测之SIFT(尺度不变换特征)(四)OpenCV中的特征检测之SIFT(尺度不变换特征)。这个过程是在高斯金字塔的不同八度的图像中完成的。它在下面的图像中表示:

(四)OpenCV中的特征检测之SIFT(尺度不变换特征)

一旦找到这个DoG,图像就会在比例和空间上搜索到局部极值。例如,图像中的一个像素与其8个邻居以及下一个尺度的9个像素和先前尺度的9个像素进行比较。如果它是一个局部极值,这是一个潜在的关键点,它基本上意味着关键点最好在这个范围内表现出来,它显示在下面的图像中:

(四)OpenCV中的特征检测之SIFT(尺度不变换特征)

针对不同的参数,本文给出了一些经验数据,可以总结为八度数(number of octaves)=4,比例级数(number of scale levels)=5,初始(四)OpenCV中的特征检测之SIFT(尺度不变换特征)(四)OpenCV中的特征检测之SIFT(尺度不变换特征)等作为最优值。

   2.2 关键化本地点(Keypoint Localization)

         一旦找到潜在的关键点位置,就必须对其进行改进以获得更准确的结果。他们使用泰勒级数展开的尺度空间来获得更精准的极值位置,并且如果这个极值的强度小于阈值(根据纸张为0.03),它将被拒绝。该阈值在OpenCV中被称为contrastThreshold。

         DoG对边缘有更高的响应,所以边缘也需要去除。为此,使用类似Harris角点检测器的概念。他们用2*2的Hessian矩阵(H)来计算失真曲线,我们从Harris角点检测器知道,对于边缘,一个特征值比另一个大。所以这里他们使用了一个简单的函数。

          如果此比率比阈值(在OpenCV中称为edgeThreshold)高,则丢弃该关键点。在纸上给出10,因此它消除了任何低对比度关键点和边缘关键点,剩下的就是强烈的兴趣点。

   2.3 方向分配(Orientation Assignment)

        现在为每个关键点分配一个方向以实现图像的旋转不变性。在关键点位置周围取决于尺度,并在该区域计算梯度幅度和方向。创建一个方向直方图,其中36个方框覆盖360度,(用(四)OpenCV中的特征检测之SIFT(尺度不变换特征)等于关键点尺度的1.5倍的梯度幅度和高斯加权窗口进行加权,得到直方图中的最高峰值,并且在任何高于80%的峰值也被认为是计算方向。)创建具有相同位置和规模但方向不同的关键点,有助于匹配的稳定性。

   2.4 关键点描述符(keypoint Descriptor)

         现在创建关键点描述符。关键点周围有一个16*16的街区(Block)。它分为16个4*4大小的字块。对于每个字块,创建8个方向直方图。所以共有128bin值可用。它被表示为形成关键点描述符的向量。除此之外,它还采取了几项措施来实现对光照变化、旋转等的鲁棒性。

   2.5 关键点匹配(Keypoint Matching)

         两幅图像之间的关键点通过识别它们最近的邻居来匹配。但在某些情况下,第二个最接近的匹配可能非常接近第一个。这可能是由于噪音或其它的原因。在那种情况下,采用最近距离与第二距离的比率,如果它大于0.8,则被拒绝。根据文章,它被排除了大约90%的错误匹配,而丢弃的只有5%的匹配正确。

         所以这是SIFT算法的总结。欲了解更多细节和理解,强烈建议阅读原文。记住一件事,这个算法是获专利的。所以这个算法被包含在OpenCV的非*模块中。

3.OpenCV中的SIFT

现在让我们来看看OpenCV中提供的SIFT函数。让我们从关键点检测并开始绘制它们,首先,我们必须构建一个SIFT对象。我们可以将不同的参数传递给它,这些参数是可选的,并且在文档中有很好的解释。

# -*- coding: utf-8 -*-
'''
SIFT(尺度不变特征变换)介绍
SIFT算法主要涉及四个步骤:
1.尺度空间极值检测(scale-space Extrema Detection)
2.关键点本地化
3.方向分配
4.关键点描述符
5.关键点匹配
下面的demo是OpenCV中的SIFT函数:cv2.SIFT()
'''

import cv2

img = cv2.imread('5.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

'''
在用python使用OpenCV进行SIFT时候,编译出现这样的问题:
AttributeError: module 'cv2' has no attribute 'SIFT'
错误代码如下:
sift = cv2.SIFT()
原因:opencv将SIFT等算法整合到xfeatures2d集合里面了。变更后的写法如下:
sift=cv2.xfeatures2d.SIFT_create()
'''
# sift = cv2.SIFT()
sift = cv2.xfeatures2d.SIFT_create()

# sift.detect()函数是在图像中查找关键点。如果只想收缩图像的一部分,可以传递掩码
# 每个关键点都是一个特殊的结构,具有许多属性。如(x,y)坐标,有意义的领域大小,指定其方向的角度,指定关键点强度的响应等等
kp = sift.detect(gray, None)

# drawKeypoints:第一个参数输入图像,第二个参数关键点,第三个参数输出图像
# cv2.drawKeypoints()用于在关键点位置绘制小圆圈。如果传递一个标志cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS给它,
# 会绘制一个尺寸为关键点的圆,甚至会显示它的方向。
# img = cv2.drawKeypoints(gray, kp, img)
img = cv2.drawKeypoints(gray, kp, img, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imwrite('sift_keypoints.jpg', img)

'''
现在计算描述符,OpenCV提供了两种方法:
1.既然已经找到了关键点,可以使用sift.compute()计算关键点描述符,例如:kp,des=sift.compute(gray,kp)
2.如果没有找到关键点,可使用函数sift.detectAndCompute()在一个步骤中直接找到关键点和描述符:如下:
'''
# 这里找出来的kp将是一个关键点列表,des是形状Number_of_Keypoints*128的一个numpy数组
kp, des = sift.detectAndCompute(gray, None)

cv2.imshow('sift_keypoints', img)
cv2.waitKey(0) & 0xFF
cv2.destroyAllWindows()

OpenCV还提供cv2.drawkeyPoints()函数,用户绘制关键点位置上的小圆圈。如果你传递一个cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS给它,它会绘制一个尺寸为关键点的圆,它甚至会显示它的方向。

(四)OpenCV中的特征检测之SIFT(尺度不变换特征)