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]}")
运行与验证方法
- 准备输入:将一张棋盘格照片(建议10×7内角点,拍摄时棋盘尽量充满画面且边缘有明显弯曲)命名为
chessboard.jpg,与脚本置于同一目录。 - 执行脚本:
python undistort_demo.py - 验证通过条件:
- 程序正常结束并输出保存信息;
- 打开
undistorted.jpg,观察棋盘格直线边缘:原图中弯曲的棋盘格边线(尤其是图像边缘区域)在校正后应恢复为直线; - 校正后图像四周可能出现黑色或空白区域(由
alpha=1保留全部像素导致),裁剪后该区域被移除,图像内容完整无缺失。
校正效果对比
下图直观展示了去畸变处理前后的典型差异——原图中因镜头畸变而弯曲的直线在校正后恢复平直,图像边缘区域的拉伸变形得到修正:
常见问题
- 校正后图像边缘出现黑色区域:这是正常现象,因畸变校正会重新映射像素。设置
alpha=1时保留全部像素但产生黑边,配合ROI裁剪即可去除;若希望自动缩放去除黑边,可将alpha设为0。 - 棋盘格直线仍弯曲:检查标定参数是否准确——拍摄15~20张不同角度的棋盘格图像进行标定,并确保棋盘格覆盖画面各个区域(尤其是边缘和角落)。
- 校正后图像模糊:使用
INTER_LINEAR插值已满足多数场景;若对质量要求高,可改用INTER_CUBIC,但耗时略增。