目录
一、概述
1.1标定原理
1.2实现步骤
1.3应用场景
二、代码
2.1 cv2.calibrateCamera函数
2.1.1输入参数
2.1.2输出参数
2.2完整代码
三、实现效果
3.1处理图像
3.2内参与畸变系数
一、概述
使用 OpenCV 进行相机标定,通常需要拍摄多张包含棋盘格标定板的图像,然后通过这些图像计算相机的内参和畸变系数。OpenCV中使用 cv2.calibrateCamera 使用非线性最小二乘法(如 Levenberg-Marquardt 算法)来最小化重投影误差,即实际图像点与由相机模型预测的图像点之间的误差。通过迭代优化,函数求解出相机的内参、畸变系数以及每张图像的外参。
1.1标定原理
相机标定是确定相机成像模型中的内参和外参的过程。内参(内参数)描述了相机自身的特性,而外参(外参数)描述了相机的姿态,即相机坐标系相对于世界坐标系的位置和方向。
1.2实现步骤
1.准备标定板
标定板通常是一个已知尺寸的棋盘格或圆点阵列。标定板上的每个角点或圆心位置已知,可以用作 3D 世界坐标系中的参考点。
2.拍摄多张标定板图像
从不同角度拍摄多张包含标定板的图像,以覆盖尽可能多的视角变化。这些图像将用于提取特征点,进行相机标定。
3.检测标定板角点
使用图像处理算法(如 OpenCV 的 cv2.findChessboardCorners)在每张图像中检测标定板的角点或圆心,并记录其像素坐标。
4.优化角点位置
为了提高精度,使用亚像素级别的优化方法(如 cv2.cornerSubPix)进一步优化角点的位置。
5.准备 3D-2D 点对
根据标定板的已知尺寸和角点数量,准备每张图像的 3D 世界坐标点(实际坐标)和对应的 2D 图像点(像素坐标)。
6.计算相机内参和畸变系数
使用标定算法(如 cv2.calibrateCamera)计算相机的内参矩阵和畸变系数。这一步需要将多张图像中的 3D-2D 点对输入到算法中,进行全局优化。
7.验证标定结果
通过对标定图像进行去畸变处理和重投影误差分析,验证标定结果的准确性。可以使用一些新的图像进行验证,检查标定结果是否符合预期。
1.3应用场景
1.计算机视觉和机器人
- 物体检测与识别:相机标定使得算法能够理解实际世界中的距离和比例,从而更准确地识别和定位物体。
- 机器人导航:机器人通过相机感知环境,需要知道相机的精确位置和方向,以便于路径规划和避障。
- 3D 重建:多视角相机标定是 3D 重建的基础,可以从多个视角拍摄的图像中恢复物体的 3D 形状。
2.增强现实(AR)
- 虚拟物体叠加:为了将虚拟物体准确地叠加到现实场景中,必须知道相机的内参和外参,从而正确地渲染虚拟物体。
- 实时跟踪:相机标定帮助实现实时跟踪和投影,使得虚拟内容能够随着相机移动而准确对齐。
3.工业检测
- 尺寸测量:在工业检测中,准确测量物体的尺寸和位置需要相机的精确标定。
- 缺陷检测:通过标定相机,可以从不同角度拍摄产品图像,检查产品表面是否存在缺陷。
二、代码
2.1 cv2.calibrateCamera函数
cv2.calibrateCamera 使用非线性最小二乘法(如 Levenberg-Marquardt 算法)来最小化重投影误差,即实际图像点与由相机模型预测的图像点之间的误差。通过迭代优化,函数求解出相机的内参、畸变系数以及每张图像的外参。
retval, cameraMatrix, distCoeffs, rvecs, tvecs = cv2.calibrateCamera(objectPoints, imagePoints, imageSize[, cameraMatrix[, distCoeffs[, rvecs[, tvecs[, flags[, criteria]]]]]])
2.1.1输入参数
1.objectPoints:
- 类型:list 或 np.ndarray
- 形状:(N, 1, 3) 或 (N, 3),其中 N 是 3D 点的数量。
- 描述:世界坐标系中的 3D 点集,每张图像一组。通常是标定板上的特征点在实际空间中的位置。
示例:对于 10 张图像,每张图像有 54 个角点,objectPoints 的形状为 [10, 54, 3]。
2.imagePoints:
- 类型:list 或 np.ndarray
- 形状:(N, 1, 2) 或 (N, 2),其中 N 是 2D 点的数量。
- 描述:图像坐标系中的 2D 点集,每张图像一组。通常是从图像中检测到的标定板的特征点的像素坐标。
示例:对于 10 张图像,每张图像有 54 个角点,imagePoints 的形状为 [10, 54, 2]。
3.imageSize:
- 类型:tuple
- 形状:(width, height)
- 描述:图像的大小,以像素为单位。
4.cameraMatrix(可选):
- 类型:np.ndarray
- 形状:(3, 3)
- 描述:初始相机内参矩阵。如果提供,函数将使用它作为初始估计,否则使用默认值。
5.distCoeffs(可选):
- 类型:np.ndarray
- 形状:(5, 1) 或 (8, 1)
- 描述:初始畸变系数。如果提供,函数将使用它作为初始估计,否则使用默认值。
6.rvecs(可选):
- 类型:list
- 形状:(N, 1, 3) 或 (N, 3)
- 描述:每张图像的初始旋转向量。如果提供,函数将使用它作为初始估计。
7.tvecs(可选):
- 类型:list
- 形状:(N, 1, 3) 或 (N, 3)
- 描述:每张图像的初始平移向量。如果提供,函数将使用它作为初始估计。
8.flags(可选):
- 类型:整数
- 描述:标定方法的标志。常用的标志包括:
- cv2.CALIB_USE_INTRINSIC_GUESS:使用提供的内参矩阵作为初始估计。
- cv2.CALIB_FIX_PRINCIPAL_POINT:固定主点(光学中心)。
- cv2.CALIB_FIX_ASPECT_RATIO:固定焦距的纵横比。
- cv2.CALIB_ZERO_TANGENT_DIST:假设切向畸变为零。
9.criteria(可选):
- 类型:tuple
- 描述:终止条件,表示迭代的最大次数和所需的最小精度。通常为 (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 1e-6)。
2.1.2输出参数
1.etval:
- 类型:浮点数
- 描述:标定的总重投影误差,表示优化过程的结果。
2.cameraMatrix:
- 类型:np.ndarray
- 形状:(3, 3)
- 描述:计算得到的相机内参矩阵。
3.distCoeffs:
- 类型:np.ndarray
- 形状:(5, 1) 或 (8, 1)
- 描述:计算得到的相机畸变系数。
4.rvecs:
- 类型:list
- 形状:(N, 1, 3) 或 (N, 3)
- 描述:每张图像的旋转向量。
5.tvecs:
- 类型:list
- 形状:(N, 1, 3) 或 (N, 3)
- 描述:每张图像的平移向量。
2.2完整代码
import cv2
import numpy as np
import glob
# 设置棋盘格参数
chessboard_size = (11, 8) # 棋盘格的内角点个数
square_size = 1.0 # 棋盘格每个方格的实际大小,单位可以是毫米、厘米或米
# 准备棋盘格的世界坐标系坐标(假设z=0)
objp = np.zeros((chessboard_size[0] * chessboard_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2)
objp *= square_size
# 用于存储所有图像的世界坐标系点和图像坐标系点
objpoints = [] # 世界坐标系中的点
imgpoints = [] # 图像坐标系中的点
# 获取所有棋盘格图像的路径
images = glob.glob('board_image/*.png')
for image_path in images:
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 检测棋盘格角点
ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)
if ret:
objpoints.append(objp)
imgpoints.append(corners)
# 绘制角点以进行可视化
img = cv2.drawChessboardCorners(img, chessboard_size, corners, ret)
cv2.imshow('Chessboard Corners', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 执行相机标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
# 输出相机的内参和畸变系数
print("Camera matrix:\n", mtx)
print("Distortion coefficients:\n", dist)
# 保存内参和畸变系数到文件
np.savez('camera_calibration.npz', mtx=mtx, dist=dist, rvecs=rvecs, tvecs=tvecs)
# 可选:验证标定结果
for image_path in images:
img = cv2.imread(image_path)
h, w = img.shape[:2]
# 计算新的相机矩阵和有效像素区域
# newcameramtx: 新的相机矩阵
# roi: 有效像素区域(区域内的像素可以保证无畸变)
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
# 使用新的相机矩阵 undistort 方法校正畸变图像
# mtx: 原始相机矩阵
# dist: 畸变系数
# newcameramtx: 新的相机矩阵
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
# # 获取有效像素区域
x, y, w, h = roi
# 裁剪图像,只保留有效像素区域
dst = dst[y:y + h, x:x + w]
cv2.imshow('Undistorted Image', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 加载 .npz 文件
data = np.load('camera_calibration.npz')
print('测试')
print(data['mtx'])
print(data['dist'])
三、实现效果
3.1处理图像
3.2内参与畸变系数
Camera matrix:
[[5.78450231e+03 0.00000000e+00 8.92032508e+02]
[0.00000000e+00 5.57232763e+03 1.07114110e+03]
[0.00000000e+00 0.00000000e+00 1.00000000e+00]]
Distortion coefficients:
[[ 3.13251587e-01 1.75995346e+00 1.34946542e-02 -1.23926024e-02
-5.43635102e+01]]