2. 畸变校正与图像去畸变
任务目标
使用已知的相机内参矩阵和畸变系数,对一张实际拍摄的棋盘格图像执行去畸变处理,并直观对比校正前后的图像差异,验证畸变校正效果。
核心原理与实现思路
相机镜头畸变主要由径向畸变(k1, k2, k3)和切向畸变(p1, p2)构成。OpenCV提供两种去畸变方式:cv2.undistort()直接输出校正图,以及cv2.initUndistortRectifyMap() + cv2.remap()先计算映射表再重映射(适合实时视频流)。本案例采用第二种方式,便于理解映射机制,同时计算效率更高。
环境依赖
- Python 3.8+
- OpenCV:
pip install opencv-python - NumPy:
pip install numpy
完整实现代码
import cv2
import numpy as np
# ========== 1. 相机参数(示例值,实际使用需通过标定获取) ==========
# 内参矩阵 K
K = np.array([[832.5, 0.0, 320.0],
[0.0, 832.5, 240.0],
[0.0, 0.0, 1.0]], dtype=np.float64)
# 畸变系数 [k1, k2, p1, p2, k3]
dist_coeffs = np.array([-0.35, 0.15, 0.001, -0.002, -0.05], dtype=np.float64)
# ========== 2. 读取图像 ==========
img = cv2.imread('distorted_chessboard.jpg')
if img is None:
raise FileNotFoundError("未找到图像文件,请将图像放在当前目录")
h, w = img.shape[:2]
# ========== 3. 计算校正映射表 ==========
# 优化内参:可调整alpha参数(0~1),0表示裁剪黑色边缘,1表示保留全部像素
new_K, roi = cv2.getOptimalNewCameraMatrix(K, dist_coeffs, (w, h), alpha=0.5, centerPrincipalPoint=True)
map_x, map_y = cv2.initUndistortRectifyMap(K, dist_coeffs, None, new_K, (w, h), cv2.CV_32FC1)
# ========== 4. 执行重映射(去畸变) ==========
undistorted_img = cv2.remap(img, map_x, map_y, interpolation=cv2.INTER_LINEAR)
# ========== 5. 裁剪黑色边缘(可选) ==========
x, y, roi_w, roi_h = roi
undistorted_cropped = undistorted_img[y:y+roi_h, x:x+roi_w]
# ========== 6. 保存与对比展示 ==========
cv2.imwrite('undistorted_result.jpg', undistorted_cropped)
# 拼接对比图(原图与校正图并排)
compare = np.hstack((img, undistorted_cropped))
cv2.imwrite('comparison.jpg', compare)
print("去畸变完成,结果已保存:undistorted_result.jpg 和 comparison.jpg")
运行方式
- 准备一张有明显畸变的棋盘格图像(或任意含直线结构的实拍图),命名为
distorted_chessboard.jpg,放在与脚本相同的目录下。 - 运行脚本:
python undistort_demo.py
验证方法与判定标准
- 视觉验证:打开
comparison.jpg,观察原图中弯曲的直线(如棋盘格边缘、墙面线条)在校正后是否恢复为直线。 - 定量验证:在图像边缘区域选取原图中明显弯曲的直线,用画图工具检查校正后该直线上的像素点是否共线。
- 通过条件:校正后图像中棋盘格方格接近正方形,边缘直线不再弯曲,且图像内容无严重拉伸变形。
常见问题
- 校正后边缘出现黑色区域:这是正常现象,可通过调整
alpha参数(0~1)平衡黑色区域大小与像素保留范围。 - 校正过度或不足:说明畸变系数不准确,需重新标定相机获取精确参数。
- 图像模糊:可尝试将
cv2.INTER_LINEAR改为cv2.INTER_CUBIC获得更平滑结果。