3. 畸变模型:径向畸变与切向畸变

聊完内参外参,咱们得面对一个现实——镜头不是完美的。你想想看,再贵的工业镜头,拍出来的照片边缘多少都会有点变形。这就是畸变。

我刚开始做标定时,总觉得畸变是小事。直到有一次做高精度测量,图像中心区域精度0.1mm,到了边缘直接差了2mm。嗯,从那以后我再也不敢忽略畸变了。

3.1 畸变从哪来?

畸变说白了就是光线通过镜头时,实际路径和理想路径之间的偏差。主要分两类:

  • 径向畸变:光线在透镜边缘弯曲得更厉害,导致图像点沿径向偏移
  • 切向畸变:镜头和成像平面不平行,导致图像点沿切向偏移

我个人的经验是:径向畸变占畸变总量的90%以上,切向畸变通常很小,但在高精度场景下不能忽略。

3.2 径向畸变——大头娃娃与枕形失真

径向畸变有两种典型表现:

类型 现象 常见场景
桶形畸变 图像中心向外膨胀,边缘向内收缩 广角镜头、手机摄像头
枕形畸变 图像中心向内收缩,边缘向外膨胀 长焦镜头、变焦镜头长焦端

数学上,径向畸变用泰勒级数建模。设归一化坐标 (x, y),畸变后的坐标为:

x_distorted = x * (1 + k1*r² + k2*r⁴ + k3*r⁶)
y_distorted = y * (1 + k1*r² + k2*r⁴ + k3*r⁶)

其中 r² = x² + y²,k1、k2、k3 是径向畸变系数。

我的经验:大多数情况下只用 k1、k2 就够了。k3 通常用于鱼眼镜头或畸变特别大的镜头。我在项目中试过,普通工业镜头用 k1、k2 校正后,残差已经小于 0.3 像素。

3.3 切向畸变——镜头装歪了

切向畸变,说白了就是镜头和传感器不平行。可能是装配公差,也可能是镜头卡口磨损。

数学模型是这样的:

x_distorted = x + [2*p1*x*y + p2*(r² + 2*x²)]
y_distorted = y + [p1*(r² + 2*y²) + 2*p2*x*y]

其中 p1、p2 是切向畸变系数。

注意:切向畸变系数通常很小(10⁻³ 量级),但如果你的标定板图像出现明显的梯形变形,那就要检查镜头安装是否牢固了。我曾经遇到过一个案例,镜头卡口松动导致切向畸变系数比正常值大了 10 倍。

3.4 Brown-Conrady 畸变模型——工业界的标准

把径向和切向畸变合在一起,就是 Brown-Conrady 模型。这是工业视觉领域最常用的畸变模型,OpenCV 和 Halcon 都在用。

完整公式:

x_distorted = x * (1 + k1*r² + k2*r⁴ + k3*r⁶) + [2*p1*x*y + p2*(r² + 2*x²)]
y_distorted = y * (1 + k1*r² + k2*r⁴ + k3*r⁶) + [p1*(r² + 2*y²) + 2*p2*x*y]

一共 5 个畸变参数:(k1, k2, k3, p1, p2)。

核心要点:Brown-Conrady 模型假设畸变是连续的、平滑的。如果你的镜头畸变特别大(比如鱼眼),这个模型就不够用了,得用更复杂的模型。

3.5 畸变对测量的影响——别小看它

我直接给你看一组数据。这是我用 12mm 工业镜头做的实测:

图像位置 径向畸变量(像素) 对应实际偏移(mm)
中心(0, 0) 0.0 0.00
1/4 视场 2.3 0.12
1/2 视场 8.7 0.45
3/4 视场 19.5 1.01
边缘 35.2 1.83

看到了吗?边缘的畸变误差接近 2mm。如果你的测量精度要求是 ±0.1mm,不校正畸变根本没法用。

畸变对测量的具体影响:

  • 尺寸测量:边缘位置的物体尺寸会被放大或缩小
  • 位置定位:物体在图像不同位置,定位偏差不同
  • 形状检测:直线会变成曲线,圆会变成椭圆
  • 多相机拼接:畸变不一致会导致拼接错位
避坑指南:我曾经做过一个项目,客户说他们的测量结果总是偏大。查了半天,发现是畸变校正参数用的是出厂默认值,而镜头经过运输震动后,畸变已经变了。从那以后,我每次项目开始前都会重新标定畸变参数,绝不偷懒。

3.6 畸变校正流程

畸变校正分两步走:

  1. 标定阶段:用标定板图像计算出畸变系数 (k1, k2, k3, p1, p2)
  2. 校正阶段:用这些系数对每帧图像做重映射

OpenCV 里校正畸变的代码很简单:

import cv2
import numpy as np

# 假设你已经有了相机内参矩阵 K 和畸变系数 dist
# K = [[fx, 0, cx], [0, fy, cy], [0, 0, 1]]
# dist = [k1, k2, p1, p2, k3]

# 生成校正映射
h, w = img.shape[:2]
new_camera_matrix, roi = cv2.getOptimalNewCameraMatrix(K, dist, (w, h), 1, (w, h))
mapx, mapy = cv2.initUndistortRectifyMap(K, dist, None, new_camera_matrix, (w, h), cv2.CV_32FC1)

# 应用校正
undistorted_img = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
性能优化:如果是在实时系统中做畸变校正,建议把 mapx、mapy 提前算好存起来,运行时只做 remap。我做过测试,这样能节省 60% 以上的计算时间。

3.7 本章知识结构

下面这张图帮你理清畸变模型的整体脉络:

畸变模型知识结构 畸变模型 径向畸变 切向畸变 桶形畸变 枕形畸变 k1/k2/k3 p1 系数 p2 系数 Brown-Conrady 模型 畸变校正 → 重映射 → 测量 核心:5 个畸变参数 (k1, k2, k3, p1, p2) 描述镜头畸变,校正后测量精度可达亚像素级

3.8 小结

畸变模型这块,记住几个关键点:

  • 径向畸变是主角,切向畸变是配角
  • Brown-Conrady 模型是工业标准,5 个参数搞定
  • 畸变校正能显著提升测量精度,尤其是边缘区域
  • 实时系统中提前算好映射表,别每帧都算

我个人习惯是:拿到一个新镜头,先拍一张棋盘格,看看边缘的直线弯成什么样。心里有个数,后面标定时就知道该重点关注什么了。