哈里斯角检测
目标
在本章中, - 我们将了解”Harris Corner Detection”背后的概念。 - 我们将看到以下函数:cv.cornerHarris(),cv.cornerSubPix()
理论
在上一章中,我们看到角是图像中各个方向上强度变化很大的区域。Chris Harris**和**Mike Stephens**在1988年的论文《**组合式拐角和边缘检测器》中做了一次尝试找到这些拐角的尝试,所以现在将其称为哈里斯拐角检测器 Harris Corner Detector.。他把这个简单的想法变成了数学形式。它基本上找到了(u,v)在所有方向上位移的强度差异。表示如下:
窗口函数要么是一个矩形窗口,要么是高斯窗口,它在下面赋予了值。
我们必须最大化这个函数E(u,v)用于角检测。这意味着,我们必须最大化第二个项。将泰勒扩展应用于上述方程,并使用一些数学步骤(请参考任何你喜欢的标准文本书),我们得到最后的等式:
其中
在此,IxIx和IyIy分别是在xI和y方向上的图像导数。(可以使用**cv.Sobel**()轻松找到)。
然后是主要部分。之后,他们创建了一个分数,基本上是一个等式,它将确定一个窗口是否可以包含一个角。
其中
特征值的乘积等于对应方阵行列式的值,和等于对应方阵对角线元素之和。
因此,这些特征值的值决定了区域是拐角,边缘还是平坦。
可以用如下图来表示:
因此,Harris Corner Detection的结果是具有这些分数的灰度图像。合适的阈值可为您提供图像的各个角落。我们将以一个简单的图像来完成它。
OpenCV中的哈里斯角检测
为此,OpenCV具有函数**cv.cornerHarris()。其参数为:
- **img - 输入图像,应为灰度和float32类型。
- blockSize - 是拐角检测考虑的邻域大小
- ksize - 使用的Sobel导数的光圈参数。 -
k - 等式中的哈里斯检测器自由参数。
请参阅以下示例:
import numpy as np
import cv2 as cv
filename = 'chessboard.png'
img = cv.imread(filename)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
gray = np.float32(gray)#必须为灰度以及float32类型
dst = cv.cornerHarris(gray,2,3,0.04)
#result用于标记角点,并不重要
#dilate 扩张
dst = cv.dilate(dst,None)
#最佳值的阈值,它可能因图像而异。
img[dst>0.01*dst.max()]=[0,0,255]
cv.imshow('dst',img)
if cv.waitKey(0) & 0xff == 27:
cv.destroyAllWindows()
结果
SubPixel精度的转角
有时,你可能需要找到最精确的角落。OpenCV附带了一个函数**cv.cornerSubPix**(),它进一步细化了以亚像素精度检测到的角落。下面是一个例子。和往常一样,我们需要先找到哈里斯角。然后我们通过这些角的质心(可能在一个角上有一堆像素,我们取它们的质心)来细化它们。Harris角用红色像素标记,精制角用绿色像素标记。对于这个函数,我们必须定义何时停止迭代的条件。我们在特定的迭代次数或达到一定的精度后停止它,无论先发生什么。我们还需要定义它将搜索角落的邻居的大小。
import numpy as np
import cv2 as cv
filename = 'chessboard.png'
img = cv.imread(filename)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 寻找哈里斯角
gray = np.float32(gray)
dst = cv.cornerHarris(gray,2,3,0.04)
dst = cv.dilate(dst,None)
ret, dst = cv.threshold(dst,0.01*dst.max(),255,0)
dst = np.uint8(dst)
# 寻找质心
'''该函数输入为一个二值化图像,输出为一个长为4的tuple,第一个是连通区域的个数,第二个是
一整张图的label,第三个是(x, y, width, height, area),
即每个区域的每个区域的左上角坐标,宽和高,面积。第四个是每个连通区域的中心点。
component 组成部分 stats/statistics 统计学 centroid质心,面心
criteria 标准,原则'''
ret, labels, stats, centroids = cv.connectedComponentsWithStats(dst)
# 定义停止和完善拐角的条件
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 100, 0.001)
''' void cv::cornerSubPix(
cv::InputArray image, // 输入图像
cv::InputOutputArray corners, // 角点(既作为输入也作为输出)
cv::Size winSize, // 区域大小为 NXN; N=(winSize*2+1)
cv::Size zeroZone, // 类似于winSize,但是总具有较小的范围,Size(-1,-1)表示忽略
cv::TermCriteria criteria // 停止优化的标准
);
第一个参数是输入图像,和cv::goodFeaturesToTrack()中的输入图像是同一个图像。
第二个参数是检测到的角点,即是输入也是输出。
第三个参数是计算亚像素角点时考虑的区域的大小,大小为NXN; N=(winSize*2+1)。
第四个参数作用类似于winSize,但是总是具有较小的范围,通常忽略(即Size(-1, -1))。
第五个参数用于表示计算亚像素时停止迭代的标准,可选的值有cv.TERM_CRITERIA_EPS 、cv.TERM_CRITERIA_MAX_ITER(可以是两者其一,或两者均选),
前者表示迭代次数达到了最大次数时停止,后者表示角点位置变化的最小值已经达到最小时停止迭代。二者均使用TermCriteria()构造函数进行指定。'''
corners = cv.cornerSubPix(gray,np.float32(centroids),(5,5),(-1,-1),criteria)
# 绘制
'''np.vstack():在竖直方向上堆叠 vertical
np.hstack():在水平方向上平铺 horizontal
'''
res = np.hstack((centroids,corners))
res = np.int0(res)#别名int64
img[res[:,1],res[:,0]]=[0,0,255]
img[res[:,3],res[:,2]] = [0,255,0]
cv.imshow('subpixel5.png',img)
cv.waitKey(0)