欲速则不达,哈里斯角点检测是一个不简单也不复杂的概念方法,但是需要了解很多前置概念,一步步来。但是想要一步到位,也可以直接跳过第一部分的前置概念理解部分。
哈里斯角点检测 Harris Corner Detection
前置概念
数字图像概念
什么是数字图像概念? 这是我最喜欢的SUV,沃尔沃XC90,在我们眼中,我们都看出这是一个非常漂亮的汽车的图片,但是在计算机的识别后,是什么?
在计算机图像中,该图像是一个数组,其中包含元素数量为 192010803,其中3为三层通道Channel;而通过对图像属性的对比,发现图像的分辨率也为1920*1080。说明经过数字化处理,图像的每个像素在计算机中都被转化为一个值。
角点
什么是角点? 角点说白了就是物体边缘的拐点。通过对角点的识别,我们可以实现很多功能,比如: 实现图片的拼接: 三维重建: 等等…
哈里斯角点原理
通过对比下述三张图,理解哈里斯对于角点的定义。
首先,我们选择一个合适大小的像素框,如途中金黄色区域。我们试图通过对框中像素值的变化去探究是否为角点。 第一个 flat,我们在上下左右以及对角线移动像素框时,会发现框中的值几乎没有变化,全部为黑色值0; 第二个 edge,我们在上下移动时,发现像素框中值没有变化,但是左右移动时以及对角线移动时会发生变化; 第三个 corner,我们不论上下移动还是左右移动还是对角线移动,都会发生变化,所以其为角点。
将上述哈里斯角点原理化为公式来看:
E
(
u
,
v
)
=
∑
x
,
y
w
(
x
,
y
)
[
I
(
x
+
u
,
y
+
v
)
?
I
(
x
,
y
)
]
2
E(u,v)=\sum _{x,y}w(x,y) [I(x+u,y+v)-I(x,y)]^2
E(u,v)=x,y∑?w(x,y)[I(x+u,y+v)?I(x,y)]2 其中,
u
,
v
u,v
u,v 分别代表着在竖直和水平方向上的偏移,
w
(
x
,
y
)
w(x,y)
w(x,y) 为像素框的中心;
I
(
x
+
u
,
y
+
v
)
I(x+u,y+v)
I(x+u,y+v) 为像素框位移之后坐标加偏移
(
x
+
u
,
y
+
v
)
(x+u,y+v)
(x+u,y+v) 的灰度值,
I
(
x
,
y
)
I(x,y)
I(x,y) 为位移之前坐标位置
(
x
,
y
)
(x,y)
(x,y) 的灰度值。
其结果:E代表的就是 “平移前选定的红框中每个像素” 与 “平移后选定的绿框的每个像素” 对应位置的差的平方和。
泰勒展开
泰勒展开将一些复杂的函数逼近近似的表示为简单的多项式函数。
f
(
x
)
=
f
(
x
0
)
+
f
′
(
x
0
)
(
x
?
x
0
)
+
f
′
′
(
x
0
)
2
(
x
?
x
0
)
2
+
.
.
.
+
f
(
n
)
(
x
0
)
n
!
(
x
?
x
0
)
n
+
o
[
(
x
?
x
0
)
n
]
f(x)=f(x_0)+f'(x_0)(x-x_0)+\frac {f''(x_0)} 2(x-x_0)^2+...+\frac {f^{(n)}(x_0)} {n!}(x-x_0)^n+o[(x-x_0)^n]
f(x)=f(x0?)+f′(x0?)(x?x0?)+2f′′(x0?)?(x?x0?)2+...+n!f(n)(x0?)?(x?x0?)n+o[(x?x0?)n]
上述泰勒公式使用的余项是皮亚诺余项。
通过上图理解,通过不断的进行泰勒展开,不断逼近原函数曲线;我们可以通过泰勒公式来获取函数的信息。
哈里斯角点检测与泰勒公式
通过一阶泰勒公式
f
(
x
)
=
f
(
x
0
)
+
f
′
(
x
0
)
(
x
?
x
0
)
f(x)=f(x_0)+f'(x_0)(x-x_0)
f(x)=f(x0?)+f′(x0?)(x?x0?),我们可以将哈里斯角点检测灰度值函数
I
(
x
+
u
,
y
+
v
)
I(x+u,y+v)
I(x+u,y+v) 公式化为:
I
(
x
+
u
,
y
+
v
)
≈
I
(
x
,
y
)
+
I
x
u
+
I
y
v
I(x+u,y+v)≈I(x,y)+I_xu+I_yv
I(x+u,y+v)≈I(x,y)+Ix?u+Iy?v
其中
I
x
u
=
d
I
d
x
u
;
I
y
v
=
d
I
d
y
v
I_xu=\frac {dI} {dx} u;I_yv=\frac {dI} {dy} v
Ix?u=dxdI?u;Iy?v=dydI?v
所以有:
E
(
u
,
v
)
=
∑
x
,
y
w
(
x
,
y
)
[
I
(
x
+
u
,
y
+
v
)
?
I
(
x
,
y
)
]
2
E(u,v)=\sum _{x,y} w(x,y)[I(x+u,y+v)-I(x,y)]^2
E(u,v)=∑x,y?w(x,y)[I(x+u,y+v)?I(x,y)]2
≈
w
(
x
,
y
)
∑
x
,
y
[
I
(
x
,
y
)
+
I
x
u
+
I
y
v
?
I
(
x
,
y
)
]
2
≈w(x,y)\sum _{x,y} [I(x,y)+I_xu+I_yv-I(x,y)]^2
≈w(x,y)∑x,y?[I(x,y)+Ix?u+Iy?v?I(x,y)]2
=
w
(
x
,
y
)
∑
x
,
y
[
I
x
u
+
I
y
v
]
2
=w(x,y)\sum _{x,y} [I_xu+I_yv]^2
=w(x,y)∑x,y?[Ix?u+Iy?v]2
=
w
(
x
,
y
)
∑
x
,
y
(
I
x
2
u
2
+
2
I
x
I
y
u
v
+
I
y
2
v
2
)
=w(x,y)\sum _{x,y} (I_x^2u^2+2I_xI_yuv+I_y^2v^2)
=w(x,y)∑x,y?(Ix2?u2+2Ix?Iy?uv+Iy2?v2)
而又因为
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) 表示为:
E
(
u
,
v
)
=
[
u
2
∑
x
,
y
I
x
2
+
2
u
v
∑
x
,
y
I
x
I
y
+
v
2
∑
x
,
y
I
y
2
]
E(u,v)=[u^2 \sum_{x,y} I_x^2 + 2uv\sum_{x,y} I_xI_y +v^2\sum_{x,y} I_y^2]
E(u,v)=[u2∑x,y?Ix2?+2uv∑x,y?Ix?Iy?+v2∑x,y?Iy2?]
转换成矩阵形式: 推导如下: 下一步,我们需要对
I
x
I_x
Ix? 与
I
y
I_y
Iy? 的意义进行探究。
探究
I
x
I_x
Ix? 与
I
y
I_y
Iy? 值的意义
首先,上述已经说明,
I
x
=
d
I
d
x
I_x=\frac {dI} {dx}
Ix?=dxdI?,
I
y
=
d
I
d
y
I_y=\frac {dI} {dy}
Iy?=dydI?,
I
(
x
,
y
)
I(x,y)
I(x,y) 代表着
(
x
,
y
)
(x, y)
(x,y) 点的像素值。
其次,我们有求 平移前后对应像素差平方值的函数
E
(
u
,
v
)
E(u,v)
E(u,v) 我们可以将该函数表示为: 即: 所以我们下面将对矩阵
M
M
M 进行研究:
对于每一个边缘上的点而言,当
d
I
d
x
\frac {dI} {dx}
dxdI? 有意义时,
d
I
d
y
\frac {dI} {dy}
dydI? 值很小约为0;当
d
I
d
y
\frac {dI} {dy}
dydI? 有意义时,
d
I
d
x
\frac {dI} {dx}
dxdI? 值很小约为0。当然除了角点。如图所示:
所以一共存在三种情况的点:
- 不在边界的
d
I
d
x
=
d
I
d
y
≈
0
\frac {dI} {dx}=\frac {dI} {dy}≈0
dxdI?=dydI?≈0 的点
- 在边界的
d
I
d
x
>
>
0
,
d
I
d
y
≈
0
\frac {dI} {dx}>>0,\frac {dI} {dy}≈0
dxdI?>>0,dydI?≈0 或
d
I
d
y
>
>
0
,
d
I
d
x
≈
0
\frac {dI} {dy}>>0,\frac {dI} {dx}≈0
dydI?>>0,dxdI?≈0 的点;
- 角点
d
I
d
x
>
>
0
,
d
I
d
y
>
>
0
\frac {dI} {dx}>>0,\frac {dI} {dy}>>0
dxdI?>>0,dydI?>>0
如果向完全理解
d
I
d
x
\frac {dI} {dx}
dxdI? 与
d
I
d
y
\frac {dI} {dy}
dydI?,需要结合 索贝尔 (Sobel) 算子 进行理解。
索贝尔算子 Sobel
因为本节博客主要介绍哈里斯角点检测,所以这里只简单介绍一下索贝尔算子。
首先,明确索贝尔算子的作用为 边缘检测。 索贝尔有两个算子,一个是检测水平边缘;另一个是检测垂直边缘;
e
.
g
.
e.g.
e.g. 假设我们想要检测原始图像
A
A
A 的横向边缘以及纵向边缘,
G
x
G_x
Gx? 为横向索贝尔算子,
G
y
G_y
Gy? 为纵向索贝尔算子。
e
.
g
.
e.g.
e.g. 案例:对比下面三个原始图片的索贝尔算子结果:
代码:
import cv2
cv2.namedWindow('video', cv2.WINDOW_NORMAL)
cv2.resizeWindow('video', 640, 480)
img = cv2.imread("Sobel_0.png")
sobel_x = cv2.Sobel(img[:, :, 0], cv2.CV_16S, 1, 0)
sobel_y = cv2.Sobel(img[:, :, 0], cv2.CV_16S, 0, 1)
absX = cv2.convertScaleAbs(sobel_x)
absY = cv2.convertScaleAbs(sobel_y)
dst = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
cv2.imshow("video", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
原始图片: Sobel_0.png;Sobel_1.png;Sobel_2.png 对三个图片分别做横向边缘检测:
对三个图片分别做纵向边缘检测:
对三个图片分别做横向+纵向边缘检测: 以上便是Sobel算子的基本实现。
回到哈里斯,结合索贝尔,继续探究
I
x
I_x
Ix? 与
I
y
I_y
Iy?:
构建三种情况下梯度统计图与梯度图:
梯度统计图:
I
x
I_x
Ix? 为水平方向的梯度值,
I
y
I_y
Iy? 为竖直方向的梯度值。 很明显,平坦点几乎没有什么梯度变化,周围没有什么像素的变化,值趋近于0; 而边缘点,只有一个方向有梯度变化,上上图中区域2的变化是在水平移动有像素值有变化,竖直方向移动,像素值几乎没有变化; 而角点,两个方向的移动都会有梯度的变化,都有像素值的大量改变。
梯度图: 平坦点的移动几乎没有任何像素值的变化; 边缘点的移动会在一个方向有大量像素值的变化,而另一个方向如同平坦点一样; 角点会在两个方向都有大量像素值的变化。
通过上述加强了对:角点与边缘和平坦的理解,下面将回归公式,从对角点响应大小判定的方案去判定是否为角点。
角点响应
角点响应公式:
R
=
d
e
t
(
M
)
?
k
(
t
r
a
c
e
(
M
)
)
2
R=det(M)-k(trace(M))^2
R=det(M)?k(trace(M))2 其中:
d
e
t
(
M
)
det(M)
det(M) 为求矩阵M的行列式的值,
d
e
t
(
M
)
=
I
x
2
?
I
y
2
?
I
x
I
y
?
I
x
I
y
det(M)=I_x^2*I_y^2-I_xI_y*I_xI_y
det(M)=Ix2??Iy2??Ix?Iy??Ix?Iy?;
k
k
k 称为经验值,一般为
0.04
0.04
0.04~
0.06
0.06
0.06;
t
r
a
c
e
(
M
)
trace(M)
trace(M) 为矩阵对角线的和,
t
r
a
c
e
(
M
)
=
I
x
2
+
I
y
2
trace(M)=I_x^2+I_y^2
trace(M)=Ix2?+Iy2?
通过
R
R
R 的值,来判断是角点的强度,也可以说来判断是角点的真实性。
前置知识到此结束,下面将通过代码案例实际进行一张图片的角点检测。
哈里斯角点检测一般流程
1、彩色图像转化为灰阶图像; 2、计算空间微分(泰勒展开); 3、建构结构张量(Sobel算子); 4、计算哈里斯响应(角点响应); 5、非极大值抑制(筛选点)。
哈里斯角点检测python代码
import cv2
import numpy as np
'''
image: 源图片;
blocksize:窗口大小;
ksize:索贝尔梯度计算的Kernel大小;
k:角点响应R的经验值系数。
'''
def cornerHarris(image, blocksize=2, ksize=3, k=0.04):
def _clacHarris(cov,k):
result = np.zeros([cov.shape[0], cov.shape[1]], dtype=np.float32)
for i in range(cov.shape[0]):
for j in range(cov.shape[1]):
a = cov[i, j, 0]
b = cov[i, j, 1]
c = cov[i, j, 2]
result[i, j] = a * c - b * b - k * (a + c) * (a + c)
return result
sobel_x = cv2.Sobel(image, cv2.CV_32F, 1, 0, ksize=ksize)
sobel_y = cv2.Sobel(image, cv2.CV_32F, 0, 1, ksize=ksize)
cov = np.zeros([image.shape[0], image.shape[1], 3], dtype=np.float32)
for i in range(image.shape[0]):
for j in range(image.shape[1]):
cov[i,j,0] = sobel_x[i,j] * sobel_x[i,j]
cov[i,j,1] = sobel_x[i,j] * sobel_y[i,j]
cov[i,j,2] = sobel_y[i,j] * sobel_y[i,j]
cov = cv2.boxFilter(cov, -1, (blocksize, blocksize), normalize=False)
return _clacHarris(cov,k)
if __name__ == '__main__':
img = cv2.imread("harris_detector.jpg")
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
result = cornerHarris(gray_img, 2, 3, 0.04)
pos = cv2.goodFeaturesToTrack(result, 0, 0.01, 10)
for i in range(len(pos)):
cv2.circle(img, (int(pos[i][0][0]), int(pos[i][0][1])), 1, [255,0,0], thickness=2)
cv2.imshow('harris',img)
cv2.waitKey(0)
角点检测结果
Final:哈里斯角点检测小结
哈里斯角点检测,主要用于用图像中找出代表角点的特征点。
角点是图像中最重要的特征,基本上角点的特性不会受到旋转、平移以及图像亮度的影响。所以虽然角点只是一张图像中很小的一部分,但是通常却代表着一张图像中最重要的特征。
2022年11月1日 HK理工大学 包玉刚图书馆
|