Opencv之SIFT特征

SIFT介绍

SIFT(Scale Invariant Feature Transform)特征是非常稳定的图像特征,在图像搜索、特征匹配、图像分类检测等方面应用十分广泛,但是它的缺点也是非常明显,就是计算量比较大,很难实时,所以对一些实时要求比较高的常见SIFT算法还是无法适用。如今SIFT算法在深度学习特征提取与分类检测网络大行其道的背景下,已经越来越有鸡肋的感觉,但是它本身的算法知识还是很值得我们学习,对我们也有很多有益的启示,本质上SIFT算法是很多常见算法的组合与巧妙衔接,这个思路对我们自己处理问题可以带来很多有益的帮助。特别是SIFT特征涉及到尺度空间不变性与旋转不变性特征,是我们传统图像特征工程的两大利器,可以扩展与应用到很多图像特征提取的算法当中,比如SURF、HOG、HAAR、LBP等。夸张一点的说SIFT算法涵盖了图像特征提取必备的精髓思想,从特征点的检测到描述子生成,完成了对图像的准确描述,早期的ImageNet比赛中,很多图像分类算法都是以SIFT与HOG特征为基础,所有SIFT算法还是值得认真详细解读一番的。SIFT特征提取归纳起来SIFT特征提取主要有如下几步:

  • 获取尺度空间
  • 构建多分辨率金字塔
  • 构建高斯差分金字塔(DOG)
  • DoG空间极值检测
  • 关键点精准定位
  • 得到特征点的主方向
  • 生成特征描述(描述子)

SIFT算法流程图

在这里插入图片描述

获取尺度空间

在一定的范围内,无论物体是大还是小,人眼都可以分辨出来,然而计算机要有相同的能力却很难,所以要让机器能够对物体在不同尺度下有一个统一的认知,就需要考虑图像在不同的尺度下都存在的特点。
尺度空间的获取通常使用高斯模糊来实现。
在这里插入图片描述不同 σ \sigma 的高斯函数决定了对图像的平滑程度,越大的σ值对应的图像越模糊。通过调节 σ \sigma 值,可以得到如下六张图片。
在这里插入图片描述

构建多分辨率金字塔

与构建图像金字塔的唯一不同是,多分辨率金字塔的每一层都有不止一张图片。
即:
在这里插入图片描述

构建高斯差分金字塔(DOG)

假设多分辨率金字塔的每层都有5张经过高斯滤波的不同分辨率的图片,将每一层的图片逐个相减,得到高斯差分金字塔,如下图所示。
在这里插入图片描述在这里插入图片描述

DoG空间极值检测

为了寻找尺度空间的极值点,每个像素点要和其图像域(同一尺度空间)和尺度域(相邻的尺度空间)的所有相邻点进行比较,当其大于(或者小于)所有相邻点时,该点就是极值点。如下图所示,中间的检测点要和其所在图像的3×3邻域8个像素点,以及其相邻的上下两层的3×3领域18个像素点,共26个像素点进行比较。
在这里插入图片描述
经过比较之后,得到所有候选关键点。

关键点精准定位

1、去除对比度低的点

图像得到像素坐标是离散的,尺度空间也是离散的间隔,每个极值点P(x, y, z)三维空间中不一定是真正的连续空间的极值点,以二维的曲线为例,得到蓝色是离散空间极值点,实际极值点是红色点,如下图所示:在这里插入图片描述所以,这些候选关键点是DOG空间的局部极值点,而且这些极值点均为离散的点,精确定位极值点的一种方法是,对尺度空间DoG函数进行曲线拟合,计算其极值点,从而实现关键点的精确定位。
在这里插入图片描述
使 x 0 = y 0 = σ 0 = 0 x_0=y_0=\sigma_0=0 ,将DoG函数利用以上的泰勒级数展开,并令向量 X = [ x y σ ] \bf X=\begin{bmatrix} x \\ y \\ \sigma \end{bmatrix} ,矩阵 D = D ( [ 0 0 0 ] ) D=D(\begin{bmatrix} 0 \\ 0 \\ 0 \end{bmatrix}) 可以得到:
D ( X ) = D + D ( X ) T X X + 1 2 X T 2 D ( X ) T X 2 X D_{(\bf X)}=D+\frac{\partial {D_{(\bf X)}^{T}}}{\partial \bf X}{\bf{X}}+\frac{1}{2} {\bf{X}}^{T}\frac{{\partial}^2 D_{(\bf X)}^{T}}{\partial {\bf X}^2}\bf X
对上式的 X X 求导并令其为0,得到极值点:
X ^ = 2 D ( X ) 1 X 2 D ( X ) X {\hat {\bf {X}}}=-\frac{{\partial}^2 D_{(\bf {X})}^{-1}}{\partial {\bf X}^2}\frac{\partial {D_{(\bf X)}}}{\partial \bf X}
将极值点代入,得到极值为:
D ( X ^ ) = D + 1 2 D ( X ) T X X ^ D(\hat{\bf {X}})=D+\frac{1}{2}\frac{\partial {D_{(\bf X)}^{T}}}{\partial \bf X}\hat{\bf {X}}
一般来说,若 D ( X ^ ) 0.03 |D(\hat{\bf {X}})| \geq 0.03 (即认为对比度较高),该候选特征点就保留下来,否则丢弃。

2、去除不稳定的边缘响应点

一个定义不好的高斯差分算子的极值在横跨边缘的地方有较大的主曲率,而在垂直边缘的方向有较小的主曲率。DOG算子会产生较强的边缘响应,需要剔除不稳定的边缘响应点。获取特征点处的Hessian矩阵,主曲率通过一个2x2 的Hessian矩阵H求出(D的主曲率和H的特征值成正比):
在这里插入图片描述
假设H的特征值为α和β(α、β代表x和y方向的梯度)且α>β。令α=rβ则有:
在这里插入图片描述
其中Tr(H)求取H的对角元素和;Det(H)为求H的行列式值。
在这里插入图片描述
则公式(r+1)^2/r的值在两个特征值相等时最小,随着的增大而增大。值越大,说明两个特征值的比值越大,即在某一个方向的梯度值越大,而在另一个方向的梯度值越小,而边缘恰恰就是这种情况。所以为了剔除边缘响应点,需要让该比值小于一定的阈值,因此,为了检测主曲率是否在某阈值r下,只需检测:
在这里插入图片描述
一般来说,r取10。

得到特征点的主方向

计算以特征点为中心、以3×1.5σ为半径的区域图像的幅角和幅值。
在这里插入图片描述在这里插入图片描述
每个特征点可以得到三个信息(x,y,σ,θ),即位置、尺度和方向。具有多个方向的关键点可以被复制成多份,然后将方向值分别赋给复制后的特征点,一个特征点就产生了多个坐标、尺度相等,但是方向不同的特征点。

生成特征描述(描述子)

计算得到梯度方向后,就要使用直方图统计特征点邻域内像素对应的梯度方向和幅值。梯度方向的直方图的横轴是梯度方向的角度(梯度方向的范围是0到360度,直方图每36度一个柱共10个柱,或者每45度一个柱共8个柱),纵轴是梯度方向对应梯度幅值的累加,在直方图的峰值就是特征点的主方向。在Lowe的论文还提到了使用高斯函数对直方图进行平滑以增强特征点近的邻域点对关键点方向的作用,并减少突变的影响。为了得到更精确的方向,通常还可以对离散的梯度直方图进行插值拟合。具体而言,关键点的方向可以由和主峰值最近的三个柱值通过抛物线插值得到。在梯度直方图中,当存在一个相当于主峰值80%能量的柱值时,则可以将这个方向认为是该特征点辅助方向。所以,一个特征点可能检测到多个方向(也可以理解为,一个特征点可能产生多个坐标、尺度相同,但是方向不同的特征点)。Lowe在论文中指出15%的关键点具有多方向,而且这些点对匹配的稳定性很关键。
左图中的梯度幅值不是直接累加到直方图中去的,要乘以一个高斯权重,即:
m ( x , y ) = m ( x , y ) G ( x , y , 1.5 σ ) m_{(x, y)}=m_{(x, y)}*G_{(x, y, 1.5\sigma)}
其中, x , y x, y 表示局部坐标。
在这里插入图片描述方向直方图的峰值则代表了该特征点处邻域梯度的方向,以直方图中最大值作为该关键点的主方向。为了增强匹配的鲁棒性,只保留峰值大于主方向峰值80%的方向作为该关键点的辅方向。因此,对于同一梯度值的多个峰值的关键点位置,在相同位置和尺度将会有多个关键点被创建但方向不同。仅有15%的关键点被赋予多个方向,但可以明显的提高关键点匹配的稳定性。
为了保证特征矢量的旋转不变性,要以特征点为中心,在附近邻域内将坐标轴旋转θ角度,即将坐标轴旋转为特征点的主方向。

在这里插入图片描述以旋转之后的主方向为中心取8x8的窗口,求每个像素的梯度幅值和方向,箭头方向代表梯度方向,长度代表梯度幅值,然后利用高斯窗口对其进行加权运算,最后在每个4x4的小块上绘制8个方向的梯度直方图,计算每个梯度方向的累加值,即可形成一个种子点,即每个特征的由4个种子点组成,每个种子点有8个方向的向量信息。
在这里插入图片描述Lowe实验结果表明,对每个关键点使用4x4共16个种子点来描述综合效果最优,这样一个关键点就会产生128维的SIFT特征向量。
在这里插入图片描述

Opencv实现

1、引入相关的库和灰度图片

import cv2
import numpy as np

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

2、得到特征点并在图中画出

sift = cv2.xfeatures2d.SIFT_create()
kp = sift.detect(gray, None)  # 关键点
img = cv2.drawKeypoints(gray, kp, img)

cv2.imshow('drawKeypoints', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

3、计算特征

kp, des = sift.compute(gray, kp)
print (np.array(kp).shape)  # (6807,)表示有6807个特征点
des.shape  # (6807, 128)表示每个特征点有128个维度

注意

由于专利保护机制,所使用的Opencv版本需要是3.4.2.16或以下版本。
安装方法:在命令行窗口输入

pip install opencv-python==3.4.2.16 
pip install opencv-contrib-python==3.4.2.16
发布了36 篇原创文章 · 获赞 1 · 访问量 537

猜你喜欢

转载自blog.csdn.net/qq_36758914/article/details/104021139
今日推荐