zl程序教程

您现在的位置是:首页 >  后端

当前栏目

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

PythonOpencv教程 介绍 Feature transform scale SIFT
2023-09-14 09:01:40 时间

OpenCV Python SIFT (Scale-Invariant Feature Transform)

【目标】

  • SIFT算法
  • SIFT特征点和描述子

【理论】

前面的章节中,我们提到了角点检测,例如Harris角点,他们是旋转不变的,因为,图像无论如何旋转,其角点特性不会发生改变,所以这类特征也称为旋转不变特征。但是如果图像缩放,原本在小图像中一定的窗口下是角点,放大后,却是平坦区域,即不是角点。如下图:

在这里插入图片描述

D.Lowe, Distinctive Image Features from Scale-Invariant Keypoints

步骤

1. 检测尺度空间的极值

从上图可以看出,我们不能用同样的窗口来检测不同尺度的关键点,对于小角点是可以的。,但为了探测更大的拐角,我们需要更大的窗口。为此,使用尺度空间过滤。文中给出了不同 σ σ σ值的高斯拉普拉斯(LoG)算子应用于图像。LoG作为一个blob检测器,它检测由于 σ σ σ变化而产生的各种大小的blob。简而言之, σ σ σ作为一个尺度参数。例如上图中, σ σ σ值低的高斯核对于小角的值较高,而 σ σ σ值高的高斯核对于大拐角的值较好。因此,我们可以在尺度和空间上找到局部极大值,这给了我们一个 ( x , y , σ ) (x,y, σ) (x,y,σ)值的列表,这意味着在 σ σ σ尺度上 ( x , y ) (x,y) (x,y)有一个潜在的关键点。

但是这个LoG还是比较耗时的,所以SIFT算法使用了近似于LoG的高斯差分。高斯差分图像是通过使用不同的 σ \sigma σ对图像进行模糊得到的高斯模糊图像再进行差分获得。分别设它们为 σ σ σ k σ kσ kσ。这一过程是对不同阶数的高斯金字塔图像进行的。如下图所示:

在这里插入图片描述

一旦完成了DoG计算,就会在图像的不同尺度和空间上搜索局部极值,例如,一个像素会与当前尺度的8邻域、上一尺度的9个像素和下一个尺度的9个像素进行比较,如果是局部极值,这就有可能是关键点,如下图所示

在这里插入图片描述

针对不同的参数,本文给出了一些经验数据,可归纳为:高斯金字塔层级数: 4 4 4, 每一层的尺度数 5 5 5, 初始 σ = 1.6 \sigma=1.6 σ=1.6, k = 2 k=\sqrt{2} k=2 等等。

2. 关键点定位

一旦找到了潜在的关键点位置,就必须对其进行细化以获得更准确的结果。他们使用尺度空间的泰勒级数展开来获得更准确的极值位置,如果该极值处的强度小于阈值(根据论文的说法为0.03),则会被拒绝。这个阈值在OpenCV中称为contrastThreshold

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

如果这个比值大于一个阈值 ( OpenCV 中称为 edgeThreshold ),则该关键点将被丢弃。论文中是10

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

3. 定向分配

现在为每个关键点分配方向,以实现图像旋转的不变性。根据尺度在关键点位置周围取邻域,并在该区域内计算梯度大小和方向。创建36个 bins 覆盖 360度 的梯度方向直方图(采用梯度幅值和高斯加权圆窗口加权, σ σ σ 等于关键点尺度的1.5倍)。取直方图中最高的峰值,任何超过其 80% 的峰值也被视为计算方向。它创建的关键点具有相同的位置和尺度,但方向不同。它有助于匹配的稳定性。

4. 关键点描述

现在关键点描述子已经创建。取关键点邻域的 16*16 区域,它被分为164x4 大小的子块。对于每个子块,创建8个bin 方向直方图。所以总共有128个 bin 值可用。将其表示为一个向量,形成关键点描述子。除此之外,还采取了一些措施来实现对光照变化、旋转等的鲁棒性。

5. 关键点匹配

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

这是对SIFT算法的总结。要了解更多细节和理解,强烈建议阅读原文。

【代码】

在这里插入图片描述

在这里插入图片描述

import numpy
import cv2 

# 读图像
img = cv2.imread("assets/home.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 创建 SIFT 检测器
sift = cv2.SIFT_create()

# 检测 SIFT 关键点
kp = sift.detect(img_gray, None)

# 另一种:检测 SIFT 关键点并 计算特征描述
# kp, descriptors = sift.detectAndCompute(img_gray, None)

# 画出所有关键点
# img = cv2.drawKeypoints(img_gray, kp, img)
# 画出尺度方向等丰富的信息
img = cv2.drawKeypoints(img_gray, kp, img, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

cv2.imshow("result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

【接口】

  • SIFT_create
cv2.SIFT_create(	[, nfeatures[, nOctaveLayers[, contrastThreshold[, edgeThreshold[, sigma]]]]]	) ->	retval
cv2.SIFT_create(	nfeatures, nOctaveLayers, contrastThreshold, edgeThreshold, sigma, descriptorType	) ->	retval

创建 SIFT 检测器

  • nfeatures: 最好关键点的数量,SIFT里用局部对比度作为排序的分数。
  • nOctaveLayers: 每一个金字塔层级里 层的数量,论文中为 3; 金字塔层级根据图像分辨率自动推导。
  • contrastThreshold: 对比度阈值,过滤那些对比度比较小的关键点,阈值越大,剩下的关键点越少。
  • edgeThreshold: 用于过滤那些类似边缘的特征。不同于 contrastThreshold,阈值越大,过滤的越少。
  • sigma: 在原始图像中 高斯核的 sigma, 如果图像质量较差,减小该数值。
  • descriptorType: 描述子类型。
  • detect
detect(	image[, mask]	) ->	keypoints
detect(	images[, masks]	) ->	keypoints

在应用的时候,根据 SIFT_Create创建的对象,再调用detect 检测关键点

  • image: 待检测的图像;
  • keypoints: 检测到的关键点,
  • mask: 掩码图像,ROI
  • compute
compute(	image, keypoints[, descriptors]	) ->	keypoints, descriptors
compute(	images, keypoints[, descriptors]	) ->	keypoints, descriptors

在应用的时候,根据 SIFT_Create创建的对象,再调用compute 检测关键点

  • image: 待计算的图像
  • keypoints: 输入的关键点,关键点因为无法计算特征的时候被删除,也可以添加;
  • descriptors: 计算的特征描述
  • detectAndCompute
detectAndCompute(	image, mask[, descriptors[, useProvidedKeypoints]]	) ->	keypoints, descriptors

检测关键点,并计算特征描述,参数描述见上述两个函数。

  • drawKeypoints
cv2.drawKeypoints(	image, keypoints, outImage[, color[, flags]]	) ->	outImage

绘制关键点

  • image: 源图像
  • keypoints: 源图像对应的关键点
  • outImage: 输出的图像,
  • color: 颜色
  • flags: Python API, flags are modified as cv2.DRAW_MATCHES_FLAGS_DEFAULT, cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS, cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG, cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS
  • cv2.KeyPoint 类的属性
  • angle: float
  • class_id: int, 类别
  • octave: int, 金字塔层级
  • pt: Point2f,坐标点
  • response: float 响应值
  • size: float 有效邻域尺寸

【参考】

  1. OpenCV官方文档
  2. DoG和LoG算子
  3. David G. Lowe. Distinctive image features from scale-invariant keypoints. Int. J. Comput. Vision, 60(2):91–110, November 2004.