2. 相机畸变校正
任务目标
在完成相机内参与畸变系数标定后,利用得到的畸变系数对实际拍摄图像进行去畸变处理,并直观比较校正前后的图像差异。本案例聚焦于畸变校正这一核心环节,假设已通过棋盘格标定获得相机内参矩阵与畸变系数。
实现原理与依赖
相机镜头畸变主要由径向畸变(k1, k2, k3)和切向畸变(p1, p2)引起。OpenCV提供cv2.undistort()函数,基于标定得到的相机矩阵与畸变系数,对整幅图像进行重映射校正。本案例使用OpenCV内置的cv2.calibrateCamera()完成标定,随后对同一相机拍摄的任意图像执行去畸变。
依赖:opencv-python、numpy。若需可视化棋盘格角点检测结果,还需matplotlib(可选)。
关键完整代码
import cv2
import numpy as np
import glob
# ========== 1. 标定相机(使用棋盘格图像) ==========
# 棋盘格内角点数(例如 9x6)
CHECKERBOARD = (9, 6)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
objp = np.zeros((CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2)
objpoints = [] # 世界坐标系中的3D点
imgpoints = [] # 图像中的2D点
# 读取标定图像(请将路径替换为实际标定图片)
images = glob.glob('calib_images/*.jpg')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, CHECKERBOARD, None)
if ret:
objpoints.append(objp)
corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
imgpoints.append(corners2)
# 执行标定,获得相机矩阵与畸变系数
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
objpoints, imgpoints, gray.shape[::-1], None, None)
print("相机内参矩阵:\n", mtx)
print("畸变系数:", dist.ravel())
# ========== 2. 对实际图像去畸变 ==========
# 读取一张待校正图像(可以是标定图像之一,或同相机拍摄的其他图像)
test_img = cv2.imread('test_image.jpg')
if test_img is None:
raise FileNotFoundError("未找到测试图像,请检查路径")
h, w = test_img.shape[:2]
# 方法一:直接使用 undistort(简单直接)
undistorted = cv2.undistort(test_img, mtx, dist, None, mtx)
# 方法二:先计算映射表再重映射(适合视频流或批量处理)
# newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
# mapx, mapy = cv2.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w, h), 5)
# undistorted = cv2.remap(test_img, mapx, mapy, cv2.INTER_LINEAR)
# ========== 3. 可视化对比 ==========
# 将原图与校正图并排显示
comparison = np.hstack((test_img, undistorted))
cv2.imwrite('comparison_result.jpg', comparison)
# 显示结果(按任意键关闭)
cv2.imshow('Original (Left) vs Undistorted (Right)', comparison)
cv2.waitKey(0)
cv2.destroyAllWindows()
print("校正完成,对比图已保存为 comparison_result.jpg")
运行与验证
- 准备数据:创建
calib_images/文件夹,放入10~15张不同角度拍摄的棋盘格照片(建议分辨率不低于640×480);准备一张待校正的测试图像test_image.jpg(可为标定图像之一)。 - 运行脚本:
python undistort_demo.py,终端将打印相机内参与畸变系数。 - 观察输出:程序弹出对比窗口,左侧为原图,右侧为去畸变后的图像。保存的
comparison_result.jpg即为对比结果。
通过判定标准:
- 校正后图像中原本弯曲的直线(如墙面边缘、门框、棋盘格边缘)恢复为直线;
- 图像边缘区域的拉伸或桶形/枕形畸变明显减轻;
- 若使用标定图像本身测试,棋盘格角点在校正后图像中的投影位置与理想针孔模型一致(可通过叠加角点检测验证)。
常见问题
- 标定失败:确保棋盘格图像清晰、角点完整可见,且拍摄角度覆盖画面边缘;若
findChessboardCorners返回False,可尝试调整CHECKERBOARD尺寸或增加图像数量。 - 校正后图像边缘黑边:这是正常现象,因校正后像素重映射导致边缘无数据。若需去除黑边,可使用
getOptimalNewCameraMatrix配合roi裁剪。 - 校正效果不明显:检查畸变系数是否数量级过小(如k1接近0),此时畸变本身轻微,属正常情况;或确认测试图像与标定图像来自同一相机且分辨率一致。