2. 畸变校正与图像去畸变

任务目标

利用相机标定得到的内参矩阵和畸变系数,对一张实际拍摄的棋盘格图像执行去畸变处理,并直观对比校正前后的图像差异。本案例聚焦于图像去畸变这一环节,假设标定参数已通过OpenCV的calibrateCamera获得。

核心原理与实现方案

相机镜头畸变主要由径向畸变(k1, k2, k3)和切向畸变(p1, p2)描述。OpenCV提供两种去畸变方式:cv2.undistort()直接对整幅图像重映射,操作简单;cv2.initUndistortRectifyMap() + cv2.remap()先计算映射表再执行重映射,适合视频流或批量处理场景(映射表只需计算一次)。本案例采用第二种方式,并利用cv2.getOptimalNewCameraMatrix()优化内参以保留更多有效像素。

环境依赖

  • Python 3.8+
  • OpenCV(pip install opencv-python)
  • NumPy(pip install numpy)

完整实现代码

import cv2
import numpy as np

# ========== 1. 标定参数(示例值,来自calibrateCamera输出) ==========
# 内参矩阵 K
K = np.array([[532.5, 0, 342.2],
              [0, 534.1, 233.5],
              [0, 0, 1]], dtype=np.float64)
# 畸变系数 [k1, k2, p1, p2, k3]
dist = np.array([-0.2873, 0.1125, 0.0012, -0.0008, -0.0184], dtype=np.float64)

# ========== 2. 读取图像 ==========
img = cv2.imread('chessboard.jpg')
if img is None:
    raise FileNotFoundError("请将图像命名为 chessboard.jpg 放在当前目录")
h, w = img.shape[:2]

# ========== 3. 优化内参(保留更多有效像素) ==========
new_K, roi = cv2.getOptimalNewCameraMatrix(K, dist, (w, h), alpha=1, newImgSize=(w, h))

# ========== 4. 计算映射表并执行去畸变 ==========
map_x, map_y = cv2.initUndistortRectifyMap(K, dist, None, new_K, (w, h), cv2.CV_32FC1)
undistorted = cv2.remap(img, map_x, map_y, interpolation=cv2.INTER_LINEAR)

# ========== 5. 裁剪ROI区域(去除黑色边框) ==========
x, y, roi_w, roi_h = roi
undistorted_cropped = undistorted[y:y+roi_h, x:x+roi_w]

# ========== 6. 保存结果 ==========
cv2.imwrite('undistorted.jpg', undistorted_cropped)
print(f"去畸变完成,结果已保存为 undistorted.jpg,尺寸: {undistorted_cropped.shape[1]}x{undistorted_cropped.shape[0]}")

运行与验证方法

  1. 准备输入:将一张棋盘格照片(建议10×7内角点,拍摄时棋盘尽量充满画面且边缘有明显弯曲)命名为chessboard.jpg,与脚本置于同一目录。
  2. 执行脚本:python undistort_demo.py
  3. 验证通过条件:
    • 程序正常结束并输出保存信息;
    • 打开undistorted.jpg,观察棋盘格直线边缘:原图中弯曲的棋盘格边线(尤其是图像边缘区域)在校正后应恢复为直线;
    • 校正后图像四周可能出现黑色或空白区域(由alpha=1保留全部像素导致),裁剪后该区域被移除,图像内容完整无缺失。

校正效果对比

下图直观展示了去畸变处理前后的典型差异——原图中因镜头畸变而弯曲的直线在校正后恢复平直,图像边缘区域的拉伸变形得到修正:

校正前(畸变图像) 边缘弯曲 边缘弯曲 去畸变 校正后(直线恢复) 直线恢复 直线恢复

常见问题

  • 校正后图像边缘出现黑色区域:这是正常现象,因畸变校正会重新映射像素。设置alpha=1时保留全部像素但产生黑边,配合ROI裁剪即可去除;若希望自动缩放去除黑边,可将alpha设为0。
  • 棋盘格直线仍弯曲:检查标定参数是否准确——拍摄15~20张不同角度的棋盘格图像进行标定,并确保棋盘格覆盖画面各个区域(尤其是边缘和角落)。
  • 校正后图像模糊:使用INTER_LINEAR插值已满足多数场景;若对质量要求高,可改用INTER_CUBIC,但耗时略增。