学习笔记 图像特征提取----SIFT算法原理
本文参考唐宇迪opencv项目实战课
zddhub的博客
论文:Scale Invariant Feature Transform(SIFT) 长达56页:)
1 概述
SIFT算法(Scale Invariant Feature Transform)平移不变性图像特征匹配算法。
SIFT算法的特点
- SIFT特征是图像的局部特征,其对旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声也保持一定程度的稳定性
- 信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配
- 多量性,即使少数的几个物体也可以产生大量的SIFT特征向量
- 高速性,经优化的SIFT匹配算法甚至可以达到实时的要求
- 可扩展性,可以很方便的与其他形式的特征向量进行联合
SIFT算法在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。SIFT所查找到的关键点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。
中SIFT算法的步骤
- 图像的尺度空间
- 多分辨率金字塔
- 高斯差分金字塔
- 关键点精确定位
- 消除边界影响
- 特征点的主方向
- 生成特征描述
2 算法步骤
2.1 图像的尺度空间
在一定的范围内无论物体是大还是小,人眼都能将其分辨出来。SIFT算法目的是是计算机对于物体在不同的尺度下又一个统一的认知。也就是说在对物体的旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声也保持一定程度的稳定性。
于是就要考虑图像在不同尺度下的特点。
通过高斯滤波实现尺度空间的获取
正态分布的标准差越大,则高斯滤波后的图像越模糊。
二维高斯曲面如图所示:
高斯模板是中心对称的。每个像素的值都是周围相邻像素值的加权平均。
这里高斯模糊并不是直接调用时域下的高斯滤波函数gussian = cv2.GaussianBlur(img, (5, 5), 1)
而是可以更改标准差的值,以改变图像模糊的程度。
from PIL import Image, ImageFont
from PIL import ImageFilter
import cv2
im = Image.open('ggg.jpg')
im = im.filter(ImageFilter.GaussianBlur(radius=1))
im.show()
im = im.filter(ImageFilter.GaussianBlur(radius=2))
im.show()
im = im.filter(ImageFilter.GaussianBlur(radius=3))
im.show()
结果如图:
原图
标准差为3:
标准差为5:
标准差为7:
2.2 高斯差分金字塔(DOG)
图像多分辨率金字塔
图像金字塔的原理(用于特征提取)
Gaussian 差分金字塔从下至上,采用下采样的方式,与高斯内核卷积,将所有的偶数行和列去掉。
Gaussian 差分金字塔的定义公式:
Gaussian 差分金字塔
Gaussian差分金字塔示意图:
在图像金字塔中同一层的图像具有相同的大小,只是选择了不同的高斯模糊的标准差,而具有不同的模糊程度。
高斯差分金字塔(DOG)中对同一层的模糊图像进行差分,得到差异性结果。
如果X为极值点,则不仅在同一层中X是周围9个点中的极值点,而且,在示意图的情况下还要原上一层和下一层进行比较,该点才能看做是极值点。也就是X点同除了自身以外的3×9-1=26个点进行比较,判断其是否是极值点。
2.3 极值点的精确定位
通过上述方法获得了候选关键点,但这些点不一定是极值点的准确位置,可能在真正的极值点的附近。
所以我们要对尺度空间的DOG函数进行曲线拟合,实现关键点的精确定位。
空心的点表示上一步的到的候选关键点,实心原点表示真正的极值点。
这些候选关键点是高斯差分金字塔(DOG)空间中的局部极值点。这些极值点是离散的。那么问题变成了一只函数中一些离散的点,求这个函数的真实的极值点。
我们上一步得到的候选关键点和真实的极值点同时分布在同一函数曲线上。
我们用泰勒级数对原函数进行拟合。
由一个一个的离散点拟合出原函数f(x).再多元函数求导,令导数等于0,求出真正的极值点。
在高斯差分金字塔(DOG)空间下每一个候选关键点可以表示为:
以3×3的矩阵为例,将矩阵写成泰勒展开式的形式:
化简后表示为:
2.4 消除边界相应
DOG算子会产生较强的边缘响应,需要剔除不稳定的边缘响应点。获取特征点处的Hessian矩阵,主曲率通过一个2x2 的Hessian矩阵H求出 ,用来消除边界响应。
2.5 精确定位的关键点如何表示出来
特征点的主方向
每个特征点获得三个信息(x,y,σ,θ):位置、尺度和方向。
2.6 生成特征描述
以一个关键点为中心如图所示,画出适当的范围,做出八个方向的直方图,方向直方图的峰值则代表了该特征点处邻域梯度的方向,以直方图中最大值作为该关键点的主方向。
旋转不变性
为了保证适量的旋转不变性,以特征点为中心,在附近邻域内将坐标轴旋转,将特征点的主方向作为坐标轴的方向。
旋转之后的主方向为中心取8x8的窗口,求每个像素的梯度幅值和方向,箭头方向代表梯度方向,长度代表梯度幅值,然后利用高斯窗口对其进行加权运算,最后在每个4x4的小块上绘制8个方向的梯度直方图,计算每个梯度方向的累加值,即可形成一个种子点,即每个特征的由4个种子点组成,每个种子点有8个方向的向量信息。
每个关键点由4×4=16个种子点来描述。这样一个挂那间店就会产生128维的SIFT特征向量。
上代码:
import cv2
import numpy as np
img = cv2.imread('box.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
print(cv2.__version__)
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()
kp, des = sift.compute(gray, kp)
print(np.array(kp).shape)
print(des.shape)
print(des[0])
函数cv2.xfeatures2d.SIFT_create()
是实例化的SIFT函数。
函数sift.detect(gray, None)
找出图形中的关键点。
然后cv2.drawKeypoints(gray, kp, img)
画出关键点。
程序kp, des = sift.compute(gray, kp)
中这里找出来的kp将是一个关键点列表,des是形状Number_of_Keypoints*128的一个numpy数组
输出结果:
至此,SIFT算法图像特征提取项目完成。
有待完善,感谢为我提供帮助的博客和唐宇迪opencv项目实战课程。