【OpenCV 例程 300篇】245. 特征检测之 BRISK 算子

时间:2022-10-23 12:55:34

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


【youcans 的 OpenCV 例程 300篇】245. 特征检测之 BRISK 算子


6.8.1 算法简介

尺度不变的二进制特征描述 BRISK (Binary Robust Invariant Scalable Kepoints), 是改进的 BRIEF 算法,也是二进制特征描述符。具有高计算效率和旋转不变性、尺度不变性,对噪声也有一定的鲁棒性。

1、建立尺度空间

为了实现尺度不变性,BRISK 算法通过构造图像金字塔来获得每个关键点的尺度。BRISK 算法的图像金字塔由 n 层倍频程(octave)和 n 层内部倍频程(intra-octave)组成。倍频程的上一层图像通过对下一层图像降采样得到,内部倍频程位于相邻倍频程之间。

img c0 d0 c1 d1 c2 d2 c3 d3
high h 2/3*h 1/2*h 1/3*h 1/4*h 1/6*h 1/8*h 1/12*h
width w 2/3*w 1/2*w 1/3*w 1/4*w 1/6*w 1/8*w 1/12*w

2、关键点检测

与 SIFT 算法类似,关键点检测包括尺度空间角点检测、非最大值抑制和亚像素插值三个步骤。

(1)使用 AGAST 方法的 FAST9-16、FAST5-8 检测器进行角点检测。对原图像 img 使用 FAST5-8 进行角点检测,作为 d(-1) 层;对图像 c0~c3、d0~d3 使用 FAST9-16 进行角点检测。共得到 9 幅角点检测图像。

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

(3)使用子像素插值方法(Sub-pixel interpolation)进行精确定位。通过非最大值抑制所得到的极值点在尺度空间和像素空间都是离散的,获得的极值点位置和尺度大小的精度都较低。

先根据 FAST 得分值在像素空间进行二维二次函数插值,得到关键点的精确坐标位置;再对尺度方向进行一维插值,得到关键点的精确尺度。

3、采样模式和主方向

BRISK 描述子的关键在于对关键点邻域的采样模式。如图所示,在以特征点(关键点)为中心的 S*S 区域采样 N 个点,构成 N ∗ ( N − 1 ) / 2 N*(N-1)/2 N(N1)/2 对采样点对 ( p i , p j ) (p_i, p_j) (pi,pj)


【OpenCV 例程 300篇】245. 特征检测之 BRISK 算子

为了避免图像混叠,对采样点 P 进行方差为 σ \sigma σ 的高斯滤波,方差 σ \sigma σ 与采样点到中心的距离 r 成正比。

再计算局部梯度值:
g ( p i , p j ) = ( p j − p i ) ∗ I ( p j , σ j ) − I ( p i , σ i ) ∣ ∣ p j − p i ∣ ∣ 2 g(p_i, p_j) = (p_j - p_i)* \frac{I(p_j,\sigma_j) - I(p_i,\sigma_i)}{||p_j - p_i||^2} g(pi,pj)=(pjpi)∣∣pjpi2I(pj,σj)I(pi,σi)
其中, g ( p i , p j ) g(p_i, p_j) g(pi,pj) 表示局部梯度值, I ( p j , σ j ) I(p_j,\sigma_j) I(pj,σj) 表示像素值, p p p 是关键点位置, σ \sigma σ 是关键点尺度。

考虑所有的采样点对,由短距离阈值 δ m a x = 9.75 ∗ t \delta_{max}=9.75*t δmax=9.75t 和长距离阈值 δ m i n = 13.67 ∗ t \delta_{min}=13.67*t δmin=13.67t(t 是特征点的尺度 k),得到短距离点对子集 S 和长距离点对子集 L。

由局部梯度值可以计算关键点的主方向:
g = ( g x g y ) = 1 L ∗ ∑ ( p i , p j ) ∈ L g ( p i , p j ) g = \binom{g_x}{g_y} = \frac{1}{L}*\sum_{(pi,pj) \in L} g(p_i,p_j) g=(gygx)=L1(pi,pj)Lg(pi,pj)
将特征点周围的采样区域旋转到主方向,旋转角度为 α = a r c t a n 2 ( g y , g x ) \alpha=arctan2(g_y, g_x) α=arctan2(gy,gx),从而实现了旋转不变性。

4、二进制特征描述符

BRISK 描述子是二值特征,描述子的每一位 bit 对应于短距离点对的比较结果:
b = { 1 , I ( p j α , σ j ) > I ( p i α , σ i ) 0 , e l s e b=\begin{cases} 1 &, I(p_j^\alpha,\sigma_j) > I(p_i^\alpha,\sigma_i)\\ 0&, else \end{cases} b={10,I(pjα,σj)>I(piα,σi),else
取 N 为 60,共有 1770 个点对。考虑其中的 512个短距离点点对,构成 512 位的二进制编码,即 64字节。

BRISK 算法直接生成二进制字符串的特征描述方法,加快了建立特征描述符的速度,也极大的降低了特征描述符的内存占用和特征匹配的时间。

4、特征匹配

使用 Hamming距离进行特征点匹配。

在图像配准测试中的速度比较:SIFT<SURF<BRISK<FREAK<ORB。对较大模糊程度的图像配准,BRISK 算法的性能最好。


6.8.2 OpenCV 中的 BRISK 类

OpenCV 提供了丰富的特征检测算法,而且继承了 cv::Feature2D 类,采用了统一的定义和封装。

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

BriefDescriptorExtractor 类的构造函数有三种形式,分别为:

BRISK::BRISK()
static Ptr< BRISK > create (int thresh=30, int octaves=3, float patternScale=1.0f)
static Ptr< BRISK > create (const std::vector< float > &radiusList, const std::vector< int > &numberList, float dMax=5.85f, float dMin=8.2f, const std::vector< int > &indexChange=std::vector< int >()))
static Ptr< BRISK > create (int thresh, int octaves, const std::vector< float > &radiusList, const std::vector< int > &numberList, float dMax=5.85f, float dMin=8.2f, const std::vector< int > &indexChange=std::vector< int >())

在 Python 语言中,OpenCV 提供了接口函数 cv.BRISK.create(), 实例化 BRISK 类。

cv.BRISK.create([, thresh=30, octaves=3, patternScale=1.0f]) → retval
cv.BRISK.create(radiusList, numberList[, dMax=5.85f, dMin=8.2f, indexChange=std::vector<int>()]) → retval

cv.BRISK.create(thresh, octaves, radiusList, numberList[, dMax=5.85f, dMin=8.2f, indexChange=std::vector<int>()]) → retval
cv.BRISK_create([, thresh=30, octaves=3, patternScale=1.0f]	) → retval

cv.BRISK_create(radiusList, numberList[, dMax=5.85f, dMin=8.2f, indexChange=std::vector<int>()]) → retval
cv.BRISK_create(thresh, octaves, radiusList, numberList[, dMax=5.85f, dMin=8.2f, indexChange=std::vector<int>()]) → retval

brisk.compute(image, keypoints[, descriptors=None]) → keypoints, descriptors

参数说明:

  • image:输入图像,单通道

  • thresh:AGGST 检测阈值

  • octaves:倍频程,0 表示单一尺度

  • patternScale:模式比例,用于对关键点的邻域进行采样

  • radiusList:关键点周围的采样半径(单位为像素,比例为 1)

  • numberList:定义圆周上的采样点数,长度与 radiusList 相同

  • dMax:短配对阈值(单位为像素,比例为 1)

  • dMin:长配对阈值(单位为像素,比例为 1)

  • indexChange:位的重映射的索引

  • keypoints :检测到的关键点,是一个特殊的数据结构

  • descriptors:关键点的描述符,Numpy 数组,形状为(n, bytes)


例程 14.27:特征检测之 BRISK 算子

    # 14.27 特征检测之 BRISK 算子
    # 读取基准图像
    imgRef = cv.imread("../images/Circuit04.png", flags=1)  # (480, 600, 3)
    refer = cv.cvtColor(imgRef, cv.COLOR_BGR2GRAY)  # 基准图像
    height, width = imgRef.shape[:2]  # 图片的高度和宽度
    print("shape of image: ", height, width)
    # 读取或构造目标图像
    top, left = int(0.1*height), int(0.1*width)
    border = cv.copyMakeBorder(imgRef, top, top, left, top, borderType=cv.BORDER_CONSTANT, value=(32,32,32))
    zoom = cv.resize(border, (width, height), interpolation=cv.INTER_AREA)
    theta= 15  # 顺时针旋转角度,单位为角度
    x0, y0 = width//2, height//2  # 以图像中心作为旋转中心
    MAR = cv.getRotationMatrix2D((x0,y0), theta, 1.0)
    imgObj = cv.warpAffine(zoom, MAR, (width, height))  # 旋转变换,默认为黑色填充
    # imgObj = cv.imread("../images/Circuit04B.png", flags=1)  # (480, 600, 3)
    object = cv.cvtColor(imgObj, cv.COLOR_BGR2GRAY)  # 目标图像
    print("shape of image: ", imgObj.shape)

    # 构造 BRISK 对象,检测关键点,计算特征描述向量
    brisk = cv.BRISK_create()  # 创建 BRISK 检测器
    kpRef, desRef = brisk.detectAndCompute(refer, None)  # 基准图像关键点检测
    kpObj, desObj = brisk.detectAndCompute(object, None)  # 目标图像关键点检测
    imgRefBrisk = cv.drawKeypoints(imgRef, kpRef, None, flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)  # 绘制关键点大小和方向
    imgObjBrisk = cv.drawKeypoints(imgObj, kpObj, None, flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)  # 绘制关键点大小和方向
    print(desRef.shape, desObj.shape)

    # 特征点匹配,Brute-force matcher
    matcher = cv.BFMatcher()  # 构造 BFmatcher 对象
    matches = matcher.match(desRef, desObj)  # 对描述子 des1, des2 进行匹配
    matches = sorted(matches, key=lambda x: x.distance)
    matches1 = cv.drawMatches(imgRef, kpRef, imgObj, kpObj, matches[:100], None, flags=2)
    print('queryIdx=%d' % matches[0].queryIdx)
    print('trainIdx=%d' % matches[0].trainIdx)
    print('distance=%d' % matches[0].distance)
    print("bf.match:{}".format(len(matches)))

    plt.figure(figsize=(9, 6))
    ax1 = plt.subplot(212)
    ax1.set_title("BRISK detector & BFMatcher")
    plt.imshow(cv.cvtColor(matches1, cv.COLOR_BGR2RGB)), plt.axis('off')
    ax2 = plt.subplot(221)
    plt.axis('off'), plt.imshow(cv.cvtColor(imgRefBrisk, cv.COLOR_BGR2RGB))
    ax2.set_title("BRISK keypoints (Ref)")
    ax3 = plt.subplot(222)
    plt.axis('off'), plt.imshow(cv.cvtColor(imgObjBrisk, cv.COLOR_BGR2RGB))
    ax3.set_title("BRISK keypoints (Obj)")
    plt.tight_layout()
    plt.show()

【OpenCV 例程 300篇】245. 特征检测之 BRISK 算子



参考文献:Stefan Leutenegger, Margarita Chli, and Roland Yves Siegwart. Brisk: Binary robust invariant scalable keypoints. In Computer Vision (ICCV), 2011 IEEE International Conference on, pages 2548–2555. IEEE, 2011.

【OpenCV 例程 300篇】245. 特征检测之 BRISK 算子

【本节完】

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

241. 尺度不变特征变换(SIFT)
242. 加速稳健特征检测算法(SURF)
243. 特征检测之 FAST 算法