2. 亚像素角点精确定位

任务说明

在相机标定中,棋盘格角点的定位精度直接影响标定结果的准确性。本案例使用OpenCV的cornerSubPix()函数,对findChessboardCorners()检测到的整数像素角点进行亚像素级精确定位,并可视化定位结果。

实现方案

核心流程为:读取棋盘格图像 → 转换为灰度图 → 使用findChessboardCorners()获取初始角点 → 使用cornerSubPix()迭代优化至亚像素精度 → 绘制并显示结果。亚像素定位基于角点邻域内灰度梯度的正交性约束,通过迭代最小化误差实现。

完整代码


import cv2
import numpy as np

# 棋盘格内角点数量(列数-1, 行数-1)
pattern_size = (9, 6)

# 读取图像并转为灰度
img = cv2.imread('chessboard.jpg')
if img is None:
    raise FileNotFoundError("未找到图像文件 chessboard.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 1. 整数像素级角点检测
found, corners = cv2.findChessboardCorners(gray, pattern_size, None)
if not found:
    raise RuntimeError("未能检测到棋盘格角点,请检查图像或pattern_size")

# 2. 亚像素角点精确定位
# 终止条件:最大迭代30次或误差小于0.001
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
corners_subpix = cv2.cornerSubPix(
    gray, corners, (11, 11), (-1, -1), criteria
)

# 3. 计算偏移量并可视化
offset = corners_subpix - corners
print(f"检测到 {len(corners)} 个角点")
print(f"最大亚像素偏移: {np.max(np.abs(offset)):.4f} 像素")

# 绘制亚像素角点(红色圆点)
img_result = img.copy()
for c in corners_subpix:
    x, y = c.ravel()
    cv2.circle(img_result, (int(x), int(y)), 3, (0, 0, 255), -1)

# 拼接原图与结果图便于对比
vis = np.hstack([img, img_result])
cv2.imshow('Original | Subpixel Corners', vis)
cv2.waitKey(0)
cv2.destroyAllWindows()

运行与验证

依赖:OpenCV(含contrib模块)、NumPy。使用pip install opencv-contrib-python numpy安装。

运行方式:将代码保存为subpixel_corners.py,与棋盘格图像chessboard.jpg置于同一目录,执行python subpixel_corners.py。

验证方法:观察弹出的对比窗口,亚像素角点应精确落在棋盘格黑白交界的角点上,且红色圆点中心与角点几何中心重合。控制台输出的最大亚像素偏移通常为0.1~0.5像素量级(具体值取决于图像质量与初始检测精度)。若偏移量接近0,说明初始整数检测已足够精确;若偏移量过大(>1像素),需检查图像是否模糊或棋盘格是否倾斜严重。

亚像素角点定位流程与验证观测点 findChessboardCorners 输出:整数像素角点 坐标精度:±0.5像素 cornerSubPix 迭代优化:梯度正交约束 终止条件:30次或ε=0.001 亚像素角点 精度:0.01~0.1像素 用于相机标定 验证观测点: ① 控制台输出最大亚像素偏移量(通常0.1~0.5像素) ② 红色圆点中心与棋盘格角点几何中心重合,无系统性偏移

判定标准

程序正常执行且无报错;对比窗口中红色角点与棋盘格角点精确对齐;控制台输出的最大偏移量在合理范围(0~1像素)内,即视为通过验证。若需定量评估,可保存亚像素坐标至文件,与手工标注的参考坐标对比,均方根误差小于0.1像素为优秀。