【OpenCV 例程 300篇】242. 加速稳健特征检测算法(SURF)

时间:2022-10-10 12:53:35

『youcans 的 OpenCV 例程300篇 - 总目录』


【youcans 的 OpenCV 例程 300篇】242. 加速稳健特征检测算法(SURF)


加速稳健特征检测算法(SpeededUp Robust Features,SURF)是SIFT的改进算法,比SIFT更快更稳定,可以应用于实时的计算机视觉系统。

SURF算法的主要特点是采用积分图像构造金字塔尺度空间,采用Harr小波特征构造特征描述子。

【OpenCV 例程 300篇】242. 加速稳健特征检测算法(SURF)


6.5.1 数学方法

SURF 算法的结构框架与 SIFT 类似,基本步骤包括:构造尺度空间、检测关键点、选取主方向和特征描述。

(1)基于均值滤波器实现 Hessian 矩阵

SIFT 算法先构造尺度空间检测极值点,再通过 Hessian 矩阵判别并消除不稳定的边缘效应;SURF 算法先计算不同方差高斯滤波图像的 Hessian 矩阵,再构造尺度空间检测极值点。

计算 Hessian 矩阵和高斯滤波都是空间卷积运算,将这两个滤波器核相乘得到 Gaussian-Hessian 滤波器核,就可以将高斯滤波和二阶导数运算的步骤合并。

在二维图像空间,高斯滤波图像的 Hessian 矩阵为:
L ( x , y , σ ) = G ( x , y , σ ) ∗ I ( x , y ) H ( x , y , σ ) = [ L x x ( x , y , σ ) L x y ( x , y , σ ) L x y ( x , y , σ ) L y y ( x , y , σ ) ] D e t ( H ) = L x x ∗ L y y − L x y ∗ L x y L(x,y, \sigma) = G(x,y, \sigma) * I(x,y)\\ H(x,y, \sigma) = \begin{bmatrix} L_{xx} (x, y, \sigma) & L_{xy} (x, y, \sigma) \\ L_{xy} (x, y, \sigma) & L_{yy} (x, y, \sigma) \end{bmatrix} \\ Det(H) = L_{xx}*L_{yy} - L_{xy}*L_{xy} L(x,y,σ)=G(x,y,σ)I(x,y)H(x,y,σ)=[Lxx(x,y,σ)Lxy(x,y,σ)Lxy(x,y,σ)Lyy(x,y,σ)]Det(H)=LxxLyyLxyLxy

其中,Lxx、Lxy、Lyy 为高斯模糊图像在各个方向的二阶导数。Lxx 是高斯二阶微分算子 ∂ 2 g ( σ ) / ∂ x 2 \partial^2{g(\sigma)/\partial x^2} 2g(σ)/x2与图像 I 的卷积,也就是高斯拉普拉斯算子 LoG。

Hessian 矩阵的行列式 Det(H) 作为特征点的判别式,其极值点就是检测的关键点。

为了提高运算速度,SURF 算法使用均值滤波器(BoxFilter)来近似高斯滤波器。

【OpenCV 例程 300篇】242. 加速稳健特征检测算法(SURF)

上左图是对 y 方向高斯滤波后求二阶导数,上右图是 BoxFilter 的近似结果;下左图是对 xy 方向高斯滤波后求二阶混合偏导,下右图是 BoxFilter 的近似结果。

图中,灰色部分对应的权值为 0,因此右图中的 Lxx、Lyy 可以用 3个叠加的均值滤波器 BoxFilter 表示,Lxy 可以用 4 个叠加的 BoxFilter 表示。例如:
D y y = B o x F T o p + B o x F M i d d l e + B o x F B o t t o m D x y = B o x F L T + B o x F L R + B o x F L B + B o x F L R D_{yy} = BoxF_{Top} + BoxF_{Middle} + BoxF_{Bottom}\\ D_{xy} = BoxF_{LT} + BoxF_{LR} + BoxF_{LB} + BoxF_{LR} Dyy=BoxFTop+BoxFMiddle+BoxFBottomDxy=BoxFLT+BoxFLR+BoxFLB+BoxFLR
进一步地,SURF 算法使用积分图像来计算均值滤波器,极大地提高了运算速度。积分图像本质上是一种查找表,不仅极大地提高了计算效率,而且可以在不同尺度空间同时进行,并行实现。

由于使用了叠加均值滤波器 Dxx、Dxy、Dyy 进行近似,需要对Hessian 矩阵的行列式 Det(H) 进行修正:
D e t ( H a p p r o x ) = D x x D y y − ( 0.9 ∗ D x y ) 2 Det(H_{approx}) = D_{xx} D_{yy} - (0.9*D_{xy})^2 Det(Happrox)=DxxDyy(0.9Dxy)2

其中,Dxx、Dxy、Dyy 为使用叠加均值滤波器实现的近似高斯二阶微分 Lxx、Lxy、Lyy。

(2)构造尺度空间

为了获取特征点的尺度参数,可以通过建立图像金字塔构造尺度空间。

SIFT 算法通过不同σ的高斯函数,对图像进行平滑滤波,然后降采样以获得下一组的缩小图像。各组图像大小不同,下组是上组的降采样(1/4);每组中各层的图像大小相同,高斯模糊尺度不同。

SURF 算法通过不断增大均值滤波器的尺寸(ksize),而不是以减小图像尺寸来构造尺度空间。各组图像大小相同,因此不需要降采样,提高了处理速度。 这种方法的缺点是均值滤波器保留了高频分量,对于尺度不变性有些影响。

尺度空间划分为一系列的倍频程,每组倍频程包括多个滤波响应图像,通过相同尺寸的输入图像与尺寸不断增大的滤波器卷积得到。第一组(Octave1)盒式滤波器核的初始尺寸为 9*9,近似于 σ=1.2 的高斯二阶偏导滤波器,逐渐增大滤波器核的尺寸为:9*9、15*15、21*21、27*27;第二组(Octave2)盒式滤波器核的初始尺寸为 15*15,逐渐增大滤波器核的尺寸为:15*15、27*27、39*39、51*51;依次类推…。

随着滤波器尺寸的增加,可以在大尺度的图像上采用间隔采样的方法,计算滤波响应值来检测特征点,这与降采样是等效的。

(3)特征点定位:

特征点定位的方法与 SIFT 算法类似。

首先在 3*3*3 邻域中应用非最大值抑制,在空间位置和尺度空间定位局部特征点。将像素点与邻域中的 26 个点(同尺度的 8 个相邻点及上下相邻尺度对应的 9*2 个点)进行比较,如果是最大值/最小值则保留作为关键点。

通过非最大值抑制所得到的极值点在尺度空间和像素空间都是离散的,所获得的极值点位置不准确。使用子像素插值方法(Sub-pixel interpolation),通过三维二次拟合函数可以求出亚像素位置精度的极值点,精确确定关键点的位置和尺度。

(4)选取关键点的主方向

为了保证特征矢量具有旋转不变形, 需要对于每一个特征点分配一个主要方向。

SIFT 算法根据关键点的邻域的梯度直方图确定主方向,而 SURF 算法在关键点的邻域计算 Haar 小波特征来确定主方向。

SURF 统计半径为 6S 的邻域内的 Haar 小波特征。构造一个以关键点为中心、以 6S 为半径、张角为 60度的扇形滑动窗口,以 0.2 弧度为旋转步长进行旋转。对扇形滑动窗口内的图像 Haar 小波的响应值进行累加 ,以最大累加值所对应的方向作为该关键点的主方向。

Haar 小波运算的本质是梯度运算,但利用积分图像可以提高计算图像梯度的效率。

(5)构造关键点的特征描述子

SIFT 把关键点的邻域划分为 4x4=16 个子块,每个子块统计 8 个方向的梯度,得到 4x4x8=128 维向量量作为 SIFT 描述子。

通过主方向旋转矫正后,SURF 将关键点周围 20s×20s 的邻域划分为 4×4=16 个子块,每个子块 5s×5s 个像素。对每个子块,用尺度为 2S 的 Haar 小波计算水平方向和垂直方向(相对于主方向)的小波响应值,构造 4 维特征向量 v = ( ∑ d x , ∑ d y , ∑ ∣ d x ∣ , ∑ ∣ d y ∣ ) v=(\sum dx, \sum dy, \sum |{dx}|, \sum |{dy}|) v=(dx,dy,dx,dy)

将 4×4 个子块的 4维特征向量 v 组合,形成 64 位的特征向量, 就是 SURF 的特征描述符。


6.5.2 OpenCV 中的 SURF 类

OpenCV 中提供 cv::xfeatures2d::SURF 类实现 SURF 方法。SURF 类继承了 cv::Feature2D 类,通过 create 静态方法创建。

SURF类的构造函数为:

SURF::SURF()
SURF::SURF(double hessianThreshold, int nOctaves=4, int nOctaveLayers=2, bool extended=true, bool upright=false )

1、创建 SURF 对象:

在Python语言中,通过SURF类的接口函数cv. SURF.create或cv. SURF_create实例化SURF类,创建SURF对象。

cv.xfeatures2d.SURF.create([, hessianThreshold=100, nOctaves=4, nOctaveLayers=3, extended=false, upright=false]) → retval
cv.xfeatures2d.SURF_create([, hessianThreshold=100, nOctaves=4, nOctaveLayers=3, extended=false, upright=false]) → retval

SURF类继承了 cv::Feature2D类,通过类成员函数surf.detect检测关键点,函数surf.compute计算关键点的描述符,函数surf.detectAndCompute检测关键点并计算关键点的描述符。

参数说明:

  • hessianThreshold:Hessian 关键点检测器的阈值,可选项,默认值 100
  • nOctaves:尺度空间金字塔的倍频程数量,即金字塔的组数,默认值 4
  • nOctaveLayers:每个倍频程的层数,默认值 3
  • extended:描述符扩展标志,默认值 false 表示描述符为 64 个元素,true 为 128个元素
  • upright:是否计算特征方向标志,默认值 false 计算特征方向,true 表示不计算方向

2、检测特征点:

cv.Feature2D.detect(image, [, mask]) → keypoints
cv.Feature2D.detectAndCompute(image, mask[, descriptors[, useProvidedKeypoints]]) → keypoints, descriptors

参数说明:

  • image:输入图像,单通道
  • mask:掩模图像,8 位单通道,指定查找关键点的区域,可选项
  • keypoints :检测到的关键点,元组,keypoints[i] 是cv:: Feature2D定义的数据结构,包括以下属性:
    • Point2f pt:坐标
    • float size:关键点的邻域直径
    • float angle:关键点的方向,取值为 [0,360)
    • float response:关键点的响应强度
    • int octave:关键点所在的图像金字塔的组
    • int class_id:关键点的 id 编号
  • descriptor:关键点的描述符,Numpy数组,形状为(n,64)或(n,128)

3、注意问题

  • ⒈SURF是专利算法,OpenCV3、OpenCV4将其移入了OpenCV_contrib扩展模块中,因此使用SURF需要opencv-contrib-python包的支持。

  • ⒉在OpenCV3/Python的一些版本中对编译进行了限制,使用SURF算法时会出现错误。建议卸载后重新安装OpenCV3.4.2解决。

  • ⒊函数cv.xfeatures2d.SURF.create或cv.xfeatures2d.SURF_create实例化SURF类,构造一个 SURF 对象。在OpenCV的不同版本中可能只允许其中一种方式。

  • ⒋函数detect、compute、detectAndCompute等是从Feature2D类继承的类成员函数,在程序中的格式为surf.detect,surf表示SURF类的实例对象。

  • ⒌关键点描述符descriptor的形状为(n,64)或(n,128),n是关键点的数量,数组元素为浮点数。descriptors[i]是关键点keypoints[i]的描述符,扩展标志extended控制描述符由64或128个元素构成。


例程 14.22:特征检测之 SURF 算法

本例程示例加速鲁棒特征变换(SURF)的应用。

# 【1614】特征检测之加速鲁棒特征变换(SURF)
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

if __name__ == '__main__':
    img = cv.imread("../images/Circuit04.png", flags=1)
    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)  # (425, 558)
    height, width = gray.shape[:2]
    print("shape of image: ", height, width)

    # 比较:SIFT 关键点检测
    # sift = cv.xfeatures2d.SIFT_create()  # OpenCV 早期版本
    sift = cv.SIFT.create()  # sift 实例化对象
    kpSift = sift.detect(gray, None)  # 关键点检测,kp 为关键点信息(包括方向)
    print("shape of keypoints: ", len(kpSift))  # 775
    imgSift1 = cv.drawKeypoints(img, kpSift, None)  # 只绘制关键点位置
    imgSift2 = cv.drawKeypoints(img, kpSift, None, flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)  # 绘制关键点大小和方向

    # 比较:SURF 关键点检测
    surf = cv.xfeatures2d.SURF.create(1000)  # surf 实例化对象
    kpSurf, desSurf = surf.detect(gray, None)  # 关键点检测,kp 为关键点信息(包括方向)
    print("shape of keypoints: ", len(kpSurf))  # 775
    imgSurf1 = cv.drawKeypoints(img, kpSurf, None)  # 只绘制关键点位置
    imgSurf2 = cv.drawKeypoints(img, kpSurf, None, flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)  # 绘制关键点大小和方向

    plt.figure(figsize=(9, 6))
    plt.subplot(221), plt.title("SIFT keypoints")
    plt.axis('off'), plt.imshow(cv.cvtColor(imgSift1, cv.COLOR_BGR2RGB))
    plt.subplot(222), plt.title("SIFT scaled keypoints")
    plt.axis('off'), plt.imshow(cv.cvtColor(imgSift2, cv.COLOR_BGR2RGB))
    plt.subplot(223), plt.title("SURF keypoints")
    plt.axis('off'), plt.imshow(cv.cvtColor(imgSurf1, cv.COLOR_BGR2RGB))
    plt.subplot(224), plt.title("SURF scaled keypoints")
    plt.axis('off'), plt.imshow(cv.cvtColor(imgSurf2, cv.COLOR_BGR2RGB))
    plt.tight_layout()
    plt.show()


【OpenCV 例程 300篇】242. 加速稳健特征检测算法(SURF)


对于 SURF 算法有兴趣的朋友,推荐阅读 Herbert Bay 等的原文和赵春江博士的OpenCV源码解读:

Herbert Bay, etal, Speeded-Up Robust Features (SURF), Computer Vision and Image Understanding, V110(2008), 346-359, doi:10.1016/j.cviu.2007.09.014
赵春江,Opencv2.4.9源码分析—SURF,https://blog.csdn.net/zhaocj/article/details/42584285

【本节完】

版权声明:
youcans@xupt 原创作品,转载必须标注原文链接:(https://blog.csdn.net/youcans/article/details/125828053)
Copyright 2022 youcans, XUPT
Crated:2022-10-10

240. OpenCV 中的 Shi-Tomas 角点检测
241. 尺度不变特征变换(SIFT)
242. 加速稳健特征检测算法(SURF)