2. 相机畸变:径向畸变与切向畸变
聊完针孔模型,咱们得面对一个现实——真实镜头不是完美的。
你想想看,理想情况下光线应该笔直穿过光心,但实际镜头是玻璃做的,有曲率、有装配误差。光线一拐弯,像素就偏离了它本该在的位置。这就是畸变。
我最早接触畸变是在做工业检测项目时。当时用广角镜头拍电路板,结果板子边缘的焊点全歪了,检测算法死活对不准。后来才发现——不是算法烂,是镜头畸变没校正。嗯,从那以后我再也不敢忽略这一步了。
2.1 畸变从哪来?
畸变主要分两类:径向畸变和切向畸变。它们的物理成因完全不同。
| 畸变类型 | 成因 | 典型表现 |
|---|---|---|
| 径向畸变 | 镜头曲面折射不均匀,光线越靠近边缘偏折越大 | 直线变弯,像鱼眼效果 |
| 切向畸变 | 镜头与成像平面不平行(装配或制造误差) | 图像像被“拉扯”倾斜 |
2.2 径向畸变:桶形 vs 枕形
径向畸变是最常见的。说白了,就是离光心越远,畸变越严重。
- 桶形畸变:图像边缘向外鼓出,像木桶。广角镜头常见。
- 枕形畸变:图像边缘向内收缩,像枕头。长焦镜头常见。
我做过一个手机摄像头标定项目,发现前置镜头几乎全是桶形畸变。自拍时脸在中间还好,一到边缘就变形——这就是径向畸变在捣鬼。
2.3 畸变的数学模型
数学上怎么描述畸变?我们拿归一化坐标说事。
设归一化坐标点为 (x, y),径向距离 r² = x² + y²。畸变后的坐标 (x_distorted, y_distorted) 为:
径向畸变模型(常用三项)
x_distorted = x * (1 + k₁*r² + k₂*r⁴ + k₃*r⁶)
y_distorted = y * (1 + k₁*r² + k₂*r⁴ + k₃*r⁶)
k₁, k₂, k₃是径向畸变系数- 通常只用
k₁, k₂就够了,k₃用于大畸变镜头(如鱼眼)
切向畸变模型
x_distorted = x + [2*p₁*x*y + p₂*(r² + 2*x²)]
y_distorted = y + [p₁*(r² + 2*y²) + 2*p₂*x*y]
p₁, p₂是切向畸变系数
合起来,完整的畸变参数就是 [k₁, k₂, p₁, p₂, k₃]。这就是 OpenCV 里 distCoeffs 向量的标准顺序。
关键理解:畸变模型是在归一化平面上施加的,不是在像素坐标上直接算。先畸变,再乘内参矩阵得到像素坐标——这个顺序不能乱。
2.4 畸变对成像的影响
畸变到底会造成什么后果?我列几个实际场景:
- 直线变曲线:建筑外墙、道路标线在图像边缘变成弧线
- 尺寸失真:同一个物体在画面中心和边缘大小不一致
- 测量误差:用畸变图像做三维重建,误差会直接放大
- 特征点偏移:角点检测、特征匹配的精度下降
我记得有一次做视觉定位,用棋盘格标定板算相机位姿。没校正畸变时,重投影误差有 3 个像素;校正后直接降到 0.3 像素。你看,差别就这么大。
避坑提醒:千万不要在畸变图像上直接做高精度测量。我曾经吃过这个亏——用未校正的图像算零件尺寸,结果边缘的零件比中间大了 5%。校正后才找到真实尺寸。
2.5 畸变校正的直观流程
下面这张图展示了从畸变图像到校正图像的完整流程:
2.6 畸变参数怎么用?
在 OpenCV 里,畸变校正就两步:
- 标定得到参数:用棋盘格照片跑
calibrateCamera(),输出distCoeffs - 校正图像:调用
undistort()或initUndistortRectifyMap()+remap()
个人习惯:我一般用 initUndistortRectifyMap() + remap() 的组合。虽然多写两行代码,但可以提前生成映射表,后续批量处理图片时速度更快。
2.7 小结
这一章我们搞清楚了:
- 畸变的两种类型——径向和切向
- 它们的数学模型——多项式加交叉项
- 畸变对成像的实际影响——直线变弯、测量不准
- 校正流程——从参数到重映射