角点检测:Harris 与 Shi-Tomasi

角点检测:Harris 与 Shi-Tomasi

目录:

  1. 图像特征类型
  2. Harris 角点检测器
  3. Shi-Tomasi 角点检测器
  4. OpenCV 代码实现
    1. Harris 角点检测
    2. Shi-Tomasi 角点检测
  5. 参考


一、图像特征类型

我们都玩过拼图游戏,将大量混乱的小片段进行正确地排列,形成一个完整的图像。

玩这个游戏的关键点是什么?就是寻找那些区分度比较高的特征片段,它们更容易用于定位。

那这些有效特征到底是什么?

回答这个问题之前,先看一下下面这张图片:


在图像的顶部,给出了它的六个图像片段。我们试试如何在原始图像中找到这些图像片段的确切位置。

如果你亲自试过的话,可以发现:

  • A和B处于平坦区域,没有什么确切的特征,它们所在的位置有很多种可能;
  • C和D要相对简单一些,它们是建筑物的边缘,我们可以找到一个大致的位置,但是要定位到精确的位置仍然很难。所以边缘是更好的特征,但还不够好。
  • E和F是建筑的一些角落,可以很容易地发现它们的位置,因为对于建筑物角落这个图像片段,我们不管朝哪个方向移动,这个片段看起来都会不一样。

简化一下,就可以得到这张图:


  • 蓝色矩形表示一个平坦区域,在各方向移动,窗口内像素值没有变化;
  • 黑色矩形表示一个边缘特征(Edges),如果沿着垂直方向移动(梯度方向),像素值会发生改变;如果沿着边缘移动(平行于边缘) ,像素值不会发生变化;
  • 对于红色矩形框来说,它是一个角(Corners),不管你把它朝哪个方向移动,像素值都会发生很大变化。

三种情况分别如下图所示:


图像特征提供了图像丰富的信息。角点特征是图像中较好的特征,比边缘特征更好地用于定位。

对于拼图游戏,我们已经知道了什么是好的特征,但是下一个问题出现了,如何找到这些角点特征。

找到这些图像特征的过程被称为特征提取,特征提取决定了最终目标识别效果的好坏。

在图像的所有区域中,那些在所有方向上做微小移动,像素值变化都很大的区域,就是角点特征所在区域。

检测角点的应用主要有:图像对齐、图像拼接(拍摄全景图)、目标识别、3D重建、运动跟踪等等。


二、Harris 角点检测器

在图像处理中,检测角点特征的算法有很多,这里先介绍最常用的,也是最基础的 Harris 角点检测器(Harris Corner Detection)。

角点是两条边缘的交点,它表示两条边方向改变的地方,所以角点在任意一个方向上做微小移动,都会引起该区域的梯度图的方向和幅值发生很大变化。

也就是一阶导数(即灰度图的梯度)中的局部最大所对应的像素点就是角点。

于是我们可以利用这一点来检测角点。

使一个固定尺寸的窗口在图像上某个位置以任意方向做微小滑动,如果窗口内的灰度值(在梯度图上)都有较大的变化,那么这个窗口所在区域就存在角点。

这样就可以将 Harris 角点检测算法分为以下三步:

  1. 当窗口(小的图像片段)同时向 x 和 y 两个方向移动时,计算窗口内部的像素值变化量 E(u, v)
  2. 对于每个窗口,都计算其对应的一个角点响应函数 R;
  3. 然后对该函数进行阈值处理,如果 R > threshold,表示该窗口对应一个角点特征。

接下来对每一步进行详细介绍。


2.1、第一步

如何确定哪些窗口会引起较大的灰度值变化?

让一个窗口的中心位于灰度图像的一个位置 (x, y) ,这个位置的像素灰度值为 I(x, y) ,如果这个窗口分别向 x 和 y 方向移动一个小的位移u和v,到一个新的位置 (x+u, y+v) ,这个位置的像素灰度值就是 I(x+u, y+v)

[I(x+u, y+v) - I(x, y)] 就是窗口移动引起的灰度值的变化值。

w(x,y) 为位置 (x,y) 处的窗口函数,表示窗口内各像素的权重,最简单的就是把窗口内所有像素的权重都设为1。

有时也会把 w(x,y) 设定为以窗口中心为原点的高斯分布(二元正态分布)。如果窗口中心点像素是角点,那么窗口移动前后,中心点的灰度值变化非常强烈,所以该点权重系数应该设大一点,表示该点对灰度变化的贡献较大;而离窗口中心(角点)较远的点,这些点的灰度变化比较小,于是将权重系数设小一点,表示该点对灰度变化的贡献较小。

则窗口在各个方向上移动 (u,v) 所造成的像素灰度值的变化量公式如下:

E(u,v) = \sum\limits_{(x,y)} w(x,y) \times [I(x+u, y+v) - I(x,y)]^2 \\

对于一个角点来说, E(u,v) 会非常大。因此,我们可以最大化上面这个函数来得到图像中的角点。

用上面的函数计算 E(u,v) 会非常慢。因此,我们使用泰勒展开式(只有一阶)来得到这个公式的近似形式。

对于二维的泰勒展开式公式为:

T(x,y) \approx f(u,v) + (x-u)f_x(u,v) + (y-v)f_y(u,v) + ... \\

\displaystyle I(u+x,v+y) 套用上面的公式,可以得到:

I(x+u,y+v) \approx I(x,y) + uI_x + vI_y \\

其中 I_xI_yI 的偏微分,在图像中就是在 x 和 y 方向的梯度图(可以通过cv2.Sobel()来得到):

I_x = \frac {\partial I(x,y)}{\partial x},\ \ I_y = \frac {\partial I(x,y)}{\partial y} \\

接下来继续推导:

\begin{aligned} E(u,v) &= \sum\limits_{(x,y)} w(x,y) \times [I(x,y) + uI_x + vI_y - I(x,y)]^2 \\ &= \sum\limits_{(x,y)} w(x,y) \times (uI_x + vI_y)^2 \\ &= \sum\limits_{(x,y)} w(x,y) \times (u^2I_x^2 + v^2I_y^2 + 2uvI_xI_y) \end{aligned} \\

把 u 和 v 拿出来,得到最终的形式:

E(u,v) \approx \begin{bmatrix} u, v \end{bmatrix} M \begin{bmatrix} u \\ v \end{bmatrix} \\

其中矩阵M为:

M = \sum\limits_{(x,y)} w(x,y) \begin{bmatrix} I_x^2 & I_xI_y \\ I_xI_y & I_y^2 \end{bmatrix} \rightarrow R^{-1} \begin{bmatrix} \lambda_1 & 0 \\ 0 & \lambda_2 \end{bmatrix} R \\

最后是把实对称矩阵对角化处理后的结果,可以把R看成旋转因子,其不影响两个正交方向的变化分量。

经对角化处理后,将两个正交方向的变化分量提取出来,就是 λ1 和 λ2(特征值)。

公式推导完了,现在回顾一下:

对于图像的每一个像素点 (x,y) ,对应一个以该像素为中心的窗口 w(x,y) ,然后该像素平移 (u,y) 得到新的像素点 (x+u, y+v) ,而 E(u,v) 就是窗口中所有像素的加权和乘以不同位置像素的灰度差值


2.2、第二步

现在我们已经得到 E(u,v) 的最终形式,别忘了我们的目的是要找到会引起较大的灰度值变化的那些窗口。

灰度值变化的大小则取决于矩阵M,那么如何找到这些窗口,我们可以使用矩阵的特征值来实现。

计算每个窗口对应的得分(角点响应函数R):

R = \text {det}(M) - k (\text {trace(M)})^2 \\

其中 \text {det}(M) = \lambda_1 \lambda_2 是矩阵的行列式, \text {trace(M)} = \lambda_1 + \lambda_2 是矩阵的迹。

λ1 和 λ2 是矩阵M的特征值, k 是一个经验常数,在范围 (0.04, 0.06) 之间。


2.3、第三步

根据 R 的值,将这个窗口所在的区域划分为平面、边缘或角点。为了得到最优的角点,我们还可以使用非极大值抑制

注意:Harris 检测器具有旋转不变性,但不具有尺度不变性,也就是说尺度变化可能会导致角点变为边缘,如下图所示:

(插播一句,想要尺度不变特性的话,可以关注SIFT特征)

因为特征值 λ1 和 λ2 决定了 R 的值,所以我们可以用特征值来决定一个窗口是平面、边缘还是角点:

  • 平面: 该窗口在平坦区域上滑动,窗口内的灰度值基本不会发生变化,所以 |R| 值非常小,在水平和竖直方向的变化量均较小,即 I_xI_y 都较小,那么 λ1 和 λ2 都较小;
  • 边缘: R 值为负数,仅在水平或竖直方向有较大的变化量,即 I_xI_y 只有一个较大,也就是 λ1>>λ2 或 λ2>>λ1;
  • 角点: R 值很大,在水平、竖直两个方向上变化均较大的点,即 I_xI_y 都较大,也就是 λ1 和 λ2 都很大

用图片表示如下:

Harris 角点检测的结果是带有这些分数 R 的灰度图像,设定一个阈值,分数大于这个阈值的像素就对应角点。


三、Shi-Tomasi 角点检测器

知道了什么是 Harris 角点检测,后来有大佬在论文《Good_Features_to_Track》中提出了它的改进版——Shi-Tomasi 角点检测,Shi-Tomasi 方法在很多情况下可以得到比 Harris 算法更好的结果。


Harris 角点检测中每个窗口的分数公式是将矩阵 M 的行列式与 M 的迹相减:

R = λ_1λ_2 − k (λ_1 + λ_2)^2 \\

由于 Harris 角点检测算法的稳定性和 k 值有关,而 k 是个经验值,不好设定最佳值。

Shi-Tomasi 发现,角点的稳定性其实和矩阵 M 的较小特征值有关,于是直接用较小的那个特征值作为分数。这样就不用调整k值了。

所以 Shi-Tomasi 将分数公式改为如下形式:

R = min (λ_1, λ_2) \\

和 Harris 一样,如果该分数大于设定的阈值,我们就认为它是一个角点。

我们可以把它绘制到 λ1 ~ λ2 空间中,就会得到下图:


四、OpenCV 代码实现

4.1 Harris 角点检测

在opencv中有提供实现 Harris 角点检测的函数 cv2.cornerHarris,我们直接调用的就可以,非常方便。

函数原型:cv2.cornerHarris(src, blockSize, ksize, k[, dst[, borderType]])

对于每一个像素 (x,y),在 (blockSize x blockSize) 邻域内,计算梯度图的协方差矩阵 M(x,y) ,然后通过上面第二步中的角点响应函数得到结果图。图像中的角点可以为该结果图的局部最大值。

即可以得到输出图中的局部最大值,这些值就对应图像中的角点。

涉及到几个参数:

  • src - 输入灰度图像,float32类型
  • blockSize - 用于角点检测的邻域大小,就是上面提到的窗口的尺寸
  • ksize - 用于计算梯度图的Sobel算子的尺寸
  • k - 用于计算角点响应函数的参数k,取值范围常在0.04~0.06之间
import cv2 as cv
import numpy as np

# detector parameters
block_size = 5
sobel_size = 3
k = 0.04

image = cv2.imread('bird.jpg')
gray_img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# modify the data type setting to 32-bit floating point 
gray_img = np.float32(gray_img)

# detect the corners with appropriate values as input parameters
corners_img = cv2.cornerHarris(gray_img, block_size, sobel_size, k)

# result is dilated for marking the corners, not necessary
dst = cv.dilate(corners_img, None)

# Threshold for an optimal value, marking the corners in Green
image[corners_img>0.01*corners_img.max()] = [0,0,255]

cv2.imwrite('new_bird.jpg', image)


4.2、Shi-Tomasi 角点检测

OpenCV 提供了 Shi-Tomasi 的函数: cv2.goodFeaturesToTrack(),来获取图像中前 N 个最好的角点。

函数原型如下:

goodFeaturesToTrack(image, maxCorners, qualityLevel, minDistance[, corners[, mask[, blockSize[, useHarrisDetector[, k]]]]])

其中的参数如下:

  • image:输入灰度图像,float32类型
  • maxCorners:返回角点的最大数目,值为0表表示没有设置最大值限制,返回所有检测到的角点。
  • qualityLevel:质量系数(小于1.0的正数,一般在0.01-0.1之间),表示可接受角点的最低质量水平。该系数乘以最好的角点分数(也就是上面较小的那个特征值),作为可接受的最小分数;例如,如果最好的角点分数值为1500且质量系数为0.01,那么所有质量分数小于15的角都将被忽略。
  • minDistance:角之间最小欧式距离,忽略小于此距离的点。
  • corners:输出角点坐标
  • mask:可选的感兴趣区域,指定想要检测角点的区域。
  • blockSize:默认为3,角点检测的邻域大小(窗口尺寸)
  • useHarrisDetector:用于指定角点检测的方法,如果是true则使用Harris角点检测,false则使用Shi Tomasi算法。默认为False。
  • k:默认为0.04,Harris角点检测时使用。


设定好这些参数,函数就能在图像上找到角点。所有低于质量水平的角点都会被忽略,然后再把合格角点按角点质量进行降序排列。

然后保留质量最高的一个角点,将它附近(最小距离之内)的角点都删掉(类似于非极大值抑制),按这样的方式最后得到 N 个最佳角点。

import numpy as np
import cv2

maxCorners = 100
qualityLevel = 0.01
minDistance = 10

img = cv2.imread('bird.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
corners = cv2.goodFeaturesToTrack(gray, maxCorners, qualityLevel, minDistance)

corners = np.int0(corners)
for i in corners:
    x,y = i.ravel()
    cv2.circle(img,(x,y),2,(0,0,255),-1)
    
cv2.imwrite('new_bird.jpg', img)


最后得到的结果如下:

三张图分别为原图,harris图和Shi-Tomasi图


Harris 和 Shi-Tomasi 都是基于梯度计算的角点检测方法,Shi-Tomasi 的效果要好一些。基于梯度的检测方法有一些缺点: 计算复杂度高,图像中的噪声可以阻碍梯度计算。

想要提高检测速度的话,可以考虑基于模板的方法:FAST角点检测算法。该算法原理比较简单,但实时性很强。

有兴趣的同学可以去了解一下。


参考:

blog.csdn.net/lwzkiller

zhuanlan.zhihu.com/p/67

docs.opencv.org/master/

Harris Corner Detection and Shi-Tomasi Corner Detection

The Shi-Tomasi Corner Detector



如果觉得有用,点个赞吧(ง •̀_•́)ง。

编辑于 2019-10-09 10:47

文章被以下专栏收录