一、引言:关于兴趣点(interest points)
在图像处理和计算机视觉领域,兴趣点(interest points),或称做关键点(keypoints)、特征点(feature points)被大量用于解决物体识别,图像识别、图像匹配、视觉跟踪、三维重建等一系列问题。我们不再观察整幅图,而是选择某些特殊的点,然后对它们进行局部有的放矢的分析。如果能检测到足够多的点,同时它们的区分度很高,并且可以精确定位稳定的特征,那么这个方法就有使用价值。
图像特征类型可以被分为以下三种:
1、边缘
2、角点(感兴趣关键点)
3、斑点(Blobs)(感兴趣区域)
其中,角点是个很特殊的存在。它们在图像中可以轻易定位,同时它们在人造物体中场景中,如门窗,桌子等中随处可见。因为角点位于两条边缘的交点处,代表了两个边缘变化的方向上的点,所以它们是可以精确定位的二维特征,甚至可以达到亚像素的精度。且其图像梯度有很高的变化,这种变化是可以用来帮助检测角点的。需要注意的是,角点与位于相同强度区域上的点不同,与物体轮廓上的点也不同,因为轮廓点难以在相同的其他物体上精确定位。
二、角点检测算法的分类
在当前的图像处理领域,角点检测算法可以归纳为三类:
1、基于灰度图像的角点检测
2、基于二值图像的角点检测
3、基于轮廓曲线的角点检测
而基于灰度图像的角点检测又可分为基于梯度、基于模板、基于模板梯度组合 三类方法,其中基于模板的方法主要考虑像素领域点的灰度变化,即图像亮度的变化,将与邻点亮度对比足够大的点定义为角点。常见的基于模板的角点检测算法有Kitchen-Rosenfeld角点检测算法,Harris角点检测算法,KLT角点检测算法以及SUSAN角点检测算法。和其他角点检测算法相比,SUSAN角点检测算法具有算法简单、位置准确、抗噪声能力强等特点。
三、角点的定义
‘如果某一点在任意方向的一个微小变动都会引起灰度很大的变化,那么我们就把它称之为角点’
关于角点的具体描述可以有几种:
1、一阶导数(即灰度的梯度)的局部最大所对应的像素点
2、两条及两条以上边缘的点
3、图像中的梯度值和梯度方向的变化速率都很高的点
4、角点处的一阶导数最大,二阶导数为0,指示物体边缘变化不连续的方向
四、Harris角点检测原理
对于角点的检测,Harris依据以下直观判断
角点:窗口移动在水平、竖直两个方向上变化均较大的点,即Ix, Iy都比较
边界:仅在水平、或者仅在竖直方向有较大的变化量,
另一个没有什么变化, 即Ix和Iy中只有一个比较大
平坦地区:在水平、竖直方向的变化量比较小。即Ix,Iy都比较小
将图像窗口平移[u, v]产生灰度变化E(u, v)
其中,w(x, y)是窗口函数,向量[u, v]
表示某个方向,以及在该方向上的位移。由上述公式可知,E(u,v)表示某个方向上图像灰度的变化。角点检测中要是E(u, v)的值最大,就是说让方程右侧的第二项的取值最大。
由
,得到:
记上式最后的结果为△,则我们可以得到:
其中M是2x2矩阵可由图像的导数求得:
这里Ix和Iy是图像在x和y方向的导数。他们根据一个用来判定窗口内是否包含角点的等式进行打分。
所以根据这些特征我们可以判断一个区域是否是角点,边界或者平面:
当λ1和
λ2都小时,|R|也小,这个区域就是一个平坦区域;
当λ1>>λ2 或者λ1<<λ2,R小于0,这个区域是边缘
当
λ1和
λ2都很大是,R也很大(λ1和λ2中的最小值都大于阈值),说明这个区域是角点。
用下面的图来表示我们的结论:
所以Harris角点检测的结果是一个由角点分数构成的灰度图像。选取适当的阈值对结果图像进行二值化我们就检测到图像中的角点。
五、函数及代码
OpenCV中的函数cv2.cornerHarris()可以用来进行角点检测。
cornerHarris(src, blockSize, ksize, k, dst=None, borderType=None)
src: 输入图像,单通道8位或float32
blockSize: 角点检测中要考虑的邻域大小
ksize:Sobel求导中使用的窗口大小
k: Harris角点检测方程中的自由参数,取值参数为[0.04, 0.06]
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('img/blox.jpg')
# img = cv.resize(img, (int(img.shape[1]*0.2), int(img.shape[0]*0.2)))
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
cv.imshow('gray', gray)
gray = np.float32(gray)
#输入图像必须是单通道, 最后一个参数的取值范围在[0.04, 0.06]
dst = cv.cornerHarris(gray, 2, 3, 0.04)
cv.imshow('harris', dst)
#膨胀操作,让检测到的角点更加清晰
dst = cv.dilate(dst, None)
cv.imshow('dilate', dst)
# 阈值的最佳值
img[dst > 0.01*dst.max()] = [0, 0, 255]
cv.namedWindow('img',cv.WINDOW_AUTOSIZE)
cv.imshow('img',img)
cv.waitKey(0)
cv.destroyAllWindows()
效果:
参考文章:
https://blog.csdn.net/qq_37059483/article/details/77836239
https://blog.csdn.net/crzy_sparrow/article/details/7391511
https://blog.csdn.net/poem_qianmo/article/details/29356187
https://blog.csdn.net/xiaowei_cqu/article/details/7805206
https://blog.csdn.net/u010103202/article/details/73250003