张正友相机标定法确定相机内参

发布时间:2026/7/30 10:48:32
张正友相机标定法确定相机内参 张正友相机标定法确定相机内参一、引言什么是相机标定在学习计算机视觉或机器人视觉时我们经常会遇到一个问题如何让计算机理解图像中的物体在真实世界中的位置相机标定就是解决这个问题的第一步。简单来说相机标定是确定相机内部几何参数内参和外部位置姿态外参的过程。其中内参描述了相机的光学特性比如焦距、主点位置和畸变系数。想象一下你拍了一张照片照片里的一个点对应真实世界中的一个点。相机标定就是建立一个数学关系将图像坐标系中的点映射到世界坐标系中的点。张正友标定法是目前最流行的标定方法之一因为它只需要一个简单的棋盘格图案操作方便且精度高。## 二、基础概念相机模型与坐标系### 2.1 针孔相机模型在深入张正友方法之前我们需要理解针孔相机模型。这是最基础的相机模型假设光线通过一个小孔投射到成像平面上。相机内参通常用一个3×3的矩阵表示K [fx 0 cx] [0 fy cy] [0 0 1]其中-fx和fy是焦距以像素为单位-cx和cy是主点坐标图像中心### 2.2 坐标系变换在标定过程中我们涉及四个坐标系1.世界坐标系真实物体的位置2.相机坐标系以相机为中心3.图像坐标系在成像平面上4.像素坐标系在数字图像中一个世界点P (X, Y, Z)投影到图像点p (u, v)的过程可以用公式表示s * [u, v, 1]^T K * [R|t] * [X, Y, Z, 1]^T其中R是旋转矩阵t是平移向量s是尺度因子。## 三、张正友标定法原理张正友标定法的核心思想是使用一个平面棋盘格作为标定物通过拍摄多张不同角度的图像求解相机内参和外参。### 3.1 为什么用棋盘格棋盘格具有明显的角点便于自动检测。每个格子的大小已知因此我们可以知道棋盘格上角点的世界坐标。通过提取图像中的角点坐标我们可以建立对应关系。### 3.2 基本步骤1.准备棋盘格打印一个棋盘格图案测量每个格子的大小2.拍摄多张图像从不同角度拍摄棋盘格照片至少3张建议10-20张3.提取角点使用图像处理算法找到棋盘格角点4.计算单应性矩阵对于每张图像计算世界平面到图像平面的单应性矩阵H5.求解内参利用单应性矩阵的约束条件求解内参矩阵K6.计算外参基于内参和单应性矩阵计算每张图像的旋转和平移7.优化参数使用非线性优化如Levenberg-Marquardt最小化重投影误差## 四、从单应性矩阵到内参### 4.1 单应性矩阵单应性矩阵H是一个3×3矩阵描述了两个平面之间的映射关系。在标定中它连接了世界平面Z0和图像平面。对于一张图像我们有s * [u, v, 1]^T K * [r1, r2, t] * [X, Y, 1]^T其中r1和r2是旋转矩阵的前两列。因此单应性矩阵H K * [r1, r2, t]。### 4.2 内参约束由于r1和r2是正交单位向量我们可以得到两个约束条件h1^T * K^{-T} * K^{-1} * h2 0h1^T * K^{-T} * K^{-1} * h1 h2^T * K^{-T} * K^{-1} * h2其中h1和h2是H的前两列。通过多张图像我们可以解出K^{-T} * K^{-1}进而得到内参。## 五、完整代码实现下面我们用Python实现一个简化的张正友标定流程。注意实际应用中建议使用OpenCV的calibrateCamera函数但为了教学我们手动实现核心步骤。### 5.1 生成虚拟棋盘格数据pythonimport numpy as npimport cv2import matplotlib.pyplot as plt# 设置棋盘格参数pattern_size (9, 6) # 内角点数量列行square_size 0.025 # 每个格子的大小米# 生成世界坐标系的角点位置假设棋盘格在Z0平面world_points np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32)world_points[:, :2] np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2) * square_size# 模拟相机内参真实值K_true np.array([[800, 0, 320], [0, 800, 240], [0, 0, 1]], dtypenp.float64)# 生成3个不同的视角旋转和平移poses [ (np.array([1, 0, 0, 0, 1, 0, 0, 0, 1], dtypenp.float64).reshape(3,3), np.array([0, 0, 0.5], dtypenp.float64)), # 正面 (np.array([0.9, 0, 0.1, 0, 1, 0, -0.1, 0, 0.9], dtypenp.float64).reshape(3,3), np.array([0.1, 0, 0.4], dtypenp.float64)), # 稍微旋转 (np.array([1, 0.1, 0, -0.1, 1, 0, 0, 0, 1], dtypenp.float64).reshape(3,3), np.array([0, 0.1, 0.3], dtypenp.float64)) # 另一个角度]print(模拟生成棋盘格图像...)image_points_list []for R, t in poses: # 投影世界点到图像坐标 rvec, _ cv2.Rodrigues(R) # 旋转向量 projected_points, _ cv2.projectPoints(world_points, rvec, t, K_true, distCoeffsNone) # 添加一些噪声模拟真实情况 noise np.random.normal(0, 0.5, projected_points.shape) projected_points noise image_points_list.append(projected_points.reshape(-1, 2))print(f生成了 {len(image_points_list)} 张图像的角点数据)### 5.2 实现标定算法pythondef calibrate_camera(image_points_list, world_points, image_size): 张正友标定法实现 参数: image_points_list: 每张图像检测到的角点坐标列表 world_points: 世界坐标系中的角点坐标Nx3 image_size: 图像尺寸 (width, height) 返回: K: 内参矩阵 dist_coeffs: 畸变系数这里假设无畸变 num_images len(image_points_list) num_points world_points.shape[0] # 步骤1计算每张图像的单应性矩阵 H_list [] for image_points in image_points_list: # 使用OpenCV的findHomography实际应用中更稳健 H, _ cv2.findHomography(world_points[:, :2], image_points) H_list.append(H) # 步骤2构建约束方程求解内参 # 定义 V 矩阵2N x 6 V np.zeros((2 * num_images, 6), dtypenp.float64) for i, H in enumerate(H_list): h1 H[:, 0] # 第一列 h2 H[:, 1] # 第二列 h3 H[:, 2] # 第三列 # 计算 v12, v11, v22 v12 np.array([ h1[0]*h2[0], h1[0]*h2[1] h1[1]*h2[0], h1[1]*h2[1], h1[2]*h2[0] h1[0]*h2[2], h1[2]*h2[1] h1[1]*h2[2], h1[2]*h2[2] ]) v11 np.array([ h1[0]*h1[0], 2*h1[0]*h1[1], h1[1]*h1[1], 2*h1[0]*h1[2], 2*h1[1]*h1[2], h1[2]*h1[2] ]) v22 np.array([ h2[0]*h2[0], 2*h2[0]*h2[1], h2[1]*h2[1], 2*h2[0]*h2[2], 2*h2[1]*h2[2], h2[2]*h2[2] ]) V[2*i, :] v12.T V[2*i1, :] (v11 - v22).T # 解 V * b 0b是6维向量 _, _, Vt np.linalg.svd(V) b Vt[-1, :] # 最小特征值对应的特征向量 # 从b中恢复内参 B11, B12, B22, B13, B23, B33 b v0 (B12*B13 - B11*B23) / (B11*B22 - B12**2) lambda_ B33 - (B13**2 v0*(B12*B13 - B11*B23)) / B11 alpha np.sqrt(lambda_ / B11) beta np.sqrt(lambda_ * B11 / (B11*B22 - B12**2)) gamma -B12 * alpha**2 * beta / lambda_ u0 gamma * v0 / beta - B13 * alpha**2 / lambda_ K np.array([[alpha, gamma, u0], [0, beta, v0], [0, 0, 1]], dtypenp.float64) # 简化处理假设无畸变 dist_coeffs np.zeros((5, 1), dtypenp.float64) return K, dist_coeffs# 执行标定image_size (640, 480)K_estimated, _ calibrate_camera(image_points_list, world_points, image_size)print(真实内参矩阵)print(K_true)print(\n估计的内参矩阵)print(np.round(K_estimated, 2))# 计算误差error np.abs(K_estimated - K_true)print(f\n估计误差\n{np.round(error, 2)})## 六、高级用法畸变校正与优化### 6.1 镜头畸变实际相机镜头存在径向畸变和切向畸变。径向畸变表现为图像边缘的弯曲切向畸变是由于镜头与成像平面不平行。张正友方法可以同时估计畸变系数。### 6.2 使用OpenCV的完整标定OpenCV提供了完整的张正友标定实现包括自动角点检测和优化。以下是实际使用示例pythonimport cv2import numpy as npimport glob# 准备棋盘格参数pattern_size (9, 6)square_size 0.025 # 米# 准备世界坐标world_points np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32)world_points[:, :2] np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2) * square_size# 存储所有图像的点obj_points [] # 世界坐标系中的点每张图像相同img_points [] # 图像坐标系中的点# 读取棋盘格图像images glob.glob(calibration_images/*.jpg) # 假设你有这些图像for fname in images: img cv2.imread(fname) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 查找棋盘格角点 ret, corners cv2.findChessboardCorners(gray, pattern_size, None) if ret: obj_points.append(world_points) # 亚像素精确化角点位置 criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners2 cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) img_points.append(corners2) # 可视化角点 cv2.drawChessboardCorners(img, pattern_size, corners2, ret) cv2.imshow(Corners, img) cv2.waitKey(500)cv2.destroyAllWindows()# 执行标定ret, K, dist_coeffs, rvecs, tvecs cv2.calibrateCamera( obj_points, img_points, gray.shape[::-1], None, None)print(标定完成)print(内参矩阵 K)print(K)print(\n畸变系数 (k1, k2, p1, p2, k3))print(dist_coeffs)# 评估标定质量mean_error 0for i in range(len(obj_points)): img_points2, _ cv2.projectPoints(obj_points[i], rvecs[i], tvecs[i], K, dist_coeffs) error cv2.norm(img_points[i], img_points2, cv2.NORM_L2) / len(img_points2) mean_error errorprint(f\n平均重投影误差: {mean_error / len(obj_points):.4f} 像素)## 七、总结张正友相机标定法是一个优雅且实用的方法它将复杂的相机标定问题转化为一个可解的数学问题。通过使用平面棋盘格作为标定物我们能够1.简化对应关系棋盘格角点易于检测且精度高2.求解内参利用单应性矩阵的正交约束3.优化参数通过重投影误差最小化提高精度在实际应用中建议拍摄15-20张不同角度的图像覆盖图像的大部分区域。标定完成后务必检查重投影误差——通常小于0.5像素表示标定质量良好。掌握了张正友标定法你就迈出了计算机视觉的重要一步。无论是构建3D重建系统、实现增强现实应用还是进行视觉SLAM准确的相机内参都是不可或缺的基础。继续探索你会发现视觉世界的更多奥秘