4. 图像畸变校正

任务说明

本案例针对机械手视觉定位中相机镜头引入的图像畸变问题,使用已知的相机内参矩阵和畸变系数,对一张棋盘格标定板图像执行去畸变处理,并通过角点重投影位置对比验证校正效果。输入图像为标定阶段采集的原始畸变图像,输出为校正后的无畸变图像及定量验证结果。

实现方案

采用OpenCV标准去畸变流程:先读取原始图像,利用cv2.undistort()函数结合内参与畸变系数生成校正图像;随后在原始图像和校正图像上分别检测棋盘格角点,计算校正前后角点坐标的偏移量,以平均像素误差作为校正效果的量化指标。代码中使用的内参和畸变系数来自典型工业相机标定结果,棋盘格尺寸为9×6内角点,方格边长25mm。

原始畸变图像 棋盘格标定板 含径向/切向畸变 相机参数 内参矩阵 K (3×3) 畸变系数 k1,k2,p1,p2,k3 fx=822.79, fy=823.18 cx=326.45, cy=239.82 cv2.undistort() 映射校正 + 角点检测 + 重投影误差计算 校正后图像 undistorted.jpg 验证输出 角点偏移量统计 图:图像畸变校正处理流程与验证路径

代码实现


import cv2
import numpy as np

# 相机内参(来自标定结果)
K = np.array([[822.79, 0.0, 326.45],
              [0.0, 823.18, 239.82],
              [0.0, 0.0, 1.0]], dtype=np.float64)

# 畸变系数 (k1, k2, p1, p2, k3)
dist_coeffs = np.array([-0.2834, 0.0735, 0.0012, -0.0008, -0.0156], dtype=np.float64)

# 棋盘格参数:9列×6行内角点
pattern_size = (9, 6)

def load_image(path):
    """读取图像并转为灰度"""
    img = cv2.imread(path)
    if img is None:
        raise FileNotFoundError(f"无法读取图像: {path}")
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    return img, gray

def undistort_image(img):
    """使用内参和畸变系数校正图像"""
    h, w = img.shape[:2]
    new_K, roi = cv2.getOptimalNewCameraMatrix(K, dist_coeffs, (w, h), 1.0, (w, h))
    undistorted = cv2.undistort(img, K, dist_coeffs, None, new_K)
    # 裁剪ROI区域(去除边缘黑边)
    x, y, w_roi, h_roi = roi
    undistorted = undistorted[y:y+h_roi, x:x+w_roi]
    return undistorted

def detect_corners(gray_img):
    """检测棋盘格角点,返回亚像素坐标"""
    ret, corners = cv2.findChessboardCorners(gray_img, pattern_size, None)
    if not ret:
        raise RuntimeError("未能检测到棋盘格角点")
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
    corners_sub = cv2.cornerSubPix(gray_img, corners, (11, 11), (-1, -1), criteria)
    return corners_sub.reshape(-1, 2)

def compute_corner_offset(corners_orig, corners_undist):
    """计算校正前后角点坐标的欧氏距离"""
    diff = corners_orig - corners_undist
    distances = np.sqrt(np.sum(diff**2, axis=1))
    return distances

def main():
    # 1. 加载原始畸变图像
    img_orig, gray_orig = load_image("calib_image.jpg")
    
    # 2. 执行去畸变
    img_undist = undistort_image(img_orig)
    gray_undist = cv2.cvtColor(img_undist, cv2.COLOR_BGR2GRAY)
    
    # 3. 检测校正前后角点
    corners_orig = detect_corners(gray_orig)
    corners_undist = detect_corners(gray_undist)
    
    # 4. 计算偏移量
    offsets = compute_corner_offset(corners_orig, corners_undist)
    
    # 5. 输出统计结果
    print(f"检测到角点数: {len(corners_orig)}")
    print(f"平均偏移量: {np.mean(offsets):.2f} 像素")
    print(f"最大偏移量: {np.max(offsets):.2f} 像素")
    print(f"最小偏移量: {np.min(offsets):.2f} 像素")
    
    # 6. 保存校正结果
    cv2.imwrite("undistorted.jpg", img_undist)
    print("校正图像已保存为 undistorted.jpg")

if __name__ == "__main__":
    main()

运行与验证

依赖环境:Python 3.8+,OpenCV 4.x,NumPy。安装命令:pip install opencv-python numpy。

输入文件:将标定阶段采集的棋盘格图像命名为calib_image.jpg,放置于代码同目录。图像应包含完整棋盘格(9×6内角点),光照均匀、无明显遮挡。

执行方式:直接运行脚本:python undistort_demo.py。

验证方法:程序输出角点偏移量统计,并生成undistorted.jpg。判定标准:

  • 校正后图像中棋盘格直线边缘无明显弯曲,格线横平竖直;
  • 角点偏移量统计中,平均偏移量通常大于0.5像素(说明原始图像确实存在畸变且被校正);
  • 若将校正后图像再次检测角点并与理论投影位置比较,重投影误差应小于0.5像素(本案例通过校正前后角点偏移量间接反映校正幅度)。

预期输出示例:检测到角点数: 54;平均偏移量: 3.42 像素;最大偏移量: 7.85 像素;最小偏移量: 0.23 像素。实际数值取决于输入图像的畸变程度。