4、相机畸变模型:径向畸变与切向畸变、畸变校正原理与OpenCV实现
做传感器融合,最头疼的问题之一就是相机和激光雷达的数据对不上。你想想看,激光雷达给的是精确的三维点云,相机拍的是二维图像,两者要融合,首先得保证相机自己拍出来的图像是「真实」的。但现实中的相机,尤其是那些便宜的、广角的镜头,拍出来的照片往往有变形——直线变弯了,人脸变胖了。这就是畸变。
我刚开始做多传感器融合时,就吃过这个亏。当时用了一台工业相机,标定完内参后直接拿去和激光雷达做联合标定,结果投影误差大得离谱。折腾了两天才发现,我压根没做畸变校正。嗯,从那以后,畸变模型这块我再也不敢马虎了。
4.1 畸变从哪来?
畸变说白了,就是光线经过镜头时,没有按照理想的小孔成像模型走。镜头是透镜,透镜有曲率,光线穿过时会发生折射。理想情况下,所有光线都应该汇聚到一点,但实际镜头做不到完美。
畸变主要分两类:径向畸变和切向畸变。我习惯把它们叫做「镜头本身的锅」和「装配工艺的锅」。
4.2 径向畸变:镜头曲率惹的祸
径向畸变是镜头形状导致的。光线经过透镜边缘时,折射角度和中心区域不一样,结果就是图像边缘的像素被「拉偏」了。
径向畸变有两种表现:
- 桶形畸变:图像边缘向外膨胀,直线变成向外弯的弧线。广角镜头常见。
- 枕形畸变:图像边缘向内收缩,直线变成向内弯的弧线。长焦镜头常见。
数学上,径向畸变用多项式来建模。我见过最常用的公式是这样的:
x_corrected = x * (1 + k1 * r² + k2 * r⁴ + k3 * r⁶)
y_corrected = y * (1 + k1 * r² + k2 * r⁴ + k3 * r⁶)
其中 (x, y) 是畸变图像中的像素坐标(归一化平面),r 是像素到图像中心的距离,k1、k2、k3 是径向畸变系数。
4.3 切向畸变:镜头装歪了
切向畸变,说白了就是镜头和成像平面不平行。可能是镜头装歪了,也可能是传感器贴片时没对齐。
切向畸变的数学模型:
x_corrected = x + [2 * p1 * x * y + p2 * (r² + 2 * x²)]
y_corrected = y + [p1 * (r² + 2 * y²) + 2 * p2 * x * y]
p1、p2 就是切向畸变系数。这个畸变在图像上表现为:原本应该是正方形的物体,变成了梯形或者平行四边形。
4.4 畸变校正原理:从「弯」到「直」
畸变校正的原理其实很简单:我们知道了畸变模型和畸变系数,就能算出每个像素「本来应该在什么位置」。
具体流程是这样的:
- 对图像中的每个像素 (u, v),先转换到归一化坐标系 (x, y)。
- 用畸变模型计算畸变后的坐标 (x_distorted, y_distorted)。
- 再转换回像素坐标系 (u_distorted, v_distorted)。
- 从原图中取 (u_distorted, v_distorted) 处的像素值,赋值给校正后的 (u, v)。
这里有个关键点:我们做的是「反向映射」。不是问「原图上的点去了哪里」,而是问「校正后的点来自原图的哪里」。这样做的好处是,校正后的图像每个像素都有值,不会出现空洞。
4.5 OpenCV 实现:两行代码搞定
OpenCV 把畸变校正封装得特别好。我个人习惯用 cv2.undistort(),一行代码就能完成校正。但如果你想更精细地控制,可以用 cv2.initUndistortRectifyMap() 先生成映射表,再用 cv2.remap() 执行重映射。
下面是我常用的代码模板:
import cv2
import numpy as np
# 假设你已经标定好了相机内参和畸变系数
camera_matrix = np.array([[fx, 0, cx],
[0, fy, cy],
[0, 0, 1]], dtype=np.float32)
dist_coeffs = np.array([k1, k2, p1, p2, k3], dtype=np.float32)
# 方法一:直接校正(简单粗暴)
img_undistorted = cv2.undistort(img, camera_matrix, dist_coeffs)
# 方法二:先生成映射表,再重映射(适合视频流)
map1, map2 = cv2.initUndistortRectifyMap(
camera_matrix, dist_coeffs, None, camera_matrix,
(img.shape[1], img.shape[0]), cv2.CV_32FC1
)
img_undistorted = cv2.remap(img, map1, map2, cv2.INTER_LINEAR)
4.6 实战中的坑与避坑指南
做畸变校正,有几个坑我踩过,分享给你:
- 坑一:忘记归一化坐标系。畸变模型是在归一化坐标系下定义的,不是像素坐标系。如果你直接把像素坐标代入畸变公式,结果肯定不对。
- 坑二:畸变系数顺序搞错。OpenCV 的畸变系数顺序是 [k1, k2, p1, p2, k3],但有些标定工具输出的是 [k1, k2, k3, p1, p2]。我吃过这个亏,校正出来的图像全是扭曲的。
- 坑三:校正后图像边缘被裁剪。畸变校正后,图像边缘会有黑边。你可以用
cv2.getOptimalNewCameraMatrix()来调整内参矩阵,保留更多有效像素。
4.7 知识体系总览
下面这张图,是我自己总结的畸变模型知识结构。你可以把它当作一个快速索引:
这张图把畸变的来源、类型、数学模型、校正方法和实战要点串在了一起。我个人习惯在做项目前先过一遍这个结构,确保没有遗漏。
好了,关于畸变模型和校正,就聊这么多。记住一句话:畸变校正不是可选项,而是必选项。尤其是做激光雷达和相机融合时,图像稍微歪一点,投影误差就会大得离谱。别问我怎么知道的——都是泪。
可可精品资料库