OpenCV day12 - Scale Invariant Feature Transform(SIFT)

275 阅读4分钟

恒定尺度特征转换

一、尺度空间定义

在一定范围内,无论物体是大还是小,人眼都可以分辨出来,但是计算机则不然,很难将不同大小的同一物体识别出来,所以要让机器能对物体在不同尺度下有一个统一的认知,也就是考虑图像在不同尺度下都存在的特点

尺度空间的获取通常使用高斯模糊来实现

L(x,y,σ)=G(x,y,σ)I(x,y)L(x, y, \sigma) = G(x, y , \sigma) * I(x, y),其中G是高斯函数 G(x,y,σ)=12πσ2ex2+y22σ2G(x, y, \sigma)=\frac{1}{2 \pi \sigma^{2}} e^{\frac{x^{2}+y^{2}}{2 \sigma^{2}}}σ\sigma标准差,和图像变换幅度成正比,越大图像就越模糊。

高斯5阶滤波.PNG

1. 多分辨率金字塔

多分辨率金字塔.PNG

2. 高斯差分金字塔(DOG)

差分金字塔.PNG

① DOG定义公式

D(x,y,σ)=[G(x,y,kσ)G(x,y,σ)]I(x,y)=L(x,y,kσ)L(x,y,σ)D(x, y, \sigma) = [G(x, y, k\sigma) - G(x, y, \sigma)] * I(x, y) = L(x, y, k\sigma) - L(x, y, \sigma),k不等于1,表示不同参数的高斯滤波

② DOG极值检测

为了寻找尺度空间内部的极值点,每个像素点都要和其图像域(统一尺度空间)和尺度域(相邻的尺度空间)的所有相邻点进行比较,当其大于(或小于)所有相邻点时,该点就是极值点。如下图所示:中间层的监测点要和其所在图像层的 D8 邻域内的 8 个像素点、相邻上下层的 3 * 3 邻域各 9 各像素点进行比较

极值检测.PNG

③ 关键点的精确定位

这些候选点是DOG空间的局部极值点,而且这些极值点均为离散点,精确定位极值点的一种方法是:对尺度空间的DoG函数进行曲线拟合,计算其极值点,从而实现关键点的精确定位

极值点定位.PNG

D(Δx,Δy,Δσ)=D(x,y,σ)+[DxDyDσ][ΔxΔyΔσ]+12[ΔxΔyΔσ][2Dx22Dxy2Dxσ2Dyx2Dy22Dyσ2Dσx2Dσy2Dσ2][ΔxΔyΔσ]D(x)=D+DTxΔx+12ΔxT2DTx2ΔxΔx=2D1x2D(x)x\begin{array}{l} D(\Delta x, \Delta y, \Delta \sigma)=D(x, y, \sigma)+\left[\begin{array}{lll} \frac{\partial D}{x} & \frac{\partial D}{y} & \frac{\partial D}{\sigma} \end{array}\right]\left[\begin{array}{c} \Delta x \\ \Delta y \\ \Delta \sigma \end{array}\right]+\frac{1}{2}\left[\begin{array}{lll} \Delta x & \Delta y & \Delta \sigma \end{array}\right]\left[\begin{array}{ccc} \frac{\partial^{2} D}{\partial x^{2}} & \frac{\partial^{2} D}{\partial x \partial y} & \frac{\partial^{2} D}{\partial x \partial \sigma} \\ \frac{\partial^{2} D}{\partial y \partial x} & \frac{\partial^{2} D}{\partial y^{2}} & \frac{\partial^{2} D}{\partial y \partial \sigma} \\ \frac{\partial^{2} D}{\partial \sigma \partial x} & \frac{\partial^{2} D}{\partial \sigma \partial y} & \frac{\partial^{2} D}{\partial \sigma^{2}} \end{array}\right]\left[\begin{array}{c} \Delta x \\ \Delta y \\ \Delta \sigma \end{array}\right] \\ D(x)=D+\frac{\partial D^{T}}{\partial x} \Delta x+\frac{1}{2} \Delta x^{T} \frac{\partial^{2} D^{T}}{\partial x^{2}} \Delta x \quad \Delta x=-\frac{\partial^{2} D^{-1}}{\partial x^{2}} \frac{\partial D(x)}{\partial x} \end{array}

3. 消除边界响应

① Hessian矩阵

Hessian矩阵:

H(x,y)=[Dxx(x,y)Dxy(x,y)Dxy(x,y)Dyy(x,y)] 令 α=λmax 为最大的特征值, β=λmin 为最小的特征值 Tr(H)=Dxx+Dyy=α+βTr(H)2Det(H)=(α+β)2αβ=(γ+1)2γDet(H)=DxxDyy(Dxy)2=αβ\begin{array}{cc} H(x, y)=\left[\begin{array}{ll} D_{x x}(x, y) & D_{x y}(x, y) \\ D_{x y}(x, y) & D_{y y}(x, y) \end{array}\right] & \text { 令 } \alpha=\lambda_{\max} \text { 为最大的特征值, } \beta=\lambda_{\min} \text { 为最小的特征值 } \\ \operatorname{Tr}(H)=D_{x x}+D_{y y}=\alpha+\beta & \frac{\operatorname{Tr}(H)^{2}}{\operatorname{Det}(H)}=\frac{(\alpha+\beta)^{2}}{\alpha \beta}=\frac{(\gamma+1)^{2}}{\gamma} \\ \operatorname{Det}(H)=D_{x x} D_{y y}-\left(D_{x y}\right)^{2}=\alpha \beta & \end{array} Lowe在论文中给出的 γ\gamma=10 ,也就是说对于主曲率比值大于10的特征点将被删除。

② 特征点的主方向

每个点 L(x, y) 的梯度的模m (x, y) 以及方向 θ(x,y) \theta(x, y) :

m(x,y)=[L(x+1,y)L(x1,y)]2+[L(x,y+1)L(x,y1)]2θ(x,y)=arctanL(x,y+1)L(x,y1)L(x+1,y)L(x1,y)\begin{array}{l} m(x, y)=\sqrt{[L(x+1, y)-L(x-1, y)]^{2}+[L(x, y+1)-L(x, y-1)]^{2}} \\ \theta(x, y)=\arctan \frac{L(x, y+1)-L(x, y-1)}{L(x+1, y)-L(x-1, y)} \end{array}

每个特征点可以得到三个信息(x,y,σ,θ)(x, y, \sigma, \theta),也就是位置、尺度和方向。具有多个方向的关键点可以被复制为多份,然后将方向值分别赋给复制后的特征点,一个特征点就产生了多个坐标、尺度相等,但是方向不同的特征点

③ 生成特征描述

在完成关键点的梯度计算之后,使用直方图统计邻域内的梯度和方向。

生成特征描述.PNG

为了保证特征矢量的旋转不变性,要以特征点为中心,在附近邻域内将坐标轴旋转 θ\theta 角度,也即是将坐标轴旋转为特征点的主方向

矢量旋转.PNG

[xy]=[cosθsinθsinθcosθ][xy]\left[\begin{array}{l} x^{\prime} \\ y^{\prime} \end{array}\right]=\left[\begin{array}{cc} \cos \theta & -\sin \theta \\ \sin \theta & \cos \theta \end{array}\right]\left[\begin{array}{l} x \\ y \end{array}\right]

旋转之后的主方向为中心,取大小为 8 * 8的仇口,求每个像素的梯度幅值和方向,箭头方向代表梯度方向,长度代表梯度幅值,然后利用高斯窗口对其进行加权运算,最后在每个 4 * 4的小块上绘制 8 个方向的梯度直方图,计算每个梯度的累加值,即可形成一个种子点,即每个特征由4个种子点组成,每个种子点都有 8 个方向的向量信息

统计直方图.PNG

建议对每个关键点都是用 4 * 4 一共 16 个种子来描述,这样的一个关键点就会产生 128 维的 SIFT 特征向量

种子.PNG

二、OpenCV SIFT函数

import cv2
import numpy as np
def cv_show(name, image):
    cv2.imshow(name, image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
img = cv2.imread('Lena.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show('L', img)
cv2.__version__ # 4.4版本以上的可以直接使用SIFT_create()
'4.5.5'

得到特征点

sift = cv2.SIFT_create() # 直接使用就行
kp = sift.detect(gray, None) # 关键点集
result = cv2.drawKeypoints(gray, kp, gray)

res = np.hstack([img, result])
cv_show('KeyFeatures', res)

绘制特征点.PNG

计算特征

kp, des = sift.compute(gray, kp)
print(np.array(kp).shape)
(1100,)
des.shape
(1100, 128)
des[0]
array([  0.,   1.,   0.,   1.,  58., 153.,   0.,   0.,  29.,   0.,   0.,
         1.,  15.,  69.,   2.,  24.,  42.,   0.,   0.,   0.,   0.,   0.,
         0.,  36.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,  10.,
        34.,   0.,   0.,  42., 153.,   0.,   0.,  90.,   2.,   0.,   0.,
        54., 153.,   9.,  19., 153.,  17.,   0.,   0.,   1.,  13.,   5.,
        47.,  16.,   3.,   0.,   0.,   0.,   0.,   0.,   2.,  53., 153.,
         0.,   0.,   9.,  48.,   0.,   0.,  24.,  16.,   0.,   0.,  44.,
       153.,  27.,  16., 153.,  13.,   0.,   0.,   3.,  54.,  34.,  68.,
        31.,   5.,   1.,   4.,   4.,   0.,   0.,   3.,  74., 153.,   0.,
         0.,   1.,   3.,   1.,   0.,  24., 114.,   2.,   0.,   9.,  47.,
        11.,   3.,  23.,   3.,   0.,   1.,   4.,  23.,  26.,  49.,   6.,
         0.,   0.,  10.,  14.,   0.,   0.,   6.], dtype=float32)