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 畸变校正的直观流程

下面这张图展示了从畸变图像到校正图像的完整流程:

畸变校正流程 原始畸变图像 桶形/枕形/切向畸变 畸变参数 k₁,k₂,p₁,p₂,k₃ 畸变模型计算 径向+切向公式 校正 图像 核心思想:像素重映射 1. 对校正图像的每个像素 (u', v'),计算其在归一化平面的坐标 2. 用畸变模型算出畸变后的归一化坐标 3. 再投影回像素坐标 (u, v),取原图对应位置的像素值

2.6 畸变参数怎么用?

在 OpenCV 里,畸变校正就两步:

  1. 标定得到参数:用棋盘格照片跑 calibrateCamera(),输出 distCoeffs
  2. 校正图像:调用 undistort() 或 initUndistortRectifyMap() + remap()

个人习惯:我一般用 initUndistortRectifyMap() + remap() 的组合。虽然多写两行代码,但可以提前生成映射表,后续批量处理图片时速度更快。

2.7 小结

这一章我们搞清楚了:

  • 畸变的两种类型——径向和切向
  • 它们的数学模型——多项式加交叉项
  • 畸变对成像的实际影响——直线变弯、测量不准
  • 校正流程——从参数到重映射