在拼图游戏中,我们一般从有明显特征的碎片入手,因为区分度比较高的特征片段提供了图像丰富的信息,更容易用于定位。在计算机视觉和图像处理中,使用计算机提取图像信息,决定每个图像的点是否属于一个图像特征称为特征点检测。
特征点检测广泛应用于目标匹配、目标跟踪、三维重建等应用中,在进行目标建模时会对图像进行目标特征的提取,常用的有颜色、角点、特征点、轮廓、纹理等特征。角点特征是图像中较好的特征,比边缘特征更好地用于定位。
在现实世界中,角点对应于物体的拐角,道路的十字路口、丁字路口等。从图像分析的角度来定义角点可以有以下两种定义:
人眼对角点的识别通常是在一个局部的小区域或小窗口完成的。
“灰度/像素值发生很大变化”这一现象可以用图像梯度进行描述。在图像局部范围内,图像梯度越大表示该局部内像素值变化越大(灰度的变化率越大)。图像的梯度在数学上可用微分或者导数来表示。对于数字图像来说,相当于是二维离散函数求梯度,并使用差分来近似导数: G x ( x , y ) = H ( x + 1 , y ) − H ( x − 1 , y ) G_x(x,y)=H(x+1,y)-H(x-1,y) Gx(x,y)=H(x+1,y)−H(x−1,y) G y ( x , y ) = H ( x , y + 1 ) − H ( x , y − 1 ) G_y(x,y)=H(x,y+1)-H(x,y-1) Gy(x,y)=H(x,y+1)−H(x,y−1) 在实际操作中,对图像求梯度通常是考虑图像的每个像素的某个邻域内的灰度变化,因此通常对原始图像中像素某个邻域设置梯度算子,然后采用小区域模板进行卷积来计算,常用的有Prewitt算子、Sobel算子、Robinson算子、Laplace算子等。
Harris角点检测是一种常用的、较为基础的特征点检测方法,可用于判断角点、边缘、平滑区域。Harris角点检测原理是利用移动的窗口在图像中计算灰度变化值,其中关键流程包括转化为灰度图像、计算差分图像、高斯平滑、计算局部极值、确认角点。
通过建立数学模型,判断哪些窗口会引起较大的灰度值变化。
让一个窗口的中心位于灰度图像位置 ( x , y ) (x,y) (x,y),将该处像素灰度值记为 I ( x , y ) I(x,y) I(x,y) ,如果让该窗口在点 ( x , y ) (x,y) (x,y)处平移 ( Δ x , Δ y ) (\Delta x,\Delta y) (Δx,Δy)到新的位置 ( u , v ) (u,v) (u,v) ,新位置处的像素灰度值就是 I ( u , v ) I(u,v) I(u,v) 。 ∣ I ( u , v ) − I ( x , y ) ∣ |I(u,v)-I(x,y)| ∣I(u,v)−I(x,y)∣就是窗口移动引起的灰度值的变化值。
设 w ( x , y ) w(x,y) w(x,y)为 ( x , y ) (x,y) (x,y)处的窗口函数,其表示窗口内各像素的权重,为了简化模型可以把窗口内所有像素的权重都设为1,即一个均值滤波核。也可以把 w ( x , y ) w(x,y) w(x,y)设定为以窗口中心为原点的高斯分布,即一个高斯核。如果窗口中心点像素是角点,那么窗口移动前后,中心点的灰度值变化非常大,所以该点权重系数应该设大一点;离窗口中心(角点)较远的点灰度变化比较小,将权重系数设小一点。
对于图像 I ( x , y ) I(x,y) I(x,y),当中心位于点 ( x , y ) (x,y) (x,y)处的窗口向各个方向平移 ( Δ x , Δ y ) (\Delta x,\Delta y) (Δx,Δy)时,为描述像素灰度值的变化,引入 E ( x , y ) E(x,y) E(x,y):
E ( x , y ; Δ x , Δ y ) = ∑ ( x , y ) w ( x , y ) × [ I ( x + Δ x , y + Δ y ) – I ( x , y ) ] 2 E(x,y;\Delta x,\Delta y) = \sum_{(x,y)}w(x,y) \times [I(x+\Delta x,y+\Delta y) – I(x,y)]^2 E(x,y;Δx,Δy)=(x,y)∑w(x,y)×[I(x+Δx,y+Δy)–I(x,y)]2
其中, w ( x , y ) w(x,y) w(x,y)是以点 ( x , y ) (x,y) (x,y)为中心的窗口,为加权函数,它既可是常数,也可以是高斯加权函数。
若窗口内是一个角点,则上式中平方项会很大,相应地, E ( x , y ) E(x,y) E(x,y)的计算结果将会很大。
根据泰勒展开,对图像 I ( x , y ) I(x,y) I(x,y)在平移 ( Δ x , Δ y ) (\Delta x,\Delta y) (Δx,Δy)后进行一阶近似:
I ( x + Δ x , y + Δ y ) = I ( x , y ) + I x ( x , y ) Δ x + I y ( x , y ) Δ y + O ( Δ x 2 , Δ y 2 ) ≈ I ( x , y ) + I x ( x , y ) Δ x + I y ( x , y ) Δ y \begin{aligned} I(x+\Delta x,y+\Delta y) &= I(x,y)+I_x(x,y)\Delta x+I_y(x,y)\Delta y+O(\Delta x^2,\Delta y^2)\\ &\approx I(x,y)+I_x(x,y)\Delta x+I_y(x,y)\Delta y \end{aligned} I(x+Δx,y+Δy)=I(x,y)+Ix(x,y)Δx+Iy(x,y)Δy+O(Δx2,Δy2)≈I(x,y)+Ix(x,y)Δx+Iy(x,y)Δy
其中, I x , I y I_x,I_y Ix,Iy是 I ( x , y ) I(x,y) I(x,y)的偏导,在图像中就是求 x x x 和 y y y 方向的梯度图:
I x = ∂ I ( x , y ) ∂ x , I y = ∂ I ( x , y ) ∂ y I_x=\frac{\partial I(x,y)}{\partial x} ,\quad I_y=\frac{\partial I(x,y)}{\partial y} Ix=∂x∂I(x,y),Iy=∂y∂I(x,y)
∴ E ( x , y ) = ∑ ( x , y ) w ( x , y ) × [ I ( x , y ) + I x Δ x + I y Δ x − I ( x , y ) ] 2 \therefore E(x, y)=\sum_{{{\scriptsize (x, y)} } } w(x, y)\times[ I(x, y) + I_{\scriptsize x} \Delta x+ I_{\scriptsize y} \Delta x- I(x,y) ] ^ {\scriptsize 2} ∴E(x,y)=(x,y)∑w(x,y)×[I(x,y)+IxΔx+IyΔx−I(x,y)]2进一步化简得到:
E ( x , y ; Δ x , Δ y ) ≈ ∑ ( x , y ) w ( x , y ) [ I x ( x , y ) Δ x + I y ( x , y ) Δ y ] 2 = [ Δ x , Δ y ] M ( x , y ) [ Δ x Δ y ] E(x,y;\Delta x,\Delta y)\approx \sum_{{{\scriptsize (x, y)} } }w(x, y)[I_x(x,y)\Delta x+I_y(x,y)\Delta y]^2=[\Delta x,\Delta y]M(x,y)\begin{bmatrix}\Delta x \\ \Delta y\end{bmatrix} E(x,y;Δx,Δy)≈(x,y)∑w(x,y)[Ix(x,y)Δx+Iy(x,y)Δy]2=[Δx,Δy]M(x,y)[ΔxΔy]
其中
M ( x , y ) = ∑ ( x , y ) [ 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 ] = [ ∑ ( x , y ) I x ( x , y ) 2 ∑ ( x , y ) I x ( x , y ) I y ( x , y ) ∑ ( x , y ) I x ( x , y ) I y ( x , y ) ∑ ( x , y ) I y ( x , y ) 2 ] \begin{aligned} M(x,y)&=\sum_{{{\scriptsize (x, y)} } } \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_{{{\scriptsize (x, y)} } } I_x(x,y)^2&\sum_{{{\scriptsize (x, y)} } } I_x(x,y)I_y(x,y) \\\sum_{{{\scriptsize (x, y)} } } I_x(x,y)I_y(x,y)&\sum_{{{\scriptsize (x, y)} } } I_y(x,y)^2\end{bmatrix} \end{aligned} M(x,y)=(x,y)∑[Ix(x,y)2Ix(x,y)Iy(x,y)Ix(x,y)Iy(x,y)Iy(x,y)2]=[∑(x,y)Ix(x,y)2∑(x,y)Ix(x,y)Iy(x,y)∑(x,y)Ix(x,y)Iy(x,y)∑(x,y)Iy(x,y)2]
经过上述步骤,我们得到了一个实对称对角矩阵 M M M,将其进一步对角化:
M = R − 1 [ λ 1 0 0 λ 2 ] R M=R^{-1} \begin{bmatrix} \lambda_{\scriptsize{1}} & 0 \\ 0 & \lambda_{\scriptsize{2}} \end{bmatrix} R M=R−1[λ100λ2]R
其中, R R R 为角点响应函数,通过判定其大小来判断像素是否为角点; λ 1 , λ 2 \lambda_{\scriptsize{1}},\lambda_{\scriptsize{2}} λ1,λ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 ) = λ 1 λ 2 det(M)=\lambda_1\lambda_2 det(M)=λ1λ2是矩阵 M M M 的行列式, t r a c e ( M ) = λ 1 + λ 2 trace(M)=\lambda_1+\lambda_2 trace(M)=λ1+λ2 是矩阵 M M M 的迹, k k k是一个经验常数,通常取值在0.04~0.06之间。
因为特征值 λ 1 \lambda_1 λ1 和 λ 2 \lambda_2 λ2 决定了 R R R 的值,所以我们可以用特征值来决定一个窗口是平坦区域、边缘还是角点:
为了得到最优的角点,我们使用非极大值抑制。应当注意的是,Harris 检测器具有旋转不变性,但不具有尺度不变性,也就是说尺度变化可能会导致角点变为边缘(P.S.:SIFT特征具有尺度不变性)。
注记:实对称矩阵一定可以对角化
定理:设 A A A 为 n n n 阶实对称矩阵,则必有正交矩阵 P P P,使 P − 1 A P = B P^{-1}AP = B P−1AP=B,其中 B B B 是以 A A A 的 n n n 个特征值为主对角线元素的对角矩阵。
在OpenCV中有提供实现Harris角点检测的函数 cv2.cornerHarris
,我们可以直接调用。
函数原型:cv2.cornerHarris(src, blockSize, ksize, k[, dst[, borderType]])
对于每一个像素 ( x , y ) (x,y) (x,y),在 ( b l o c k S i z e × b l o c k S i z e ) (blockSize \times blockSize) (blockSize×blockSize) 邻域内,计算梯度图的协方差矩阵 M ( x , y ) M(x,y) M(x,y),然后通过上述角点响应函数得到结果图,输出图中的局部最大值对应图像中的角点。
参数解释:
利用Anaconda搭建OpenCV环境,运行以下代码(Python):
# -*- coding: utf-8 -*-
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('building_SDU.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, cv.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 = cv.getStructuringElement(cv.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():
cv.circle(image, (c, r), 5, (0, 0, 255), 0)
image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
plt.imshow(image)
plt.show()
处理前:
增大 k k k的值,将减小角点响应值 R R R,降低角点检测的灵敏性,减少被检测角点的数量;减小 k k k值,将增大角点响应值 R R R,增加角点检测的灵敏性,增加被检测角点的数量。
在进行Harris角点检测时,使用了微分算子对图像进行微分运算,而微分运算对图像密度的拉升或收缩和对亮度的抬高或下降不敏感。换言之,对亮度和对比度的仿射变换并不改变Harris响应的极值点出现的位置,但是阈值的选择,可能会影响角点检测的数量。
Harris角点检测算子使用的是角点附近的区域灰度二阶矩阵,二阶矩阵可以表示成一个椭圆,椭圆的长短轴是二阶矩阵特征值平方根的倒数。当特征椭圆转动时,特征值并不发生变化,所以判断角点响应值 R R R也不发生变化,由此说明Harris角点检测算子具有旋转不变性。
尺度的变化可能将角点变为边缘,或者边缘变为角点,Harris的理论基础并不具有尺度不变性。如下图所示,左侧的图像可能被检测为边缘或曲线,而当左图被缩小时,在检测窗口尺寸不变的前提下,右侧的图像可能被检测为一个角点。
上述Harris角点具有光照不变性、旋转不变性、尺度不变性,但是严格意义上来说并不具备仿射不变性。而Harris-Affine是一种新颖的检测仿射不变特征点的方法,可以处理明显的仿射变换,包括大尺度变化和明显的视角变化。
深入了解Harris-Affine角点检测,请移步原论文: Scale & Affine Invariant Interest Point Detectors