OpenCV 图像特征提取
Harris 角点检测
1、什么是角点
同角点并列的还有边界点、平面点。看下面的图可以看出来三者之间的位置关系。
从上面的图片我们可以看出来:
- 平面:A, B
- 边界:C, D
- 角点:E, F
简化一点就是下面三种情况:
2、如何区分角点、边界和平面
我们使用肉眼观察很方便,但是输入到计算机里面就需要相应的算法进行识别。
-
平面: 之所以叫平面,是因为在图像窗口任意移动过程中,像素值不会发生明显的变化,趋于平滑。
-
边界: 当图像窗口在边界范围移动时,像素值会沿一个方向基本不变,在垂直的方向发生巨大变化。可以看中间的图片,当沿 y 轴运动时,像素值不会发生大的变化。当沿 x 轴移动时会发生像素值会发生巨大变化。
-
角点: 不同于平面和边界,图像窗口处于角点位置时,沿任意方向都会发生巨大变化。
利用上面的规则我们就可以区分角点、边界和平面。
3、角点公式推导
我们在实际中主要是观察移动前后像素值的变化情况。
1、 当图像在
(
x
,
y
)
(x, y)
(x,y) 处平移
(
Δ
x
,
Δ
y
)
(\Delta x, \Delta y)
(Δx,Δy) 后的自相似性:
c ( x , y ; Δ x , Δ y ) = ∑ u , v ∈ W ( x , y ) w ( u , v ) ( I ( u , v ) − I ( u + Δ x , v + Δ y ) ) 2 c(x,y;\Delta x,\Delta y) = \sum_{u,v\in W(x,y)}w(u,v)(I(u,v)-I(u+\Delta x, v+\Delta y))^2 c(x,y;Δx,Δy)=u,v∈W(x,y)∑w(u,v)(I(u,v)−I(u+Δx,v+Δy))2
其中 w ( x , y ) w(x,y) w(x,y) 是以点 ( x , y ) (x, y) (x,y) 为中心的窗口,这里既可以取常数,或者是高斯加权的函数(推荐)。
2、 对上面的部分式子进行化简,泰勒展开:
I ( u + Δ x , v + Δ y ) = I ( u , v ) + I x ( u , v ) Δ x + I y ( u , v ) Δ y + O ( Δ x 2 , Δ y 2 ) ≈ I ( u , v ) + I x ( u , v ) Δ x + I y ( u , v ) Δ y I(u+\Delta x, v+\Delta y)=I(u,v)+I_x(u,v)\Delta x + I_y(u,v)\Delta y + O(\Delta x^2, \Delta y^2) \approx I(u,v)+ I_x(u, v)\Delta x + I_y(u,v)\Delta y I(u+Δx,v+Δy)=I(u,v)+Ix(u,v)Δx+Iy(u,v)Δy+O(Δx2,Δy2)≈I(u,v)+Ix(u,v)Δx+Iy(u,v)Δy
其中 I x , I y I_x,I_y Ix,Iy 分别是 x , y x, y x,y 的偏导数。
3、 进一步化简关于 c 的方程,近似可得:
c ( x , y ; Δ x , Δ y ) ≈ ∑ w ( I x ( u , v ) Δ x + I y ( u , v ) Δ y ) 2 = [ Δ x , Δ y ] M ( x , y ) [ Δ x Δ y ] c(x,y;\Delta x,\Delta y) \approx\sum_w (I_x(u, v)\Delta x + I_y(u,v)\Delta y)^2 = [\Delta x , \Delta y]M(x,y)\begin{bmatrix} \Delta x \\ \Delta y \end{bmatrix} c(x,y;Δx,Δy)≈w∑(Ix(u,v)Δx+Iy(u,v)Δy)2=[Δx,Δy]M(x,y)[ΔxΔy]
我们发现上卖弄的式子是一个平方项,我们可以使用二次型对结果进行化简,提取二次型矩阵。
4、二次型矩阵
M
(
x
,
y
)
=
∑
w
[
I
x
(
x
,
y
)
2
I
x
(
x
,
y
)
I
y
(
x
,
y
)
I
x
(
x
,
y
)
I
y
(
x
,
y
)
I
y
(
x
,
y
)
2
]
=
[
∑
w
I
x
(
x
,
y
)
2
∑
w
I
x
(
x
,
y
)
I
y
(
x
,
y
)
∑
w
I
x
(
x
,
y
)
I
y
(
x
,
y
)
∑
w
I
y
(
x
,
y
)
2
]
=
[
A
B
B
C
]
M(x, y) = \sum_w\begin{bmatrix} I_x(x,y)^2 & I_x(x, y)I_y(x, y) \\ I_x(x, y)I_y(x, y) & I_y(x, y)^2 \end{bmatrix} = \begin{bmatrix} \sum_w I_x(x,y)^2 & \sum_w I_x(x,y)I_y(x,y)\\ \sum_w I_x(x,y)I_y(x,y) & \sum_w I_y(x,y)^2 \end{bmatrix}=\begin{bmatrix} A & B \\ B & C \end{bmatrix}
M(x,y)=w∑[Ix(x,y)2Ix(x,y)Iy(x,y)Ix(x,y)Iy(x,y)Iy(x,y)2]=[∑wIx(x,y)2∑wIx(x,y)Iy(x,y)∑wIx(x,y)Iy(x,y)∑wIy(x,y)2]=[ABBC]
化简可得:
c
(
x
,
y
;
Δ
x
,
Δ
y
)
≈
A
Δ
x
2
+
2
C
Δ
x
Δ
y
+
B
Δ
y
2
c(x,y;\Delta x,\Delta y) \approx A\Delta x^2+2C\Delta x \Delta y + B \Delta y^2
c(x,y;Δx,Δy)≈AΔx2+2CΔxΔy+BΔy2
{ A = I x 2 B = I x I y C = I y 2 \begin{cases} A=I_x^2\\ B=I_x I_y\\ C=I_y^2 \end{cases} ⎩⎪⎨⎪⎧A=Ix2B=IxIyC=Iy2
5、 形状估计
我们可以看得出来二次项函数是一个椭圆函数,类似于:
x
2
a
2
+
y
2
b
2
=
c
\frac{x^2}{a^2}+\frac{y^2}{b^2} = c
a2x2+b2y2=c,只不过是一个不过圆心的椭圆函数。
而且椭圆的大小随着
Δ
x
,
Δ
y
\Delta x , \Delta y
Δx,Δy的变化而变化。类似于一个锥体。
我们最后希望检测角点,我们上面提到,它的变化越大,说明是角点的可能性越大。也就是说在单位 Δ x , Δ y \Delta x , \Delta y Δx,Δy变化中,角点所在位置一般就是$A, B, C $较大值的点。实际中我们并不好利用三个值来进行判断是否是边界,平面还是角点。所以我们根据性质来减少自变量。
6、 减少自变量
根据我们之前学的高数,里面有一个定理:
实对称矩阵一定可以对角化
我们的 M M M 矩阵是一个实对称矩阵( M T = M M^T = M MT=M ),那么它一定可以对角化,并且一定存在一组特征值( λ 1 , λ 2 \lambda_1, \lambda_2 λ1,λ2 ),那么就将矩阵对角化。这样自变量由三个变成了两个,我们就方便来区分三者。
从上图我们可以分析出来:
- 平面: λ 1 , λ 2 \lambda_1,\lambda_2 λ1,λ2均小,也就说梯度不怎么变化
- 边界: λ 1 ≫ λ 2 , λ 1 ≪ λ 2 \lambda_1 \gg \lambda_2,\lambda_1 \ll \lambda_2 λ1≫λ2,λ1≪λ2,也就是说梯度只沿一个方向变化快,垂直方向变化慢。
- 角点: λ 1 , λ 2 \lambda_1,\lambda_2 λ1,λ2 均大,也就是沿任意方向变化均快。
7、 角点响应值
上面我们只是分析了依靠
λ
1
,
λ
2
\lambda_1,\lambda_2
λ1,λ2来区分三者,那么需要更加准确的表达式来进行判别。
R
=
d
e
t
M
−
α
(
t
r
a
c
e
M
)
2
R=detM - \alpha(traceM)^2
R=detM−α(traceM)2
其中
d
e
t
M
=
λ
1
λ
2
:
行
列
式
的
值
t
r
a
c
e
M
=
λ
1
+
λ
2
:
迹
detM = \lambda_1\lambda_2:行列式的值 \quad traceM=\lambda_1+\lambda_2:迹
detM=λ1λ2:行列式的值traceM=λ1+λ2:迹
4、OpenCV相关函数
cv2.cornerHarris(img, blockSize, ksize, k)
- img:输入图像。类型为float32
- blockSize:角点检测中指定的区域大小
- ksize:Soble求导中使用窗口的大小
- k:取值为[0.04-0.06]
5、角点检测程序实现
程序实现:
def cv_show(src):
cv2.imshow('',src)
cv2.waitKey(0)
cv2.destroyAllWindows()
img_copy = img.copy()
img = cv2.imread('Pic/check.jpg') # 棋盘图片
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转换为灰度图
dst = cv2.cornerHarris(gray, 2, 3, 0.04) # 角点检测
img[dst>0.0005*dst.max()]=[0,0,255] # 标记角点
res = np.hstack((img_copy, img))
cv_show(res)
结果如下:
最后
更多精彩内容,大家可以转到我的主页:曲怪曲怪的主页
或者关注我的微信公众号:TeaUrn