角点定义
人们普遍认为角点是二维图像亮度变化剧烈的点或图像边缘曲线上曲率极大值的点。这些点在保留图像图形重要特征的同时,可以有效地减少信息的数据量,使其信息的含量很高,有效地提高了计算的速度,有利于图像的可靠匹配,使得实时处理成为可能。其在三维场景重建、运动估计、目标跟踪、目标识别、图像配准与匹配等计算机视觉领域起着非常重要的作用。
角点实例如下,红色点即为一种角点:
角点检测方法
目前的角点检测算法可以说各种各样。一般使用者仅仅要求得到一个准确的角点检测结果或该检测算法易于编程实现,满足实际后续匹配等应用需要。常见的角点检测方法主要有三类:
1、基于灰度图像的角点检测
2、基于二值图像的角点检测
3、基于轮廓曲线的角点检测
而Harris特征点检测就属于灰度图像检测方法的一种,Harris适用于角点数目较多且光源复杂的情况,其对图像序列的角点检测效果很好,对亮度和对比度的变化不敏感(光照不变性),具有旋转不变性,不具有尺度不变性 。
Harris特征点检测算法
1.原理
算法利用局部的窗口在图像上移动,判断窗口移动前与移动后的灰度变化,当窗口往原始位置的各个方向移动的时候其灰度值都和初始位置相比变化剧烈,就可以判断这个点为角点。
2.算法流程
算法实现分为三步:
1.当窗口(局部区域)同时向 x (水平)和 y(垂直) 两个方向移动u,v距离时,计算窗口内部的像素值变化量为
E
(
u
,
v
)
E(u,v)
E(u,v);
w
(
x
,
y
)
w(x,y)
w(x,y)为窗口函数(
n
∗
n
n*n
n∗n矩阵,其内部元素为图像区域对应位置的权重值),
(
x
,
y
)
(x,y)
(x,y)为窗口中心点坐标值,
(
x
+
v
,
y
+
u
)
(x+v,y+u)
(x+v,y+u)为移动后窗口中心新的位置坐标,
I
(
x
+
v
,
y
+
u
)
I(x+v,y+u)
I(x+v,y+u)表示窗口在新位置的像素值(以
(
x
+
u
,
y
+
v
)
(x+u,y+v)
(x+u,y+v)为中心的矩阵,内部元素为该区域的像素值),
[
I
(
x
+
u
,
y
+
v
)
−
I
(
x
,
y
)
]
2
[I(x+u,y+v)-I(x,y)]^2
[I(x+u,y+v)−I(x,y)]2得到像素差值矩阵,然后与
w
(
x
,
y
)
w(x,y)
w(x,y)做卷积得到
E
(
u
,
v
)
E(u,v)
E(u,v)。
为了提高计算效率,对
E
(
u
,
v
)
E(u,v)
E(u,v)进行简化,利用泰勒级数展开来得到这个公式的近似形式:
对于二维的泰勒展开式公式为:
T
(
x
,
y
)
=
f
(
u
,
v
)
+
(
x
−
u
)
f
x
(
u
,
v
)
+
(
y
−
v
)
f
y
(
u
,
v
)
+
.
.
.
.
T(x,y)=f(u,v)+(x-u)f_x(u,v)+(y-v)f_y(u,v)+....
T(x,y)=f(u,v)+(x−u)fx(u,v)+(y−v)fy(u,v)+....
则
I
(
x
+
u
,
y
+
v
)
I(x+u,y+v)
I(x+u,y+v) 为:
I
(
x
+
u
,
y
+
v
)
=
I
(
x
,
y
)
+
u
I
x
+
v
I
y
I(x+u,y+v)=I(x,y)+uI_x+vI_y
I(x+u,y+v)=I(x,y)+uIx+vIy
其中 I x I_x Ix和 I y I_y Iy是 I I I的微分(偏导),在图像中就是求 x x x 和 y y y 方向的梯度图:
I x = ∂ I ( x , y ) ∂ x I_x=\frac{\partial I(x,y)}{\partial x} Ix=∂x∂I(x,y)
I y = ∂ I ( x , y ) ∂ y I_y=\frac{\partial I(x,y)}{\partial y} Iy=∂y∂I(x,y)
将
I
(
x
+
u
,
y
+
v
)
=
I
(
x
,
y
)
+
u
I
x
+
v
I
y
I(x+u,y+v)=I(x,y)+uI_x+vI_y
I(x+u,y+v)=I(x,y)+uIx+vIy代入
E
(
u
,
v
)
E(u,v)
E(u,v)可得:
提出 u 和 v ,得到最终的近似形式:
其中矩阵M为:
最后是把实对称矩阵对角化处理后的结果,可以把R看成旋转因子,其不影响两个正交方向的变化分量。经对角化处理后,将两个正交方向的变化分量提取出来,就是 λ1 和 λ2(特征值)。
2.计算每个窗口的角点响应函数
R
R
R;
现在我们已经得到
E
(
u
,
v
)
E(u,v)
E(u,v)的最终形式,别忘了我们的目的是要找到会引起较大的灰度值变化的那些窗口。
灰度值变化的大小则取决于矩阵M,M为梯度的协方差矩阵。在实际应用中为了能够应用更好的编程,所以定义了角点响应函数R,通过判定R大小来判断像素是否为角点。
计算每个窗口对应的得分(角点响应函数R定义):
其中
d
e
t
(
M
)
=
λ
1
λ
2
det(M)=\lambda_1\lambda_2
det(M)=λ1λ2是矩阵的行列式,
t
r
a
c
e
(
M
)
=
λ
1
+
λ
2
trace(M)=\lambda_1+\lambda_2
trace(M)=λ1+λ2 是矩阵的迹。
λ
1
λ1
λ1 和
λ
2
λ2
λ2 是矩阵
M
M
M的特征值,
k
k
k是一个经验常数,在范围 (0.04, 0.06) 之间。
R R R的值取决于 M M M的特征值,对于角点 ∣ R ∣ |R| ∣R∣很大,平坦的区域 ∣ R ∣ |R| ∣R∣很小,边缘的 R R R为负值。
3.对角点响应函数与阈值进行比较,若 R > t h r e s h o l d R>threshold R>threshold,则该窗口对应一个角点特征。
根据 R 的值,将这个窗口所在的区域划分为平面、边缘或角点。为了得到最优的角点,我们还可以使用非极大值抑制。
注意:Harris 检测器具有旋转不变性,但不具有尺度不变性,也就是说尺度变化可能会导致角点变为边缘。想要尺度不变特性的话,可以关注SIFT特征。
因为特征值 λ1 和 λ2 决定了 R 的值,所以我们可以用特征值来决定一个窗口是平面、边缘还是角点:
平面::该窗口在平坦区域上滑动,窗口内的灰度值基本不会发生变化,所以 ∣ R ∣ |R| ∣R∣ 值非常小,在水平和竖直方向的变化量均较小,即 I x I_x Ix和 I y I_y Iy都较小,那么 λ1 和 λ2 都较小;
边缘: ∣ R ∣ |R| ∣R∣值为负数,仅在水平或竖直方向有较大的变化量,即 I x I_x Ix和 I y I_y Iy只有一个较大,也就是 λ1>>λ2 或 λ2>>λ1;
角点:[公式] 值很大,在水平、竖直两个方向上变化均较大的点,即 I x I_x Ix和 I y I_y Iy 都较大,也就是 λ1 和 λ2 都很大。
如下图所示:
Harris 角点检测的结果是带有这些分数 R 的灰度图像,设定一个阈值,分数大于这个阈值的像素就对应角点。
基于opencv的python实现
函数介绍
在opencv中有提供实现 Harris 角点检测的函数 cv2.cornerHarris,我们直接调用的就可以,非常方便。
函数原型:cv2.cornerHarris(src, blockSize, ksize, k[, dst[, borderType]])
对于每一个像素 (x,y),在 (blockSize x blockSize) 邻域内,计算梯度图的协方差矩阵 M ( x , y ) M(x,y) M(x,y),然后通过上面第二步中的角点响应函数得到结果图。图像中的角点可以为该结果图的局部最大值。
即可以得到输出图中的局部最大值,这些值就对应图像中的角点。
参数解释:
- src - 输入灰度图像,float32类型
- blockSize - 用于角点检测的邻域大小,就是上面提到的窗口的尺寸
- ksize - 用于计算梯度图的Sobel算子的尺寸
- k - 用于计算角点响应函数的参数k,取值范围常在0.04~0.06之间
代码
import cv2 as cv
from matplotlib import pyplot as plt
import numpy as np
# detector parameters
block_size = 3
sobel_size = 3
k = 0.06
image = cv.imread('D:/LKY/cat.jpg')
print(image.shape)
height = image.shape[0]
width = image.shape[1]
channels = image.shape[2]
print("width: %s height: %s channels: %s"%(width, height, channels))
gray_img = cv.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 = cv.cornerHarris(gray_img, block_size, sobel_size, k)
# result is dilated for marking the corners, not necessary
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3, 3))
dst = cv.dilate(corners_img, kernel)
# Threshold for an optimal value, marking the corners in Green
#image[corners_img>0.01*corners_img.max()] = [0,0,255]
for r in range(height):
for c in range(width):
pix=dst[r,c]
if pix>0.05*dst.max():
cv2.circle(image,(c,r),5,(0,0,255),0)
image = cv.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image)
plt.show()
结果
原图
阈值为0.05,k=0.06
阈值为0.1,k=0.06
阈值为0.05,k=0.05
总结
增大k的值,将减小角点响应值R,降低角点检测的灵敏性,减少被检测角点的数量;减小k值,将增大角点响应值R,增加角点检测的灵敏性,增加被检测角点的数量;阈值越大可检测到的角点也越少。
参考
https://blog.csdn.net/weixin_41923000/article/details/88631944?
https://gitee.com/yuanmingkun/team-learning/blob/master
https://blog.csdn.net/weixin_45720850/article/details