角是图像中各个方向上强度变化很大的区域,Harris等人做了一些找到这些角的尝试,并变成如下数学形式:
E ( u , v ) = ∑ x , y w ( x , y ) ⏟ window function [ I ( x + u , y + v ) ⏟ shifted intensity − I ( x , y ) ⏟ intensity ] 2 . E(u,v) = \sum_{x,y} \underbrace{w(x,y)}_\text{window function} \, [\underbrace{I(x+u,y+v)} _\text{shifted intensity}-\underbrace{I(x,y)}_\text{intensity}]^2. E(u,v)=x,y∑window function w(x,y)[shifted intensity I(x+u,y+v)−intensity I(x,y)]2. 以上函数基本找到了 ( u , v ) (u, v) (u,v)在所有方向上位移的强度差异。角检测时,需要最大化上式,其泰勒展开如下:
E ( u , v ) ≈ [ u v ] M [ u v ] , E(u,v) \approx \begin{bmatrix} u & v \end{bmatrix} M \begin{bmatrix} u \\ v \end{bmatrix}, E(u,v)≈[uv]M[uv],其中
M = ∑ x , y w ( x , y ) [ I x I x I x I y I x I y I y I y ] , M = \sum_{x,y} w(x,y) \begin{bmatrix}I_x I_x & I_x I_y \\ I_x I_y & I_y I_y \end{bmatrix}, M=x,y∑w(x,y)[IxIxIxIyIxIyIyIy],其中 I x , I y I_x, I_y Ix,Iy是 x x x和 y y y方向上的图像导数,可以使用cv.Sobel() 找到。
接下来创建一个等式,用于判断一个窗口是否可以包含一个角:
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 ; t r a c e ( M ) = λ 1 + λ 2 , det(M)=\lambda_1\lambda_2;\\ trace(M)=\lambda_1+\lambda_2, det(M)=λ1λ2;trace(M)=λ1+λ2,其中 λ 1 \lambda_1 λ1和 λ 2 \lambda_2 λ2是 M M M的特征值。
因此,这些特征值决定了给定区域是拐角、边缘还是平坦:
1)当 ∣ R ∣ |R| ∣R∣较小时,平坦;
2) R < 0 R<0 R<0,边;
3) R R R很大,角。
最终,哈夫角检测的结果就是具有 R R R值的灰度图像。
# coding: utf-8
import cv2 as cv
import numpy as np
def test(file_name):
""""""
img = cv.imread(file_name)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
gray = np.float32(gray)
# 输入参数包括灰度图、检测时的邻域大小、sobel导数的光圈参数、检测器自由参数
dst = cv.cornerHarris(gray, 3, 3, 0.1)
dst = cv.dilate(dst, None)
img[dst > 0.01 * dst.max()] = [0, 0, 255]
cv.imshow("", img)
cv.waitKey()
if __name__ == '__main__':
file_name = "shudu.png"
test(file_name)
找到最精确的角落:
# coding: utf-8
import cv2 as cv
import numpy as np
def test(file_name):
""""""
img = cv.imread(file_name)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
gray = np.float32(gray)
# 输入参数包括灰度图、检测时的邻域大小、sobel导数的光圈参数、检测器自由参数
dst = cv.cornerHarris(gray, 3, 3, 0.1)
dst = cv.dilate(dst, None)
_, dst = cv.threshold(dst, 0.01 * dst.max(), 255, 0)
dst = np.uint8(dst)
# 寻找质心
ret, labels, stats, centroids = cv.connectedComponentsWithStats(dst)
# 定义停止和完善拐角的条件
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 100, 0.001)
corners = cv.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]
cv.imshow("", img)
cv.waitKey()
if __name__ == '__main__':
file_name = "shudu.png"
test(file_name)