上一章节中,我们看到在图像中每个方向变化都很大的区域就是角点,一个早期的尝试是由 Chris Harris & Mike Stephens 在1998年的论文 A Combined Corner and Edge Detector 完成的。所以现在称之为 Harris 角点检测。他讲这些思想转换为数学公式形式,通过寻找不同方向上位移的亮度差异。
E(u,v)=∑x,yw(x,y)[I(x+u,y+v)−I(x,y)]2E(u,v)=\sum\limits_{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)w(x,y)w(x,y) 是窗口函数,可以是矩形窗口,也可以是高斯窗口,对应像素不同的权重。
I(x+u,y+v)I(x+u,y+v)I(x+u,y+v) 是移动区域的亮度,
I(x,y)I(x,y)I(x,y) 是亮度函数;
我们需要最大化函数 E(u,v)E(u,v)E(u,v)进行角点检测,这就需要我们最大化 [I(x+u,y+v)−I(x,y)]2[I(x+u,y+v)-I(x,y)]^2[I(x+u,y+v)−I(x,y)]2,将泰勒展开应用于上述方程,并使用一些数学步骤(请参考您喜欢的任何标准教科书以获得完整推导),我们得到最终方程为:
E(u,v)≈[uv]M[uv]E(u,v)\approx[u \quad v] M \begin{bmatrix} u \\ v \end{bmatrix} E(u,v)≈[uv]M[uv]
其中
M=∑x,yw(x,y)[IxIxIxIyIyIxIyIy]M=\sum\limits_{x,y}w(x,y)\begin{bmatrix} I_xI_x & I_xI_y \\ I_yI_x & I_yI_y \end{bmatrix} M=x,y∑w(x,y)[IxIxIyIxIxIyIyIy]
其中 IxI_xIx 和 IyI_yIy 是图像在 xxx 和 yyy 方向上的梯度(可以通过 sobel 获得);
然后来到了主要部分,创建了一个分数,基本就是一个等式,确定了窗口是否包含了角点与否。
R=det(M)−k(trace(M))2R=det(M)-k(trace(M))^2 R=det(M)−k(trace(M))2
其中:
因此,这些特征值的大小决定了这个区域是角点,边还是平坦区域。



import cv2
import numpy as np # 读入图片并灰度化
# img = cv2.imread("assets/blox.jpg")
img = cv2.imread("assets/left08.jpg")
# img = cv2.imread("assets/chessboard(1).png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 检测角点
gray = np.float32(gray)
dst = cv2.cornerHarris(gray, 2, 3, 0.04)# 膨胀
dst = cv2.dilate(dst, None)# 找到计算值比较大的,并显示
img[dst>0.01*dst.max()] = [0, 0, 255]cv2.imshow('dst', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

import numpy as np
import cv2# 读入图片并灰度化
img = cv2.imread("assets/left08.jpg")
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)# 寻找角点
gray = np.float32(gray)
dst = cv2.cornerHarris(gray, 2, 3, 0.04)# 阈值化找到角点
dst = cv2.dilate(dst,None)# 阈值化
ret, dst = cv2.threshold(dst, 0.01*dst.max(), 255, 0)
dst = np.uint8(dst)# 找到连通域中心
ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst)# 寻找亚像素角点
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
corners = cv2.cornerSubPix(gray, np.float32(centroids), (5,5), (-1,-1), criteria)# 显示
res = np.hstack((centroids, corners))
res = np.int0(res)
img[res[:,1],res[:,0]]=[0,0,255]
img[res[:,3],res[:,2]] = [0,255,0]cv2.imshow("res", img)
cv2.imshow("dst", dst)cv2.waitKey(0)
cv2.destroyAllWindows()cv2.imwrite('subpixel5.png', img)
cv2.cornerHarris( src, blockSize, ksize, k[, dst[, borderType]] ) -> dst

Harris 角点检测
- src: 单通道8位图像或者浮点图像
- dst: 存储 harris 角点检测响应, CV_FC1 大小与源图像一致;
- blocksize: 领域大小
- ksize: sobel算子的直径
- k: harris角点检测的自由参数里的 kkk
- borderType: 扩边参数,BORDER_WRAP 不支持

cv2.cornerSubPix( image, corners, winSize, zeroZone, criteria ) -> corners
优化角点位置
- image: 输入的单通道图像,8位或者浮点数
- corners: 初始化的角点位置,并且可以接受输出
- winSize: 搜寻窗口的半径,如果设置为 (5, 5),则搜索区域为 (52+1, 52+1) =(11,11)的窗口;
- zeroZone: (-1,-1) 表示没有这个尺寸
- criteria: 迭代准则,要么次数达到了,要么移动的距离很小了。
cv.connectedComponentsWithStats( image[, labels[, stats[, centroids[, connectivity[, ltype]]]]] ) -> retval, labels, stats, centroids
cv.connectedComponentsWithStatsWithAlgorithm( image, connectivity, ltype, ccltype[, labels[, stats[, centroids]]] ) -> retval, labels, stats, centroids
计算二值图像连通区域,并返回一系列的统计值
- image: 8位单通道需要标记的图像
- labels: 输出的标签图像
- stats: 统计每一个标签的输出,包含背景标签
- centroids: 每个区域的中心,可以通过 centroids(label, 0) 访问 x, centroids(label, 1) 访问 y;
- connectivity: 4邻域或8邻域
- ltype: 输出标签类型 CV_32S 或者 CV_16U
- ccltype: 连通区域类型

下一篇:无模型深度强化学习算法