资讯动态

张正友标定法:棋盘格、内参畸变与OpenCV实践一次讲清

发布时间:2026/10/15 12:25:40 来源:尧图企业网站定制
简介这是一份围绕张正友标定法整理的MATLAB实现资料包面向计算机视觉初学者、课程设计与科研人员解决摄像机标定中相机内参、外参与畸变系数的估计问题。压缩包共27个文件、仅85KB以10个.m脚本为主体涵盖单应矩阵计算、坐标归一化、旋转测试等关键环节另有9个txt数据与说明文件、7个.asv自动保存备份以及1篇CAJ格式原始论文《基于单平面模板的摄像机定标研究》。目前已有502人学习下载配套脚本与数据可让读者从棋盘格角点检测开始逐步推演内参矩阵、外参矩阵与畸变校正的完整求解流程对照论文公式即可快速复现实验并扩展至自己的图像数据上。体积小巧且目录结构清晰适合直接打开调试是理解张正友标定法底层原理的实用入门资料。1. 张正友标定法到底是什么一张棋盘格照片就能让相机认识自己你有没有想过手机拍照时明明镜头是圆的拍出来的直线却不会弯曲自动驾驶汽车判断障碍物距离靠的又是哪双眼睛这一切背后都藏着一个看似朴素却极其关键的步骤——相机标定。通俗地说标定就是让相机认识自己它得知道自己镜头的焦距是多少、画面中心在哪、成像有没有变形。在众多标定方法里张正友标定法因为一张棋盘格就够了的极致简单成了计算机视觉领域应用最广的入门必修课。它由学者张正友在1998年提出也叫张氏标定法既不需要昂贵的精密设备又能达到不错的精度像极了做菜时不用高汤用清水也能炖出一锅好汤的智慧。2. 相机为什么会看歪从针孔模型到畸变的麻烦2.1 从针孔模型说起相机是怎么看见世界的想象一个暗盒前面戳一个针尖大小的小孔外面的光线穿过小孔在盒子内部倒映出外面的景象——这就是针孔相机模型也是所有相机标定理论的起点。在这个理想模型中世界上的一个点、针孔中心、成像平面上的一个点三者永远在一条直线上。这条共线关系就是标定要守住的基本盘。但现实中的相机不是针孔。手机镜头由好几片透镜组成光线穿过透镜会发生折射导致世界的点、光心、成像点不再严格共线。于是我们需要给这个理想模型加一些补丁参数内参焦距、主点坐标、像素尺寸描述相机自身的几何结构外参旋转矩阵和平移向量描述相机在世界坐标系中的位置和朝向。张正友标定法要做的就是通过拍摄已知尺寸的棋盘格把这两类参数反解出来。2.2 畸变的真面目桶形、枕形和切向畸变光线经过透镜时边缘区域和中心区域的折射程度不同画面就会鼓起来或凹下去这叫径向畸变——桶形畸变看起来像画面从中心向外膨胀枕形畸变则像四角被向外拉扯。除此之外镜头安装做不到绝对平行于成像平面还会产生切向畸变画面像被人从侧面轻轻拧了一把。畸变参数一般用 k1、k2径向和 p1、p2切向来建模有些标定算法还会引入 k3 来应对强畸变的广角镜头。张正友标定法正是把畸变系数也放进优化目标里让相机在标定完成后能把畸变校正回来。2.3 为什么不能拿厂商参数直接用你可能会想相机出厂时不是给过参数吗问题在于镜头是批量生产的每一颗镜头的焦距、畸变都存在细微差异而且相机跌落、温度变化都会让参数漂移。更关键的是我们的应用场景——比如机器人导航、三维重建、AR叠加——对空间关系的精度要求是毫米级的厂商给的那组平均参数根本不够用。所以每次安装好相机或者换了镜头都得重新标定一次。3. 张正友标定法的核心套路拍照、找角点、算矩阵3.1 第一步打印一张棋盘格张正友标定法最友好的地方就是它只需要一张打印出来的棋盘格黑白相间的方格图案贴在硬纸板上就算制作了标定板。为什么用棋盘格因为黑白方格的交界处——角点——有极其明确的像素坐标计算机可以自动、稳定地检测到它们。棋盘格的方格边长必须精确测量比如 30 毫米因为它是整个标定过程中的世界尺度换句话说所有计算出来的相机参数都以这个边长作为真实长度的基准。测量不准后面全白搭这一步也成了很多同学第一次标定时最容易忽略的坑。3.2 第二步从多个角度拍摄 1020 张照片拿着棋盘格让它以不同角度、不同远近、不同位置出现在画面里拍摄 10 到 20 张照片。要求是棋盘格必须完整出现在画面中角度变化要丰富——左右倾斜、上下倾斜、前后移动都得有。为什么要这么麻烦因为单个角度的照片只能给出一个二维平面到图像的映射关系多个角度才能把相机内参的真实形状从不同侧面逼问出来。拍摄的关键细节包括光照均匀避免反光不能使用自动对焦要锁定焦距否则每张照片的焦距都在变标定结果会互相矛盾棋盘格尽量占画面的 1/3 以上太小了角点检测容易出错。3.3 第三步让程序自动检测角点拍摄完成后进入算法部分。以 OpenCV 为例最核心的调用是cv2.findChessboardCorners()它会返回棋盘格所有内角点的像素坐标。这个函数的工作逻辑并不神秘先对图像做二值化和形态学处理找到候选的黑色方块再用四边形匹配和亚像素细化把角点坐标精确定位到亚像素级别。import cv2 import numpy as np # 棋盘格规格内角点数量比如 9x6 的格子内角点是 8x5 pattern_size (8, 5) # 每个格子的实际边长单位毫米 square_size 30.0 # 准备世界坐标系中的棋盘格角点坐标z 恒为 0 objp np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32) objp[:, :2] np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2) objp * square_size # 遍历图片检测角点并保存坐标 obj_points [] # 世界坐标系中的点 img_points [] # 图像坐标系中的点 for fname in image_files: img cv2.imread(fname) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) found, corners cv2.findChessboardCorners(gray, pattern_size, None) if found: # 亚像素级别精确定位 criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners2 cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria) obj_points.append(objp) img_points.append(corners2)这里有两个关键参数pattern_size是棋盘格内角点的行列数注意不是格子数square_size是世界坐标系的比例尺不同单位毫米、厘米、米只影响平移向量的尺度不影响内参。cornerSubPix做的是亚像素细化让角点精度从像素级提升到亚像素级这对最终的标定精度影响显著。3.4 第四步解出内参、外参和畸变系数检测到足够的角点对后核心算法分三步走先用闭合解直接线性变换法粗略估计内外参数再用最大似然估计做非线性优化最后用最小二乘方法计算畸变系数。张正友标定法的一个精妙之处在于它利用棋盘格平面在世界坐标系中 z 恒为 0这一特点把相机成像方程从一个复杂的 3D 到 2D 投影简化成了一个平面到平面的单应性变换Homography。每个角度下的棋盘格都能算出一个单应性矩阵积累足够多的单应性矩阵后内参就能通过解线性方程组解出来精细打磨则交给非线性优化。# OpenCV 中一步完成标定 ret, mtx, dist, rvecs, tvecs cv2.calibrateCamera( obj_points, img_points, gray.shape[::-1], None, None ) # mtx 是内参矩阵dist 是畸变系数 print(内参矩阵:\n, mtx) print(畸变系数:, dist.ravel())calibrateCamera的入参是前面准备好的世界坐标点、图像坐标点和图像尺寸返回的mtx是 3x3 的内参矩阵含焦距和主点dist是畸变系数向量rvecs和tvecs则保存了每一张照片对应的外参。这一行代码背后就是张正友标定法从单应性矩阵到参数分解的全部数学推导。4. 深入标定结果的内部如何读懂内参、外参和误差报告4.1 内参矩阵里的四个数字意味着什么内参矩阵看起来是一堆数字其实只包含四个有物理意义的变量fx、fy 是焦距在 x、y 方向的像素度量cx、cy 是主点光轴穿过成像平面的交点的像素坐标。理想情况下fx 和 fy 应当相等主点应当位于画面中心但由于制造误差它们总会有些偏差。内参矩阵 K [fx 0 cx] [ 0 fy cy] [ 0 0 1]fx 可以直观理解为相机焦距毫米× 像素密度像素/毫米所以同一颗镜头传感器像素越小fx 数值越大。读懂这四个数你就能判断相机是否存在明显的装配误差以及三维重建时的尺度是否可靠。4.2 重投影误差标定结果好不好的体检报告标定完成后如何判断结果靠不靠谱靠重投影误差Reprojection Error。原理很简单用标定出来的参数把世界坐标系中的棋盘格角点重新投影回图像上计算投影点和实际检测到的角点之间的像素距离这个距离的平均值就是重投影误差。# 计算重投影误差 total_error 0 for i in range(len(obj_points)): imgpoints2, _ cv2.projectPoints(obj_points[i], rvecs[i], tvecs[i], mtx, dist) error cv2.norm(img_points[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2) total_error error print(平均重投影误差:, total_error / len(obj_points))通常重投影误差小于 0.1 像素就算标定质量优秀0.10.3 像素可以用超过 0.5 像素则说明有照片出了问题——可能是棋盘格反光、拍摄时抖动或者某张照片的棋盘格角度太单一。不少新手拿到标定结果不看这个误差就直接用结果三维重建时偏差好几厘米回头查才发现是标定没做好。4.3 图像去畸变标定之后立刻能做的一件事标定得到的畸变系数最直接的应用就是去畸变。OpenCV 提供了两种方式cv2.undistort()一步到位但需要对整幅图像做重映射计算量大cv2.initUndistortRectifyMap()则是先算好映射表之后每帧只需查表重映射性能更高适合实时视频流处理。# 生成映射表适合视频流实时去畸变 newcameramtx, roi cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h)) mapx, mapy cv2.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w, h), cv2.CV_32FC1) dst cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)getOptimalNewCameraMatrix里那个1是自由缩放比例因子设为 1 表示保持全部像素设为 0 则会裁掉边缘畸变严重的区域但分辨率损失会明显。实际项目中如果对画面视野要求高通常设 1 然后手动裁剪如果边缘畸变本身干扰视觉可以用 0。4.4 标定结果为什么会有波动照片数量和角度分布的影响标定结果不是一次拍完就固定的它对输入数据很敏感。照片数量少少于 10 张时方程约束不足解出来的内参往往在多次尝试之间跳来跳去角度分布太集中比如全是正对棋盘格拍摄单应性矩阵之间的差异太小内参的可辨识度就会下降。一个可行的经验法则是拍摄 15 张以上照片让棋盘格在画面中的倾角覆盖 -30° 到 30° 的范围并且远近位置拉开差距。标定完成后再拍一组不同角度的照片作为验证集算一次重投影误差如果验证误差远高于训练误差说明标定过拟合了——侧面遮盖了某个角度范围的样本太稀缺。5. 张正友标定法与其它标定方法的区别三大流派怎么选5.1 传统标定法 vs 张正友法高精度与便捷性的取舍传统标定法以使用精密加工的三维标定块为代表把一块带有精确刻度图案的立方体放在相机前拍摄一次就能完成标定。它的优点是只需拍摄一张照片、精度极高缺点同样明显标定块造价昂贵动辄数千元而且对摆放位置要求苛刻一旦标定块的加工精度不够或者位置偏移误差立刻放大。张正友法把三维标定块换成了一张二维棋盘格用多次拍摄替代单次拍摄。代价是精度稍逊于顶级的三维标定块标定但成本低到可以忽略操作难度也大幅下降。对大多数应用场景来说张正友法的精度重投影误差可做到 0.050.15 像素已经足够满足视觉测量、定位和重建的需求。5.2 自标定 vs 张正友法没有标定板但也要付出代价自标定Self-Calibration是另一条路线完全不需要标定物只凭相机运动过程中多帧图像之间的对应关系来求解参数。它听起来很酷但也有明显的短板——算法病态性强对图像特征的数量和质量要求很高在场景纹理不够丰富时不仅精度不稳定甚至可能完全无法收敛。张正友法本质上是一个介于传统与自标定之间的方法它用棋盘格提供了可靠的约束又不像三维标定块那样要求精密设备。这也是它的历史地位——它让标定从高端实验室的专用操作变成了普通开发者也能随手完成的常规步骤。5.3 OpenCV 标定与 MATLAB 标定工具箱的区别实际动手时OpenCV 和 MATLAB 是最常用的两个工具。OpenCV 的标定完全基于代码灵活性高适合嵌入到自动化流程里MATLAB 的 Calibration Toolbox 提供图形界面每一步都有可视化反馈角点检测结果、误差分布图更适合教学和一次性标定。两者的算法内核都是张正友标定法差别主要体现在参数输出的组织方式和错误诊断的便捷程度上。MATLAB 会输出每张照片的外参三维示意图帮你看清棋盘格在空间中的姿态这个功能在排查拍摄角度问题时特别实用OpenCV 则需要自己写代码画出来。6. 张正友标定法误区与易混概念三个新手最容易栽的跟头6.1 误区一把世界坐标系的单位当成米结果重建出来全是歪的这是最典型的翻车现场。很多同学把棋盘格边长填成 0.03米但测量时是用直尺量的毫米值 30两者差了 1000 倍。外参的平移向量会跟着缩放二维图像坐标不受影响看起来标定误差很小但输出给三维重建模块时单位错乱导致重建的物体尺寸全部错误。为什么混因为棋盘格角点的世界坐标既可以写成毫米也可以写成米算法本身不关心单位它只处理数值。怎么区分牢记你填什么单位标定结果就以什么单位解释并在注释里显式写明单位——内参不受影响但外参平移量直接依赖这个选择。6.2 误区二自动对焦不关每张照片的焦距都在变拍摄时如果开了自动对焦相机每次对焦都会微调镜头位置等效焦距跟着变化。张正友标定法假设所有照片共享同一组内参这个假设一旦被破坏标定算法会把焦距的变化混入畸变系数甚至外参里最终结果不稳定。为什么混手机相机默认自动对焦很多人完全没意识到拍摄过程中焦距已经变了。怎么区分拍摄前在相机 App 里长按锁定对焦或者切换到专业模式手动设定焦距工业相机则设置 fixed focus。判断方法很简单标定多次如果内参矩阵忽大忽小八成就是对焦没锁住。6.3 误区三内参矩阵中的 fx 和 fy 直接当成物理焦距其实它是像素焦距fx 的单位是像素不是毫米。如果要换算成物理焦距需要 fx 除以传感器每毫米的像素数。这看起来是一个知识性细节但确实有很多做三维重建的初学者把 fx 当作物理焦距用来计算视角大小结果算出来的视场角偏得离谱。为什么混名称里带f容易被当成焦距真值。怎么区分记住公式 f_mm fx / (pixel_size_mm)或者直接用 fx 和图像宽度计算水平视场角FOV 2 * arctan(width / (2 * fx))这样既不用换单位也不容易混淆。7. 什么时候该用张正友标定法边界、坑位和一句建议张正友标定法的适用边界其实很清楚。适合它的情况单目相机标定、相机内参未知且不允许使用昂贵设备、需要把多个相机标定到统一坐标、或者只是想快速给相机做一个能用的去畸变配置。不适合它的情况对精度有毫米级甚至亚毫米级要求且预算充足这种情况应该上三维标定块或者完全无法放置标定板的场景这时只能用自标定但要接受精度波动再或者鱼眼镜头这类超广角强畸变镜头标准针孔模型加上 k1/k2/k3 可能不够需要专门的全向相机标定模型。具体到三维重建项目里我的建议是把张正友标定法当成默认起点然后用重投影误差作为验收标准。误差达标就放心用误差超标优先排查拍摄质量而不是急着换算法。这套方法最让我服气的地方在于它的性价比——纸板和打印机换来的精度放在二十年前是需要几万元设备才能做到的事。作为一个做过几年视觉开发的普通工程师我每次拿到新的摄像头都会强制自己走一遍完整的标定流程锁对焦、拍 15 张、检查重投影误差、保存映射表。有一次偷懒拿了上一个相机的标定结果直接换到新摄像头结果测距偏差直接到了 8 厘米从那以后我再也不敢跳步骤了。张正友标定法并不复杂但它的价值恰恰藏在那些看起来不重要的细节里单位、角点、对焦、误差报告每一个都算数。希望这篇梳理能帮你把标定这块硬骨头啃下来少走几段弯路一次就把标定做明白。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑