1. 张正友标定法概述相机标定是计算机视觉中最基础也最重要的任务之一。简单来说它就像给相机做体检——通过分析相机拍摄的特定图案如棋盘格计算出相机的视力参数。这些参数决定了相机如何将三维世界转换为二维图像。张正友标定法是其中最经典的方法由张正友博士在1998年提出。它的巧妙之处在于只需要一个平面棋盘格不需要昂贵的三维标定设备操作简单手持棋盘格随意移动拍摄即可精度高通过多幅图像和优化算法获得稳定结果我在实际项目中多次使用这个方法发现它的鲁棒性确实很好。即使拍摄条件不理想如光线不均匀、棋盘格部分遮挡只要采集足够多的图像建议15-20张依然能得到准确的标定结果。2. 单应性矩阵连接平面与图像的桥梁2.1 单应性矩阵的几何意义想象你拿着手机对着桌面上的棋盘格拍照。棋盘格是一个平面它在照片中的成像经历了三个关键变换棋盘格平面到相机坐标系的刚体变换旋转平移相机坐标系到图像平面的透视投影图像平面到像素坐标系的仿射变换这三个变换可以合并为一个3×3的单应性矩阵H。这个矩阵的神奇之处在于它能把棋盘格上的任意点[X,Y]直接映射到像素坐标[u,v][u] [h11 h12 h13][X] [v] [h21 h22 h23][Y] [1] [h31 h32 h33][1]我在调试时常用这个性质验证单应性矩阵是否正确——把棋盘格角点的世界坐标乘以H看是否得到对应的像素坐标。2.2 单应性矩阵的求解方法要解这个矩阵至少需要4组对应点。为什么是4组因为H有8个自由度虽然矩阵有9个元素但可以整体缩放每组点提供两个方程。实际操作中我建议采集20-30个角点。这样即使部分点检测不准依然能通过最小二乘法得到稳定的解。具体步骤检测棋盘格角点OpenCV的findChessboardCorners就很好用构建线性方程组# 对于每组点(X,Y)-(u,v) A np.array([ [X, Y, 1, 0, 0, 0, -u*X, -u*Y], [0, 0, 0, X, Y, 1, -v*X, -v*Y] ]) b np.array([u, v])用SVD分解求解Ah0的最小二乘解记得对数据做归一化我遇到过因为像素坐标和世界坐标量级差太大导致数值不稳定的情况。简单做法是将坐标平移缩放使均值为0方差为√2。3. 从单应性矩阵到相机内参3.1 内参约束的推导单应性矩阵H可以分解为 H λA[r1 r2 t] 其中A是内参矩阵[r1 r2 t]是外参λ是比例因子。利用旋转矩阵的正交性r1ᵀr20‖r1‖‖r2‖1可以得到两个关键约束 h1ᵀA⁻ᵀA⁻¹h2 0 h1ᵀA⁻ᵀA⁻¹h1 h2ᵀA⁻ᵀA⁻¹h2这些约束中出现了B A⁻ᵀA⁻¹这个矩阵特别重要。通过它我们可以绕过直接求A的困难。3.2 内参的封闭解B矩阵对称只有6个独立元素。设b[B11,B12,B22,B13,B23,B33]ᵀ每个单应性矩阵提供两个方程[v12]ᵀb 0 [(v11-v22)]ᵀb 0其中vij [hi1hj1, hi1hj2hi2hj1, hi2hj2, hi3hj1hi1hj3, hi3hj2hi2hj3, hi3hj3]ᵀ至少需要3幅图像6个方程才能解出b。我建议用5幅以上图像构建超定方程组通过SVD求解更稳定。解出B后内参矩阵A可以通过Cholesky分解得到# B A⁻ᵀA⁻¹ A_inv np.linalg.cholesky(B).T A np.linalg.inv(A_inv)4. 相机外参的求解4.1 旋转和平移的估计有了内参A和单应性H外参可以直接计算 λ 1/‖A⁻¹h1‖ r1 λA⁻¹h1 r2 λA⁻¹h2 r3 r1×r2 t λA⁻¹h3注意这样得到的R可能不严格正交。我通常会在优化前用SVD强制正交化U, _, Vt np.linalg.svd(R) R U Vt4.2 外参的实际意义每幅图像对应一组外参表示棋盘格相对于相机的姿态旋转矩阵R棋盘格相对于相机的位置平移向量t在双目视觉系统中这些外参可以用来计算两个相机之间的相对位置关系。我曾经用这个方法标定双目摄像头精度能达到0.1mm以内。5. 畸变校正让图像不再弯曲5.1 畸变模型实际镜头会有明显的畸变主要有两种径向畸变图像像鱼眼一样弯曲 x_corrected x(1 k1r² k2r⁴)切向畸变由镜头与传感器不平行引起 x_corrected [2p1xy p2(r²2x²)]张正友方法主要估计径向畸变k1,k2。对于普通镜头这已经足够但鱼眼镜头可能需要更高阶模型。5.2 畸变参数估计通过最小化重投影误差求解# 理想坐标 u_ideal u (u-u0)*(k1*r² k2*r⁴) v_ideal v (v-v0)*(k1*r² k2*r⁴)构建线性方程组Dkd其中D的每行是[(u-u0)r², (u-u0)r⁴]或[(v-v0)r², (v-v0)r⁴]d的每行是u_observed-u或v_observed-v6. 完整标定流程与实战技巧6.1 标定步骤总结打印棋盘格并固定在平整表面从不同角度拍摄15-20张照片确保棋盘格充满画面检测角点并验证所有点都被正确识别计算初始单应性矩阵求解相机内参初始值估计外参和畸变参数联合优化所有参数6.2 提高标定精度的技巧棋盘格要平整我试过用玻璃板压住纸张效果比软质棋盘格好很多覆盖整个视野移动棋盘格到图像的四个角落和中心不同倾斜角度既有正面拍摄也有大角度倾斜光照均匀避免反光和阴影影响角点检测在Python中用OpenCV实现整个流程非常方便ret, mtx, dist, rvecs, tvecs cv2.calibrateCamera( object_points, image_points, image_size, None, None)7. 常见问题与解决方案7.1 标定结果不稳定的可能原因角点检测不准确尝试调整findChessboardCorners的参数棋盘格移动不足确保有足够的旋转和平移变化数值不稳定检查数据归一化和矩阵条件数7.2 验证标定质量计算重投影误差mean_error 0 for i in range(len(object_points)): imgpoints2, _ cv2.projectPoints( object_points[i], rvecs[i], tvecs[i], mtx, dist) error cv2.norm(image_points[i], imgpoints2, cv2.NORM_L2)/len(imgpoints2) mean_error error print(平均重投影误差: {}.format(mean_error/len(object_points)))好的标定结果误差通常小于0.5像素。8. 进阶话题非传统标定板虽然棋盘格最常用但在某些场景下其他图案可能更好圆形网格圆心定位精度更高编码标记适合动态标定或多相机系统随机图案可用于无标定板的自标定我曾经在一个工业项目中尝试使用圆形网格发现重投影误差比棋盘格降低了约15%。这是因为圆心的亚像素定位比角点更准确特别是在图像模糊的情况下。