初探SIFT特征(附代码实现)

冒泡...恍恍惚惚了几天...还是要振作冲鸭!

SIFT

介绍

SIFT(Scale-invariant feature transform)即尺度不变特征转换。
SIFT特征是基于物体上的一些局部外观的兴趣点,与影像的大小和旋转无关。SIFT算法是用来提取图像的局部特征的算法,它的实质是在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。(注:查找到的关键点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。)

特点

1.SIFT特征是图像的局部特征,其对旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声也保持一定程度的稳定性;
2.独特性(Distinctiveness)好,信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配;
3.多量性,即使少数的几个物体也可以产生大量的特征向量。
4.高速性,经优化的匹配算法甚至可以达到实时的要求。
5.可扩展性,可以很方便的与其他形式的特征向量进行联合。

算法

1、初始化操作,构建图像的金字塔
2、关键点搜索、定位、筛选
3、关键点方向赋值
4、关键点描述子的生成

分步骤详解:
(1)初始化操作,构建图像的金字塔
名词解释:
图像尺度空间:图像的尺度空间它可以模拟人在距离目标由近到远的过程中,目标在视网膜当中形成的图像的过程。尺度越大图像越模糊,相当于我们观察远处物体,这时候关注该物体的轮廓。比如可以这样理解,在过马路的时候我们只可以看清马路对面的行人的轮廓,但是他衣服的花纹就会显得比较模糊。
特点:


高斯模糊: 这里尺度空间的生成需要使用高斯模糊来实现,Lindeberg等人已经证明高斯卷积核是实现尺度变换的唯一线性核。高斯模糊是一种图像滤波器,它使用正态分布(高斯函数)计算模糊模板,并使用该模板与原图像做卷积运算,达到模糊图像的目的。
实现:

八度(octave): 八度就是在特定尺寸(长宽)下,经不同高斯核模糊的图像的集合。八度的集合是高斯金字塔。

第一步中的初始化操作就是通过高斯卷积核来实现尺度变换,从而模拟图像的多尺度特征,之后就是建立高斯金字塔和差分高斯金字塔。

建立高斯金字塔:高斯金字塔构建过程中,一般首先将图像扩大一倍,在扩大的图像的基础之上构建高斯金字塔,然后对该尺寸下图像进行高斯模糊,几幅模糊之后的图像集合构成了一个八度,然后对该八度下的最模糊的一幅图像进行下采样的过程,长和宽分别缩短一倍,图像面积变为原来四分之一。这幅图像就是下一个八度的初始图像,在初始图像图像的基础上完成属于这个八度的高斯模糊处理,以此类推完成整个算法所需要的所有八度构建,这样这个高斯金字塔就构建出来了。


建立差分高斯金字塔:差分,顾名思义,也就是要做差。对同一个八度的上下两幅相邻的图像做差得到插值图像,所有八度的这些插值图像的集合,就构成了差分高斯金字塔。差分高斯金字塔的好处是为后续的特征点的提取提供了方便。

关于高斯金字塔的生成细节可以参考:(https://blog.csdn.net/hit2015spring/article/details/52895367)

(2)关键点搜索、定位、筛选
寻找:实质是为了寻找尺度空间的极值点,因此每一个采样点要和它所有的相邻点比较,看其是否比它的图像域和尺度域的相邻点大或者小。
举个例子:如图所示,中间的检测点和它同尺度的8个相邻点和上下相邻尺度对应的9×2个点共8+18=26个点比较,以确保在尺度空间和二维图像空间都检测到极值点。 一个点如果在DOG尺度空间本层以及上下两层的26个领域中是最大或最小值时,就认为该点是图像在该尺度下的一个特征点。下图中将叉号点要比较的26个点都标为了绿色。

同一组的相邻尺度之间进行寻找:
首尾两张图都只有一个相邻图,无法进行极值比较(极值点是连续的)。为了满足尺度变化连续性,要在octave每一组的顶层再生成3幅图像,所以高斯金字塔有S+3幅图像,而DOG金字塔有S+2幅图像。
定位:
在离散空间下寻找的点不一定是符合的,所以需要定位。
可以通过对尺度空间DoG函数进行曲线拟合寻找极值点来减小这种误差。

筛选:在DOG函数中,它存在比较强的边缘效应,而当特征点在边缘的时候,这些点就会很不稳定,所以,我们应该把这些边缘效应很强的点找出来,进行筛选。
详细过程可参见(https://blog.csdn.net/hit2015spring/article/details/52972890)
至此,我们得到了关键点的位置信息和尺度信息(x,y,σ)。

(3)关键点方向赋值
为了实现图像的旋转不变性,需要根据检测到的关键点的局部图像结构为特征点方向赋值。
梯度直方图:方向直方图的核心是统计以关键点为原点,一定区域内的图像像素点对关键点方向生成所作的贡献。

image.png

由此可以得到每一个点的幅值和幅角,但是由于与特征点的距离不同,各个点对特征点的支持也是不一样的,所以离得远的就支持弱一点,离得近的就支持大一点,这里面的权重就要通过一个规则来管理,而这个规则就是高斯滤波,即以特征点为圆心,做一个高斯滤波,给它周围的点加上一个高斯权重。高斯函数的方差当然还是与这个特征点所在的图像层的尺度有关。

直方图峰值代表该关键点邻域内图像梯度的主方向,当存在另一个相当于主峰值。

(4)关键点描述子的生成
注意:这个描述子不但包括关键点,也包括关键点周围对其有贡献的像素点。
a.描述子采样区域


b.区域坐标旋转:为了使sift特征点具有旋转不变性,要以特征点为中心,在附近邻域内旋转θ角,即旋转为特征点的方向。

c.计算采样区域梯度直方图
将旋转后区域划分为d×d个子区域(每个区域间隔为mσ像元),在子区域内计算8个方向的梯度直方图,绘制每个方向梯度方向的累加值,形成一个种子点。与求主方向不同的是,此时,每个子区域梯度方向直方图将0°~360°划分为8个方向区间,每个区间为45°。即每个种子点有8个方向区间的梯度强度信息。然后对子区域的像素的梯度大小采用mσd/2的高斯加权函数加权。由于存在d×d,即4×4个子区域,所以最终共有4×4×8=128个数据,形成128维SIFT特征矢量。特征向量形成后,为了去除光照变化的影响,需要对它们进行归一化处理。

SIFT的缺点

  1. 实时性不高。
  2. 有时特征点较少。
  3. 对边缘光滑的目标无法准确提取特征点。
    4.模糊的图像和边缘平滑的图像,检测出的特征点过少,对圆更是无能为力.

代码

import cv2
import numpy as np
img = cv2.imread('F:/3.jpg')  # 读取要处理的图片
alg = input('Select an Alg --> ')

def fd(algorithm):
    if algorithm == 'SIFT':
        return cv2.xfeatures2d.SIFT_create()
    if algorithm == 'SURF':
        threshold = input('Enter a threshold --> ')  # 提示输入SURF算法所采用的阈值
        return cv2.xfeatures2d.SURF_create(float(threshold))
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换为灰度图像
fd_alg = fd(alg)
keypoints, descriptor = fd_alg.detectAndCompute(gray, None)
img = cv2.drawKeypoints(image=img, outImage=img, keypoints=keypoints,
                        flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS,
                        color=(51, 163, 236))
cv2.imshow('keypoints', img)
while (True):
    if cv2.waitKey(30) & 0xff == ord('q'):
        break
cv2.destroyAllWindows()

结果



参考(https://blog.csdn.net/happyer88/article/details/45817305 )
(https://blog.csdn.net/zddblog/article/details/7521424)


Finally~so tired!

你可能感兴趣的:(初探SIFT特征(附代码实现))