SIFT/SURF算法
1 SIFT算法
SIFT算法的实质是在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。SIFT所查找到的关键点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。
1.1 基本流程
将SIFT算法分解为如下四步:
1.尺度空间极值检测:搜索所有尺度上的图像位置。通过高斯差分函数来识别潜在的对于尺度和旋转不变的关键点。
2.关键点定位︰在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于
它们的稳定程度。
3.关键点方向确定:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向。所有后面的对图像数据的操作都相对于关键点的方向、尺度和位置进行变换,从而保证了对于这些变换的不变性。
4.关键点描述:在每个关键点周围的邻域内,在选定的尺度上测量图像局部的梯度。这些梯度作为关键点的描述符,它允许比较大的局部形状的变形或光照变化。
1.2 尺度空间极值检测
在不同的尺度空间使用不同的窗口检测极值点,对小的关键点使用小的窗口,对大的关键点使用大的窗口,为了达到上述目的,我们使用尺度空间滤波器。
1.3关键点定位
由于DoG对噪声和边缘比较敏感,因此在上面高斯差分金字塔中检测到的局部极值点需经过进一步的检验才能精确定位为特征点。
使用尺度空间的泰勒级数展开来获得极值的准确位置,如果极值点的灰度值小于阈值(一般为0.03或0.04)就会被忽略掉。在 OpenCV中这种阈值被称为contrastThreshold。
DoG算法对边界非常敏感,所以我们必须要把边界去除。Harris算法除了可以用于角点检测之外还可以用于检测边界。从 Harris 角点检测的算法中,当一个特征值远远大于另外一个特征值时检测到的是边界。那在DoG算法中欠佳的关键点在平行边缘的方向有较大的主曲率,而在垂直于边缘的方向有较小的曲率,两者的比值如果高于某个阈值(在OpenCV中叫做边界阈值),就认为该关键点为边界,将被忽略,一般将该阈值设置为10。
将低对比度和边界的关键点去除,得到的就是我们感兴趣的关键点。
1.4 关键点方向确定
经过上述两个步骤,图像的关键点就完全找到了,这些关键点具有尺度不变性。为了实现旋转不变性,还需要为每个关键点分配一个方向角度,也就是根据检测到的关键点所在高斯尺度图像的邻域结构中求得一个方向基准。
完成关键点梯度计算后,使用直方图统计关键点邻域内像素的梯度幅值和方向。具体做法是,将360°分为36柱,每10°为一柱,然后在以r为半径的区域内,将梯度方向在某一个柱内的像素找出来,然后将他们的幅值相加在一起作为柱的高度。
1.5 关键点描述
通过以上步骤,每个关键点就被分配了位置,尺度和方向信息。接下来我们为每个关键点建立一个描述符,该描述符既具有可区分性,又具有对某些变量的不变性,如光照,视角等。而且描述符不仅仅包含关键点,也包括关键点周围对其有贡献的的像素点。主要思路就是通过将关键点周围图像区域分块,计算块内的梯度直方图,生成具有特征向量,对图像信息进行抽象。
1.6 总结
SIFT在图像的不变特征提取方面拥有无与伦比的优势,但并不完美,仍然存在实时性不高,有时特征点较少,对边缘光滑的目标无法准确提取特征点等缺陷,自SIFT算法问世以来,人们就一直对其进行优化和改进,其中最著名的就是SURF算法。
1.7 SURF原理
使用SIFT算法进行关键点检测和描述的执行速度比较慢,需要速度更快的算法。2006年Bay提出了
SURF算法,是SIFT算法的增强版,它的计算量小,运算速度快,提取的特征与SIFT几乎相同,将其与SIFT算法对比如下:
2 算法实现
在OpenCV中利用SIFT检测关键点的流程如下所示:
- 实例化sift
sift = cv.xfeatures2d.SIFT_create()
- 利用sift.detectAndCompute()检测关键点并计算
kp,des=sift.detectAndCompute(gray,None)
参数:
- gray:进行关键点检测的图像,用灰度图像
返回:
- kp:关键点信息,包括位置,尺度,方向信息
- des:关键点描述符,每个关键点对应128个梯度信息的特征向量
3 将关键点检测结果绘制在图像上
cv.drawKeypoints(image,keypoints,outputimage,color,flags)
参数:
-
image:原始图像
-
keypoints:关键点信息,将其绘制在图像上
-
outputimage:输出图片,可以是原始图像
-
color:颜色设置,通过修改(b,g,r)的值,更改画笔的颜色,b=蓝色,g=绿色,r=红色
-
flags:绘图功能的标识设置
1.cv2.DRAW_MATCHES_FLAGS_DEFAULT:创建输出图像矩阵,使用现存的输出图像绘制匹配对和特征点,对每一个关键点只绘制中间点
2.cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG:不创建输出图像矩阵,而是在输出图像
上绘制匹配对
3.cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS:对每一个特征点绘制带大小和方向的关键点图形
4.cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS:单点的特征点不被绘制
SURF算法的应用与上述流程是一致的
示例:利用SIFT算法检测图片关键点,并绘制出来:
import cv2 as cv
import matplotlib.pyplot as plt
img = cv.imread("../image/yy.jpg")
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# sift关键点检测
# 1 实例化sift对象
sift = cv.xfeatures2d.SIFT_create()
# 2 关键点检测:kp关键点信息包括方向,尺度,位置信息,des是关键点的描述符
kp,des = sift.detectAndCompute(gray,None)
# 3 在图像上绘制关键点的检测结果
cv.drawKeypoints(img,kp,img,flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
plt.figure(figsize=(8,6),dpi=100)
plt.imshow(img[:,:,::-1]),plt.title("sift检测")
plt.show()
注:以上数据来自b站黑马程序员视频,点击跳转