一前言
今天起得太晚,下午又是满课,所以今天的内容会少一点,但是我觉得挺有意思的,也有用处。
二主要内容--特征检测哈里斯角
解释说明
我们大多数人都玩过拼图游戏。我们获得由大量图片碎片,并且需要将他们组合起来形成一张完整的图片。问题来了,我们如何实现呢? 我们如何将相同的理论投射到计算机程序中,以便计算机可以玩拼图游戏?如果计算机可以玩拼图游戏了,为什么我们不能给计算机提供很多真实的自然风景图像,并告诉它将所有这些图像拼接成一个大的单一图像?如果计算机可以将多个自然图像拼接成一个,那么如何提供大量建筑物或任何结构的图片并告诉计算机从中创建 3D 模型呢?
那么,问题和想象力还在继续。但这一切都取决于最基本的问题:你如何玩拼图游戏?你如何将大量的混乱图像片段排列成一个大的单个图像?如何将大量自然图像拼接到单个图像中?
答案是,我们正在寻找独特的特定模式或特定特征,可以轻松跟踪和比较。如果我们找到这样一个特征的定义,我们可能会发现很难用文字表达,但我们知道它们是什么。如果有人要求您指出可以在多个图像之间进行比较的一个好的功能,您可以指出一个。这就是为什么即使是小孩子也可以简单地玩这些游戏。我们在图像中搜索这些特征,找到它们,在其他图像中查找相同的特征并对齐它们。而已。 (在拼图游戏中,我们更多地关注不同图像的连续性)。所有这些能力都存在于我们身上。
因此,我们的一个基本问题扩展到更多,但变得更具体。这些功能是什么?
我们回答了我们的问题,"这些特征是什么?"。但接下来的问题出现了。我们如何找到它们?或者我们如何找到角落?我们以直观的方式回答了这一点,即在图像中寻找在其周围的所有区域中移动(少量)时具有最大变化的区域。在接下来的章节中,这将被投射到计算机语言中。因此,查找这些图像特征称为特征检测。
获得这些功能及其描述后,您可以在所有图像中找到相同的功能并对齐它们,将它们拼接在一起或做任何您想做的事情。
OpenCV 中的 Harris 角点检测器
OpenCV 中的 cv.cornerHarris() 函数用来实现 Harris 角点检测。
dst = cv.cornerHarris(src, blockSize, ksize, k[, dst[, borderType]])
参数说明
| 参数 | 说明 |
|---|---|
src |
输入图像,必须是 单通道8位 或 浮点型 图像 |
blockSize |
邻域大小(用于计算协方差矩阵的窗口大小) |
ksize |
Sobel算子的孔径大小(必须为奇数,如 3, 5, 7) |
k |
Harris检测器的自由参数(经验值 0.04~0.06) |
dst |
输出图像,存储Harris响应值(大小与src相同) |
borderType |
像素外推方法(默认 cv.BORDER_DEFAULT) |
基本原理
Harris角点检测基于以下观察:
-
平坦区域:所有方向移动,图像强度变化小
-
边缘区域:沿边缘方向移动变化小,垂直方向变化大
-
角点区域:所有方向移动,图像强度变化都大
优势应用场景:
-
纹理丰富的图像(棋盘、建筑、织物)
-
刚性物体(机器零件、建筑物)
-
光照变化不大的环境
-
需要精确位置定位的任务
局限性场景:
-
纹理单一的区域(天空、纯色墙壁)
-
非刚性物体(水、云、火焰)
-
严重模糊或低分辨率图像
-
快速运动导致的运动模糊
Harris角点检测作为计算机视觉的基础算法,虽然原理简单,但因其计算效率高 、实现简单 、对旋转不变等特点,仍然是许多实际应用中的首选方法或预处理步骤。
python
import numpy as np
import cv2 as cv
# 读取图像文件
filename = 'D:\python_code\pic\sumoiao.webp'
img = cv.imread(filename)
# 转换为灰度图像并转换为浮点型
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
gray = np.float32(gray)
# 使用Harris角点检测算法
dst = cv.cornerHarris(gray, 2, 3, 0.04)
# 膨胀结果以便更好地标记角点
dst = cv.dilate(dst, None)
# 设置阈值并标记检测到的角点
img[dst > 0.01 * dst.max()] = [0, 0, 255]
# 显示结果图像
cv.imshow('Harris Corner Detection', img)
# 等待按键或ESC退出
if cv.waitKey(0) & 0xff == 27:
cv.destroyAllWindows()
效果如下

这是一个标准的棋盘,可以看到检测的效果很好,那我们换一个试一下

这是一张素描图像,很明显效果并不好,这也是哈里斯算法的局限
所以我将介绍一个更好的算法
具有亚像素级精度的角点
有时,您可能需要以最高精度找到角点。OpenCV 带有一个函数 cv.cornerSubPix() ,它进一步细化了角点检测,以达到亚像素级精度。
什么是亚像素级角点检测?
-
标准角点检测:返回整数像素坐标(如(100, 50))
-
亚像素级检测:返回浮点数坐标(如(100.325, 50.781))
-
精度提升:可达0.1像素甚至更高精度
为什么需要亚像素精度?
-
整数像素坐标存在量化误差
-
许多应用需要毫米级甚至微米级精度
-
提高后续计算的准确性(如3D重建、测量)
代码如下
python
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
# 参数配置
IMAGE_PATH = 'D:/python_code/pic/qipan.jpg' # 使用正斜杠避免转义问题
HARRIS_BLOCK_SIZE = 2
HARRIS_KSIZE = 3
HARRIS_K = 0.04
THRESHOLD_RATIO = 0.01
SUBPIX_WIN_SIZE = (5, 5)
# 图像读取与校验
input_img = cv.imread(IMAGE_PATH)
if input_img is None:
raise FileNotFoundError(f"图像文件读取失败: {IMAGE_PATH}")
# 灰度转换
gray_img = cv.cvtColor(input_img, cv.COLOR_BGR2GRAY)
# Harris角点检测
gray_float = np.float32(gray_img)
harris_response = cv.cornerHarris(gray_float, HARRIS_BLOCK_SIZE, HARRIS_KSIZE, HARRIS_K)
harris_response = cv.dilate(harris_response, None)
# 阈值处理与连通区域分析
_, thresholded = cv.threshold(harris_response, THRESHOLD_RATIO * harris_response.max(), 255, 0)
thresholded = np.uint8(thresholded)
_, _, stats, centroids = cv.connectedComponentsWithStats(thresholded)
# 角点处理
if len(centroids) > 1:
raw_corners = centroids[1:] # 排除背景点
print(f"检测到 {len(raw_corners)} 个初始角点")
# 角点精化
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 100, 0.001)
refined_corners = cv.cornerSubPix(gray_float, np.float32(raw_corners), SUBPIX_WIN_SIZE, (-1,-1), criteria)
else:
print("未检测到有效角点")
raw_corners = refined_corners = np.array([])
# 可视化处理
def draw_corners(img, corners, color, radius):
for pt in corners:
x, y = map(int, pt)
if 0 <= x < img.shape[1] and 0 <= y < img.shape[0]:
cv.circle(img, (x, y), radius, color, -1)
result_img = input_img.copy()
draw_corners(result_img, raw_corners, (0, 0, 255), 5) # 红色原始角点
draw_corners(result_img, refined_corners, (0, 255, 0), 3) # 绿色精化角点
# 结果展示
plt.figure(figsize=(12, 6))
plt.subplot(121), plt.imshow(cv.cvtColor(input_img, cv.COLOR_BGR2RGB))
plt.title('原始图像'), plt.axis('off')
plt.subplot(122), plt.imshow(cv.cvtColor(result_img, cv.COLOR_BGR2RGB))
plt.title(f'角点检测结果\n共检测到 {len(raw_corners)} 个角点'), plt.axis('off')
plt.tight_layout()
plt.savefig('corner_detection_result.png', dpi=300, bbox_inches='tight')
plt.show()
其实源代码没有这么麻烦,但是涉及显示问题,即使我下了QT库也无法正常显示所以只能用matplotlib代替。
效果如下


这样的效果就好一点了,而且运行的时候还会显示角点数
三最后一语
今天就先学这么多吧,新的一周开始了希望大家新的一周又更多收获。
"唯独在这些孤独和沉思默想的时刻。我才是我,和天性相符的我, 我才既无忧烦又无羁束。"
------卢梭《一个孤独的散步者的遐想》
感谢观看,共勉!!