一、Shi-Tomasi⻆点检测原理
Shi-Tomasi 角点检测算法是一种改进的角点检测算法,它在 Harris 角点检测算法的基础上进行了优化。它的原理如下:
1.首先,对于给定的图像,需要将其转换为灰度图像。
2.对于每个像素点(x, y),计算其在 x 和 y 方向上的梯度(通常使用 Sobel 算子),得到梯度的幅值(A)和梯度的方向(B)。
3.对于每个像素点(x, y),计算一个自相关矩阵M,定义为:
M
=
∑
(
w
(
x
,
y
)
∗
[
I
x
2
,
I
x
∗
I
y
;
I
x
∗
I
y
,
I
y
2
]
)
M = \sum (w(x, y) * [Ix^2, Ix * Iy; Ix * Iy, Iy^2])
M=∑(w(x,y)∗[Ix2,Ix∗Iy;Ix∗Iy,Iy2])
4.其中,w(x, y) 是一个窗口函数,用于加权计算局部区域的贡献。常用的窗口函数有矩形窗口或高斯窗口。
5.计算自相关矩阵M的特征值λ1和λ2。
6.使用特征值来计算角点响应度,这里使用的是Shi-Tomasi响应函数:
R = min(λ1, λ2)
Shi-Tomasi 角点检测算法通过选择最小特征值作为响应度度量,相较于 Harris 角点检测算法的 R = λ1 * λ2 - k * (λ1 + λ2)^2,Shi-Tomasi 在计算角点响应度时更加稳定。这也是相对于Harris角点检测的最大改进
7.对所有像素点的角点响应度进行排序,选取排名前N的角点作为最终的角点结果。
二、代码的分步骤实现
import cv2
import numpy as np
# 1. 读取图像并转换为灰度图像
img = cv2.imread('test_1.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 2. 计算图像中每个像素点的梯度值
gradient_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
gradient_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
# 3. 计算梯度的幅值平方
gradient_sqr = gradient_x ** 2 + gradient_y ** 2
# 4. 构建自相关矩阵M,并计算特征值
window_size = 3
k = 0.04 # Harris响应函数参数
corner_response = np.zeros_like(gray, dtype=np.float32)
for i in range(window_size, gray.shape[0] - window_size):
for j in range(window_size, gray.shape[1] - window_size):
M = np.zeros((2, 2), dtype=np.float32)
for u in range(-window_size, window_size + 1):
for v in range(-window_size, window_size + 1):
dx = gradient_x[i + u, j + v]
dy = gradient_y[i + u, j + v]
M[0, 0] += dx * dx
M[0, 1] += dx * dy
M[1, 0] += dx * dy
M[1, 1] += dy * dy
# 计算特征值
eigenvalues, _ = np.linalg.eig(M)
# 5. 使用特征值计算角点响应度,选择最小特征值作为响应度度量
corner_response[i, j] = np.min(eigenvalues)
# 6. 应用非极大值抑制
neighborhood_size = 7 # 非极大值抑制邻域大小
max_corners = 1000 # 最大角点数量
threshold = 0.01 # 响应度阈值,根据实际情况设置
corners = []
for i in range(window_size, gray.shape[0] - window_size):
for j in range(window_size, gray.shape[1] - window_size):
# 判断角点的响应度是否大于阈值并且是局部最大值
if corner_response[i, j] > threshold and \
corner_response[i, j] == np.max(corner_response[max(0, i - neighborhood_size): min(i + neighborhood_size + 1, gray.shape[0]),
max(0, j - neighborhood_size): min(j + neighborhood_size + 1, gray.shape[1])]):
corners.append((j, i)) # 存储角点的坐标
# 根据角点响应度排序
corners.sort(key=lambda c: corner_response[c[1], c[0]], reverse=True)
corners = corners[:max_corners]
# 7. 绘制角点结果
for corner in corners:
x, y = corner
cv2.circle(img, (x, y), 3, (0, 255, 0), -1)
# 8. 显示角点结果
cv2.imshow('Shi-Tomasi Corners', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出结果: