OpenCV入门(二十七)快速学会OpenCV 26 SIFT算法

OpenCV入门(二十七)快速学会OpenCV 26 SIFT算法

作者:Xiou

1.SIFT概述

之前使用的cv2.cornerHarris,能很好地检测角点且有明显的优势,因为角就是角点,即使旋转图像也能检测到这些角点。但是,如果将图像缩放到更小或者更大的尺寸,图像的某些部分可能丢失或者获得高质量的角点。

例如,如图所示是F1意大利大奖赛赛道的一幅图像的角点检测结果。

在这里插入图片描述

如图所示是基于同一幅图像的一个更小版本的角点检测结果。

在这里插入图片描述

会注意到角点是如何变得更紧凑的,可是,尽管我们获得了一些角点,但是也丢失了一些角点!比如,我们来检查一下阿斯卡里(Variante Ascari)减速弯道,这条位于从西北直到东南的赛道尽头的弯道看起来像波形曲线。在大版本的图像中,双弯道的入口和顶端都被检测为角点。在缩小版本的图像中,没有检测到这样的顶端。如果进一步缩小图像,在某种程度上我们还会丢失弯道入口的角点。

这种特征的丢失引发了一个问题:我们需要一种算法,不管图像大小都能工作。于是尺度不变特征变换(Scale-Invariant Feature Transform,SIFT)登场了。虽然这个名字听起来有点神秘,但是既然我们知道了要解决什么问题,它实际上就是有意义的。我们需要一个函数(变换)来检测特征(特征变换),并且不会因图像尺度的不同而输出不同的结果(尺度不变特征变换)。

2.图像尺度空间

在一定的范围内, 无论物体是大还是小, 人眼都可以分辨出来. 而计算机要有相同的能力却很难, 所以要让机器能够对物体在不同尺度下有一个统一的认知, 就需要考虑图像在不同的尺度下都存在的特点。

多分辨率金字塔
使用高斯模糊, 不同的 σ 决定了图像的平滑程度, 越大的 σ 值对应的图像越模糊. 通过使用不同的 σ 我们可以实现多分辨率金字塔。

在这里插入图片描述
高斯模糊:

cv2.GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None)

参数:
src: 需要滤波的图片;
ksize: 卷积核大小;
sigmaX: 高斯核函数在 X 方向的的标准偏差;
sigmaY: 高斯核函数在 Y 方向的的标准偏差;

代码实例:

import cv2
from matplotlib import pyplot as plt

# 读取图片
img = cv2.imread("p1.jpg")

# 画图
f, ax = plt.subplots(2, 3, figsize=(12, 8))
ax[0, 0].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
ax[0, 0].set_title("original")
ax[0, 0].set_xticks([])
ax[0, 0].set_yticks([])
# 高斯模糊
for i in range(1, 6):

    # 高斯模糊
    image_blur = cv2.GaussianBlur(img, (15, 15), i)

    # 计算子图
    ax[int(i/3), i % 3].imshow(cv2.cvtColor(image_blur, cv2.COLOR_BGR2RGB))

    # 标题
    ax[int(i/3), i % 3].set_title("σ" + str(i))
    ax[int(i/3), i % 3].set_xticks([])
    ax[int(i/3), i % 3].set_yticks([])

# 展示图片
plt.show()

输出结果:
在这里插入图片描述

高斯差分金字塔
DoG (Difference of Gaussian) 即高斯差分金字塔, 是在高斯金字塔的基础上构建起来的. 通过对高斯金字塔逐层相减, 得到 t-1 的高斯差分金字塔:

在这里插入图片描述

计算极值点
DoG 空间极值检测: 将每个像素点和同一层周围的 8 个像素点以及上下两层的 18 个像素点, 共 26 个像素点进行比较. 如果一像素点大于或小于邻近的 26 个像素点的时候, 就成为了极值点。

在这里插入图片描述

3.SIFT 算法

SIFT (Scale Invariant Feature Transform), 即尺度不变特征变换匹配算法. SIFT 算法对于旋转和尺度具有不变性。

语法结构:

cv2.SIFT_create()

获取特征点:

sift.detect(img_gray, None)

绘制特征点:

cv2.drawKeypoints(image, keypoints, outImage, color=None, flags=None)

参数:
image: 输入图像;
keypoints: 特征点;
outImage: 输出图像;
将特征点转换为128 维的向量:

sift.compute(img, kp)

代码实例:

在这里插入代码片

输出结果:

在这里插入图片描述

(1545,)
(1545, 128)
[  0.   0.   0.   0.   0.   0.   0.   0.   1.   1.   0.   0.   0.   0.
   0.   0.  27.  20.   0.   0.   0.   0.   0.   0.  53.  25.   0.   0.
   0.   0.   0.   2.   1.   7.   0.   0.   0.   0.   0.   0.  64.  61.
   0.   0.   0.   0.   0.   3. 159. 105.   0.   0.   0.   0.   0.   9.
 159.  46.   0.   0.   0.   0.   0.   4.  14.  34.   5.   0.   0.   0.
   0.   0. 159.  43.   0.   0.   0.   0.   0.  24. 159.  49.   0.   0.
   0.   0.   0.  38. 159.  38.   0.   0.   0.   0.   0.   5.  27.  10.
   1.   0.   0.   0.   0.   5.  78.  80.   1.   0.   0.   0.   0.   1.
 159. 159.   3.   0.   0.   0.   0.   1. 106. 159.   0.   0.   0.   0.
   0.   0.]


常规导入后,加载想要处理的图像。然后,把图像转换成灰度图像。至此,你可能已经发现OpenCV中的很多方法都需要灰度图像作为输入。下一步是创建SIFT检测对象,并计算灰度图像的特征和描述符:

在后台,这些简单的代码行执行了一个复杂的过程:创建一个cv2.SIFT对象,该对象使用DoG检测关键点,再计算每个关键点周围区域的特征向量。正如detectAndCompute方法的名字清楚表明的那样,该方法主要执行两项操作:特征检测和描述符计算。该操作的返回值是一个元组,包含一个关键点列表和另一个关键点的描述符列表。

最后,用cv2.drawKeypoints函数在图像上绘制关键点,然后用常规的cv2.imshow函数对其进行显示。作为其中一个参数,cv2.drawKeypoints函数接受一个指定想要的可视化类型的标志。这里,我们指定cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINT,以便绘制出每个关键点的尺度和方向的可视化效果。

关键点剖析:

每个关键点都是cv2.KeyPoint类的一个实例,具有以下属性:
·pt(点)属性包括图像中关键点的x和y坐标。
·size属性表示特征的直径。
·angle属性表示特征的方向,如前面处理过的图像中的径向线所示。
·response属性表示关键点的强度。由SIFT分类的一些特征比其他特征更强,response可以评估特征强度。·octave属性表示发现该特征的图像金字塔层。

SIFT算法的操作方式类似于人脸检测算法,迭代处理相同的图像,但是每次迭代时都会更改输入。具体来说,图像尺度是在算法每次迭代(octave)时都变化的一个参数。因此,octave属性与检测到关键点的图像尺度有关。·class_id属性可以用来为一个关键点或者一组关键点分配自定义的标识符。

posted @ 2023-04-01 13:44  小幽余生不加糖  阅读(76)  评论(0编辑  收藏  举报  来源