一、为什么需要相机内参数标定?
相机成像模型是在小孔成像模型基础上发展而来的,但是由于引入透镜以及CMOS、CCD等加工工艺和安装问题无法得到完美的模型,必须根据实际情况对这些偏差进行校正才能得到最接近小孔成像的理想模型。市面上的相机都不能保证得到严格的理想模型,一旦涉及测量和计算相关的问题就必须要对相机内参数进行标定。当用相机拍了一张图像发现显示中正常的中文字变得扭曲、直线变弧线等情况时,将内参数带入相机模型公式修正图像后可以令扭曲的文字和弧线恢复正常。
1.1 现实中的相机存在问题
- 镜头畸变:径向畸变、切向畸变(另外因为镜头的引入还会带来失焦问题即常说的景深问题)
- 安装误差:CMOS/CCD安装不平行于镜头平面
- 像素非正方形:像素不是完美的正方形(现在工业相机好多了)
- 主点偏移:光轴与图像平面交点不在图像中心
1.1.1 径向畸变(Radial Distortion)
桶形畸变:图像边缘向内弯曲

枕形畸变:图像边缘向外弯曲

鱼眼畸变:严重的非线性畸变

1.1.2 切向畸变(Tangential Distortion)
- 镜头与图像传感器不平行
- 安装过程中的倾斜误差
1.2 不标定相机内参数造成的问题
1.2.1 几何测量不准确
- 直线在图像中弯曲
- 距离测量误差
- 角度测量偏差

1.2.2 三维重建失败
1.2.3 视觉任务性能下降
- SLAM系统漂移
- 目标跟踪丢失
- 图像拼接错位
二、相机标定核心基础:四大坐标系与成像模型
2.1 相机成像的四大坐标系
2.1.1 世界坐标系
真实世界的物理坐标系,世界坐标系是根据实际场景人为设定的,通常以m/mm为单位。比如要通过双相机测量某个产品上某个螺丝孔位是否符合设计要求,根据图纸在产品上建立的坐标系就可以作为世界坐标系。
2.1.2 摄像机坐标系
相机光心为原点,X/Y 平行于成像平面,Z 为光轴方向,单位:m/mm。
坐标系原点建立在光圈位置,Z方向垂直于像平面坐标系:

摄像机坐标系下的点P和对应的像平面上的P′P'P′ 的关系如下,很简单的一个相似三角形计算:
P=[xyz]→P′=[x′y′]P = \begin{bmatrix} x \\ y \\ z \end{bmatrix} \rightarrow P' = \begin{bmatrix} x' \\ y' \end{bmatrix}P= xyz →P′=[x′y′]
{x′=fxzy′=fyz\begin{cases} x' = f\frac{x}{z} \\ y' = f\frac{y}{z} \end{cases} {x′=fzxy′=fzy
这里看到的是三维点到二维点的映射转换,涉及的两个坐标系在实际应用时都属于中间坐标系。摄像机坐标系到世界坐标系下还需要进行一次三维空间转换。像平面坐标系到图像中的像素坐标所在的坐标系也还有一层转换。在OpenCV中准备好一系列像素坐标系下特征点像素坐标和特征点在世界坐标系下对应的三维坐标就可以实现内参数标定。
2.1.3 像平面坐标系
成像平面的物理坐标系,原点为光轴与成像平面交点(主点),单位:mm,对应就是2.1.2图像中的二维像平面坐标系。
2.1.4 像素坐标系
相机传感器的像素坐标系,原点为图像左上角或者左下角,单位:pixel(像素)。

注意区别像平面坐标系,像素坐标系中的像素是离散的,单位是像素,一般作为原始输入。像平面坐标系中坐标值是连续的,单位一般是mm或者m,一般只是一个中间坐标系,用于解释原理的。像素坐标系下的点和像平面坐标系下的点存在一个平移和缩放的关系,摄像机坐标系到像素坐标系映射关系如下:
P=(x,y,z)→P′=(αxz+cx,βyz+cy)P = (x, y, z) \rightarrow P' = \left(\alpha\frac{x}{z} + c_x, \beta\frac{y}{z} + c_y\right)P=(x,y,z)→P′=(αzx+cx,βzy+cy)
其中,α=f∗k\alpha=f*kα=f∗k, β=f∗l\beta=f*lβ=f∗l,k和l分别为像素到物理尺寸的缩放系数,如果像素严格是正方形,这两个数值相等。
关键结论:标定的本质是求解坐标系之间的变换矩阵,建立像素与物理世界的一一对应。
2.2 相机成像模型
针孔相机的核心假设:光线沿直线传播,光心为投影中心;
核心投影公式:透视投影变换(摄像机坐标系→图像物理坐标系→像素坐标系);
相机机坐标系→图像物理坐标系:相似三角形推导;
图像物理坐标系→像素坐标系:平移(主点偏移)+ 缩放(像素物理尺寸);
针孔相机的内参数矩阵 K:首次引出内参,明确矩阵中每个元素的物理意义;
主点坐标(光轴与成像平面的交点像素坐标,理想为图像中心);
关键:内参数矩阵 K 由相机硬件决定(焦距、传感器、像素尺寸),一旦标定完成,相机内参固定(除非更换镜头 / 调整焦距)。
2.2.1 摄像机坐标系下相机模型公式推导
摄像机坐标系到像素坐标系映射关系用齐次坐标表示:
P′=[αx+cxzβy+cyzz]=[α0cx00βcy00010][xyz1]P' = \begin{bmatrix} \alpha x + c_x z \\ \beta y + c_y z \\ z \end{bmatrix} = \begin{bmatrix} \alpha & 0 & c_x & 0 \\ 0 & \beta & c_y & 0 \\ 0 & 0 & 1 & 0 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix}P′= αx+cxzβy+cyzz = α000β0cxcy1000 xyz1
注意:齐次坐标转欧式坐标不唯一,欧式坐标转齐次坐标是唯一的。

如果感光芯片不垂直,还需要引入一个角度参数,角度为90时就退化成上面的公式:
P′=[α−αcotθcx00β/sinθcy00010][xyz1] P' = \begin{bmatrix} \alpha & -\alpha\cot\theta & c_x & 0 \\ 0 & \beta/\sin\theta & c_y & 0 \\ 0 & 0 & 1 & 0 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} P′= α00−αcotθβ/sinθ0cxcy1000 xyz1
在实际项目中,代码中经常看到
P′=MP=K[I0]P P' =MP =K[I 0]P P′=MP=K[I0]P
其中,M矩阵被称为投影矩阵,这里是个特殊的投影矩阵,是摄像机坐标系和世界坐标系重合的特殊情况:
M=[α−αcotθcx00β/sinθcy00010] M = \begin{bmatrix} \alpha & -\alpha\cot\theta & c_x & 0 \\ 0 & \beta/\sin\theta & c_y & 0 \\ 0 & 0 & 1& 0 \end{bmatrix} M= α00−αcotθβ/sinθ0cxcy1000
K被称为摄像机内参数矩阵,I是单位阵,内参数决定了摄像机坐标系下空间点到像素坐标系中图像点的映射:
K=[α−αcotθcx0β/sinθcy001] K = \begin{bmatrix} \alpha & -\alpha\cot\theta & c_x \\ 0 & \beta/\sin\theta & c_y \\ 0 & 0 & 1 \end{bmatrix} K= α00−αcotθβ/sinθ0cxcy1
显然此时K矩阵有5个自由度。
2.2.2 世界坐标系下相机模型公式推导
一个人在不同角度下看物体呈现在视网膜的图像是不同的,相对于摄像机坐标系,又引入了世界坐标系:

这里实际上多了一层空间坐标转换,当R为单位阵,T为0时世界坐标系和摄像机坐标系重合:
P=[ RT01]Pw P = \begin{bmatrix} \ R &T \\ 0 & 1 \end{bmatrix} P_w P=[ R0T1]Pw
其中:
Pw=[ xwywzw1] P_w = \begin{bmatrix} \ x_w \\ y_w \\ z_w \\ 1 \end{bmatrix} Pw= xwywzw1
综上可知,世界坐标系到像素平面坐标系的变换关系如下。其中,M是投影矩阵,形状是[3, 4],R是世界坐标系到摄像机坐标系的旋转矩阵,形状是[3, 3],T是平移量,形状是[3,1]:
p=M[ RT01]Pw p = M\begin{bmatrix} \ R &T \\ 0 & 1 \end{bmatrix} P_w p=M[ R0T1]Pw
注意到这里仍然没有考虑相机的畸变,这个公式是不涉及畸变系数的。其中像素坐标系中的像素点p和世界坐标系中的点PwP_wPw可以是已知的,然后就是求解投影矩阵的11个参数,一个点可以构建2个方程。
求解方程的思路:
- 先构建方程组,将投影矩阵变成12维度列向量,通过PM=0的形式,求解12维的M列向量,求解过程涉及最小二乘法和奇异值分解,人为加入一个模为1的约束条件,可能会导致最终结果和真实结果存在尺度差异,然后再转换成标准的形状为[3,4]的投影矩阵。
对矩阵P进行奇异值分解:U2n∗12D12∗12VT12∗12U_{2n*12}D_{12*12}{V^T}_{12*12}U2n∗12D12∗12VT12∗12,V矩阵的最后一列就是要求解的结果。 - M=K[R T]M=K[R \ T]M=K[R T] 通过求解出的投影矩阵,进一步求解相机内参数矩阵K,和世界坐标系到摄像机坐标系的旋转矩阵R及平移向量T。这里面需要带入表达式,并且利用旋转矩阵T内部的约束条件求解。
2.3 相机的畸变模型(真实相机的 "非理想性",工程必考虑)
畸变的产生原因:镜头光学制造误差 + 装配误差,导致光线传播偏离直线,分为径向畸变和切向畸变;
径向畸变(主要畸变,镜头中心→边缘畸变递增):桶形畸变(广角镜头)、枕形畸变(长焦镜头);
矫正公式:基于泰勒级数展开,核心参数(径向畸变系数,k1/k2 即可满足大部分场景);
切向畸变(次要畸变,镜头与成像平面不平行导致):核心参数(切向畸变系数);
关键结论:内参数标定需同时求解 K 和畸变系数,才能完成完整的图像畸变矫正。
当存在畸变的时候,2.2节中提到的相机内参数矩阵K又有了新的变化,不再是线性方程组,无法再使用奇异值分解的方式,求解方式发生了变换,一般用牛顿法和列文伯格-马尔夸特方法求解非线性方程组。
非线性方程组求解一般要给变量设定一个初始值,然后进行迭代找到最优解。
三、棋盘格标定(张正友标定法)
3.1 棋盘格标定法的核心优势
成本低:仅需打印一张棋盘格标定板,无需高精度三维标定物;
精度高:兼顾标定效率和标定精度,满足工业 / 科研大部分场景;
易实现:OpenCV/Matlab/ROS 均有成熟的开源实现,无需从零推导。
3.2 棋盘格标定的核心原理
核心假设:标定板为平面(Z=0),通过多视角棋盘格图像求解单应性矩阵 H;
单应性矩阵(Homography):平面世界坐标系→像素坐标系的变换矩阵,建立 H 与内参 K、外参(R/t)的关系;
核心步骤:通过多张标定板图像得到多个单应性矩阵→利用矩阵约束求解内参 K→再求解每张图像的外参(旋转矩阵 R + 平移向量 t)→最后通过最小二乘法优化畸变系数;
关键:标定板图像数量建议15-20 张,且标定板在图像中覆盖不同位置(左上 / 右下 / 中心)、不同角度(倾斜 0-45°)、不同距离,提升标定精度。
棋盘格标定只是最常用的标定方法,不是唯一,理论上能在多张图像中找到一系列特征点对,就可以标定相机内参数,所以可以人为构造编码点,也可以想运动恢复结构中通过特征提取方法找到多张图像中某个特征点的轨迹,然后解算。前者有人工参与可以达到更高的精度,后者属于自标定范畴,精度和鲁棒性不如使用已知控制点的标定方法。。
四、实战必备工具
4.1 硬件准备
待标定相机:工业相机(注意鱼眼相机要用特定的模型,并不是所有的相机都用一套相机模型)
标定板:棋盘格标定板,可以用打印机直接打印,但是要设置每个格子的物理尺寸,标定时需要,如果追求更高的标定精度,可以在网上买高精度棋盘格板。
4.2 采集标定板图像
采集要求:采集15-20 张棋盘格图像,标定板需满足:
完整出现在图像中,无遮挡;
覆盖图像不同区域(左上、右上、左下、右下、中心);
包含不同倾斜角度(0°、15°、30°、45° 等);
包含不同拍摄距离(近、中、远);
采集技巧:相机固定焦距(变焦镜头需锁死焦距)、固定曝光 / 白平衡(避免图像亮度变化)、标定板保持平整;
图像格式:建议保存为 jpg/png,统一命名(如 calib_01.jpg、calib_02.jpg),放在单独文件夹。
五、实战落地:基于 OpenCV-Python 的相机内参标定
5.1 内参数标定的输入
# 棋盘格内角点数量 (width, height)
pattern_size: [11, 8]
# 单个棋盘格方块的物理尺寸 (单位:毫米)
square_size_mm: 25
# 亚像素优化窗口尺寸
subpixel_winsize: [7, 7]
# 图像数据基础路径
image_path: "data/src_images/"
# 标定结果输出路径
calib_result_path: "data/calib_ret/"
5.2 标定板角点检测
核心 API:
cv2.findChessboardCorners(检测棋盘格内角点)
| 参数 | 说明 |
|---|---|
gray |
输入灰度图像 |
pattern_size |
棋盘格内角点数量 (cols, rows) |
flags |
检测标志位组合 |
标志位说明:
CALIB_CB_ADAPTIVE_THRESH:自适应阈值处理CALIB_CB_NORMALIZE_IMAGE:图像灰度归一化CALIB_CB_FILTER_QUADS:过滤四边形误检
输出:
ret:是否检测到棋盘格corners:角点像素坐标(N, 1, 2)
cv2.cornerSubPix(角点亚像素精细化,提升检测精度)
| 参数 | 说明 |
|---|---|
gray |
输入灰度图像 |
corners |
初始角点坐标 |
subpixel_winsize |
搜索窗口大小 (7,7) |
(-1,-1) |
零区域表示无死区 |
criteria |
迭代终止条件 |
终止条件:
- 最大迭代 30 次
- 精度达到 0.001 像素即停止
cv2.drawChessboardCorners(绘制检测到的角点,验证检测效果,剔除检测失败的图像)

可以看到标定板并不是旋转对称的,实际上每个特征点位置都是唯一的。
完整调用代码如下:
python
def _process_image(self, fname: str, objp: np.ndarray):
"""处理单个图像"""
img = cv2.imread(fname)
if img is None:
return False
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(
gray, self.pattern_size,
flags=cv2.CALIB_CB_ADAPTIVE_THRESH
| cv2.CALIB_CB_NORMALIZE_IMAGE
| cv2.CALIB_CB_FILTER_QUADS
)
if ret:
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
corners_refined = cv2.cornerSubPix(
gray, corners,
self.subpixel_winsize,
(-1,-1), criteria
)
self.obj_points.append(objp)
self.img_points.append(corners_refined)
# 保存可视化结果
vis = cv2.drawChessboardCorners(img.copy(), self.pattern_size, corners_refined, ret)
save_path = os.path.join(self.calib_result_path, os.path.basename(fname))
cv2.imwrite(save_path, vis)
return ret
5.3 求解相机内参 + 畸变系数(核心标定步骤,一行 API 搞定)
核心 API:cv2.calibrateCamera;
输入参数:角点世界坐标(人工定义,如标定板左上角为 (0,0,0),相邻格子为 (25,0,0))、角点像素坐标、图像尺寸;
输出参数:相机内参数矩阵 K、畸变系数(k1,k2,p1,p2,k3)、每张图像的外参(R/t)、重投影误差;
关键:世界坐标的单位需与标定板格子物理尺寸一致(如格子 25mm,则世界坐标步长为 25)。
ret, mtx, dist,rvecs, tvecs = cv2.calibrateCamera(
self.obj_points, self.img_points,
cv2.imread(images[0]).shape[:2][::-1], None, None
)
| 参数 | 类型 | 说明 |
|---|---|---|
| 输入 | ||
obj_points |
List[ndarray] |
3D 世界坐标 [N×(88,3)] |
img_points |
List[ndarray] |
2D 像素坐标 [N×(88,1,2)] |
imageSize |
tuple |
图像尺寸 (width, height) |
cameraMatrix |
ndarray |
初始内参矩阵(传 None 自动估计) |
distCoeffs |
ndarray |
初始畸变系数(传 None 自动估计) |
| 输出 | ||
ret |
float |
总体重投影误差 |
mtx |
(3,3) |
相机内参矩阵 |
dist |
(5,1) |
畸变系数向量 |
rvecs |
List |
每张图的旋转向量 |
tvecs |
List |
每张图的平移向量 |
这个函数中输入输出数据如下图所示:





5.4 标定结果验证
重投影误差的物理意义:标定得到的参数反投影到像素坐标系的角点坐标与实际检测的角点坐标的平均距离(单位:pixel);
精度判断标准:重投影误差小于 1 像素为优秀,1-2 像素为合格,大于 2 像素需重新标定(原因:图像采集差 / 角点检测失败 / 标定板数量不足);
可视化:绘制重投影误差曲线 / 直方图,分析每张图像的误差贡献,剔除误差过大的图像重新标定。
python
def _calc_reproj_err(self, corners, rvecs, tvecs, mtx, dist):
reproj_err = []
for i in range(len(corners)):
corners_reproj, _ = cv2.projectPoints(self.obj_points[i], rvecs[i], tvecs[i], mtx, dist)
err = cv2.norm(corners_reproj, corners[i], cv2.NORM_L2) / len(corners_reproj)
reproj_err.append(err)
return reproj_err
5.7 输出格式参考
这里输出了内参数矩阵,相机畸变系数,重投影误差,如下:
python
相机内参矩阵:
- 1856.56801535
- 0.00000000
- 1272.15399299
- 0.00000000
- 1846.27512135
- 724.28347776
- 0.00000000
- 0.00000000
- 1.00000000
相机畸变系数:
- -0.40679068
- 0.23561453
- -0.00004663
- -0.00007326
- -0.08640262
平均重投影误差: 0.02307 像素
内参矩阵输出格式:
python
# 这里没有考虑感光芯片相邻边不垂直的情况
mtx = [[fx, 0, cx],
[ 0, fy, cy],
[ 0, 0, 1]]
畸变系数输出格式:
dist = [k1, k2, p1, p2, k3]
径向 径向 切向 切向 径向
六、实战延伸:标定结果的应用 ------ 图像畸变矫正
6.1 畸变矫正的两种方法
方法 1:cv2.undistort(直接矫正图像,一步到位,适合快速开发);
方法 2:cv2.initUndistortRectifyMap + cv2.remap(先计算矫正映射表,再映射图像,适合批量处理,效率更高);
核心输入:待矫正图像、标定得到的内参 K、畸变系数;
输出:无畸变的矫正图像(配矫正前 VS 矫正后效果对比图,突出边缘 / 直线矫正效果)。
6.2 矫正效果验证
视觉验证:观察图像中的直线(如标定板边缘、墙面墙角),矫正后应保持直线,无弯曲;
定量验证:对矫正后的标定板图像重新检测角点,检查角点排列是否规则。
6.3 工程优化:矫正图像的裁剪
问题:畸变矫正后图像边缘会出现黑边(因畸变矫正的像素映射导致);
解决方法:cv2.getOptimalNewCameraMatrix(求解优化后的内参矩阵,裁剪黑边,保留有效图像区域);
关键参数:alpha(裁剪系数,0 = 裁剪所有黑边,1 = 保留完整图像含黑边,0.5 为折中)。
七、标定精度提升:避坑指南与关键技巧
7.1 标定常见问题及解决方案(高频坑,附排查步骤)
问题 1:角点检测失败 / 检测不完整→原因:图像模糊 / 标定板遮挡 / 内角点尺寸设置错误→解决方案:重新采集清晰图像 / 剔除遮挡图像 / 核对内角点尺寸;
问题 2:重投影误差过大(>2 像素)→原因:标定板图像数量不足 / 覆盖角度 / 位置不够 / 标定板弯曲→解决方案:补充采集图像 / 重新打印平整标定板;
问题 3:矫正后图像仍有畸变→原因:畸变系数未求解 / 标定参数错误→解决方案:检查标定代码,确保保存 / 加载的参数正确;
问题 4:标定结果不稳定(多次标定 K 差异大)→原因:相机移动 / 焦距变化 / 曝光变化→解决方案:固定三脚架 / 锁死焦距 / 固定曝光白平衡。
7.2 标定精度提升的关键技巧(工业级经验)
标定板:打印后用卡尺精准测量格子尺寸,粘贴在硬质平板上(如亚克力板),保证绝对平整;
图像采集:采集 15-20 张图像,覆盖图像全区域、0-45° 倾斜角度、近中远三种距离,避免图像过亮 / 过暗 / 模糊;
角点检测:使用亚像素精细化(cv2.cornerSubPix),提升角点检测精度;
相机设置:固定焦距、曝光、白平衡、ISO(相机参数变化会导致内参变化);
环境要求:标定环境光线均匀,无强光反射(避免标定板反光导致角点检测失败);
多次标定:重复标定 3-5 次,取内参的平均值(减少随机误差)。
7.3 不同场景的标定注意事项
工业相机:标定前需完成镜头对焦,锁死焦距和光圈,避免参数变化;
手机相机:避免使用数码变焦,使用光学变焦(若有)并固定,手机保持稳定;
远距拍摄场景:增加标定板拍摄距离,保证远距图像的数量;
高精度测量场景:使用更高精度的标定板(如陶瓷标定板),增加图像数量(20-30 张)。
八、拓展内容:进阶标定知识
8.1 单目相机 VS 双目相机标定
单目标定:仅求解内参 + 畸变系数,无法测量绝对距离(需结合其他信息);
双目标定:先分别标定左右相机内参,再标定外参(极线几何)和基础矩阵 / 本质矩阵,可实现三维测距;
延伸:双目相机标定的核心是立体标定,基于张正友标定法拓展。
8.2 变焦镜头的标定方法
问题:变焦镜头焦距变化,内参数矩阵 K 会随之变化(f_x/f_y 改变);
解决方案:分焦距标定,在常用焦距段(如 5mm、10mm、15mm)分别标定,建立 "焦距 - 内参" 映射表,使用时根据当前焦距调用对应内参。
8.3 其他标定方法对比
传统标定法:基于高精度三维标定物(如标定块),精度高但成本高、操作复杂;
自标定法:无需标定物,通过相机运动求解内参,精度低、鲁棒性差,适合无标定物场景,如SFM;
张正友标定法:平衡成本、精度、操作难度,成为主流。
8.4 标定工具推荐(除了 OpenCV,还有这些选择)
Matlab Camera Calibrator:可视化操作,无需编程,适合快速标定,精度高;
ROS Calibration:基于 ROS 框架,适合机器人 / 无人机场景的相机 + 激光雷达联合标定;
Kalibr:开源标定工具,支持多相机、相机 + IMU 联合标定,适合 SLAM 场景。
九、总结与展望(梳理核心,引导后续学习)
9.1 本文核心知识点梳理
相机标定的本质:求解四大坐标系的变换关系,得到内参数矩阵 K 和畸变系数;
核心模型:理想针孔相机模型 + 径向 / 切向畸变模型;
主流方法:张正友棋盘格标定法(低成本、高精度、易实现);
实战关键:图像采集(多视角 / 多位置)、角点检测(亚像素)、重投影误差验证(<1 像素为优秀);
工程落地:标定结果保存 + 畸变矫正 + 黑边裁剪。
9.2 后续学习方向(根据兴趣拓展)
双目相机立体标定 + 三维测距;
相机与 IMU/LiDAR 的联合标定(SLAM 核心);
鱼眼相机标定(超广角镜头,畸变模型不同);
标定结果的优化(束调整 BA,提升高精度场景标定精度);
工业视觉中的标定应用(如工件尺寸检测、视觉定位)。