本文目录导读:

我来详细说明如何使用OpenCV进行相机标定,包含完整案例:
准备工作
安装依赖
pip install opencv-python numpy
准备标定板
- 打印棋盘格标定板(如9×6的内角点)
- 或使用显示器显示棋盘格
- 从不同角度拍摄15-20张照片
完整相机标定代码
import cv2
import numpy as np
import glob
import os
class CameraCalibration:
def __init__(self, chessboard_size=(9, 6), square_size=25):
"""
初始化相机标定器
:param chessboard_size: 棋盘格内角点数量 (列数, 行数)
:param square_size: 棋盘格方格大小(毫米)
"""
self.chessboard_size = chessboard_size
self.square_size = square_size
# 准备对象点(3D坐标)
self.object_points = [] # 世界坐标系中的3D点
self.image_points = [] # 图像坐标系中的2D点
# 棋盘格角点的世界坐标
self.objp = np.zeros((chessboard_size[0] * chessboard_size[1], 3), np.float32)
self.objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2)
self.objp *= square_size
def find_corners(self, image_paths):
"""
查找棋盘格角点
:param image_paths: 图像路径列表
:return: 成功检测到角点的图像数量
"""
successful_images = 0
for image_path in image_paths:
# 读取图像
img = cv2.imread(image_path)
if img is None:
print(f"无法读取图像: {image_path}")
continue
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 查找棋盘格角点
ret, corners = cv2.findChessboardCorners(gray,
self.chessboard_size,
None)
if ret:
# 亚像素精确化角点位置
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
corners_refined = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
# 保存点集
self.object_points.append(self.objp)
self.image_points.append(corners_refined)
# 可视化角点
cv2.drawChessboardCorners(img, self.chessboard_size,
corners_refined, ret)
cv2.imshow('Chessboard Corners', img)
cv2.waitKey(100)
successful_images += 1
print(f"成功检测: {image_path}")
else:
print(f"未能检测到棋盘格: {image_path}")
cv2.destroyAllWindows()
return successful_images
def calibrate(self, image_size):
"""
执行相机标定
:param image_size: 图像尺寸 (width, height)
:return: 标定结果
"""
if len(self.object_points) < 3:
print("至少需要3张有效图像进行标定")
return None
# 执行标定
ret, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(
self.object_points,
self.image_points,
image_size,
None,
None
)
return ret, camera_matrix, dist_coeffs, rvecs, tvecs
def evaluate_calibration(self, image_paths):
"""
评估标定结果(重投影误差)
"""
total_error = 0
for i, image_path in enumerate(image_paths):
img = cv2.imread(image_path)
if img is None:
continue
# 计算重投影错误
imgpoints2, _ = cv2.projectPoints(
self.object_points[i],
rvecs[i],
tvecs[i],
camera_matrix,
dist_coeffs
)
error = cv2.norm(self.image_points[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
total_error += error
return total_error / len(self.object_points)
def main():
"""
主函数:演示相机标定流程
"""
# 设置参数
CHESSBOARD_SIZE = (9, 6) # 棋盘格内角点数
SQUARE_SIZE = 25 # 方格大小(毫米)
# 创建标定器
calibrator = CameraCalibration(CHESSBOARD_SIZE, SQUARE_SIZE)
# 获取标定图像路径
input_dir = "calibration_images" # 存放标定图像的文件夹
print("=== 相机标定程序 ===")
print(f"棋盘格配置: {CHESSBOARD_SIZE[0]}x{CHESSBOARD_SIZE[1]} 内角点")
print(f"方格大小: {SQUARE_SIZE}mm")
print()
# 检查目录是否存在
if not os.path.exists(input_dir):
print(f"错误: 找不到目录 {input_dir}")
print("请在程序同目录下创建 'calibration_images' 文件夹")
print("并将棋盘格照片放入该文件夹")
return
# 获取所有图像
image_paths = glob.glob(os.path.join(input_dir, "*.jpg")) + \
glob.glob(os.path.join(input_dir, "*.png"))
if len(image_paths) == 0:
print("未找到图像文件,请将棋盘格照片放入文件夹")
return
print(f"找到 {len(image_paths)} 张图像")
print("开始检测棋盘格角点...")
# 查找角点
successful = calibrator.find_corners(image_paths)
print(f"\n成功检测: {successful}/{len(image_paths)} 张图像")
if successful < 3:
print("有效图像不足,请拍摄更多照片")
return
# 获取图像尺寸
sample_img = cv2.imread(image_paths[0])
image_size = (sample_img.shape[1], sample_img.shape[0])
# 执行标定
print("\n正在执行相机标定...")
results = calibrator.calibrate(image_size)
if results is None:
return
ret, camera_matrix, dist_coeffs, rvecs, tvecs = results
# 输出标定结果
print("\n=== 标定结果 ===")
print(f"重投影误差: {ret:.6f}")
print(f"\n相机内参矩阵 (K):")
print(f"{camera_matrix}")
print(f"\n畸变系数 (k1, k2, p1, p2, k3):")
print(f"{dist_coeffs.flatten()}")
# 评估标定质量
mean_error = calibrator.evaluate_calibration(image_paths[:successful])
print(f"\n平均重投影误差: {mean_error:.6f} 像素")
# 保存标定结果
save_calibration_results(camera_matrix, dist_coeffs)
# 演示畸变校正
demonstrate_undistortion(image_paths[0], camera_matrix, dist_coeffs)
print("\n标定完成!")
def save_calibration_results(camera_matrix, dist_coeffs):
"""
保存标定结果到文件
"""
np.savez("calibration_results.npz",
camera_matrix=camera_matrix,
dist_coeffs=dist_coeffs)
print("\n标定结果已保存到 calibration_results.npz")
def demonstrate_undistortion(image_path, camera_matrix, dist_coeffs):
"""
演示畸变校正效果
"""
# 读取原始图像
img = cv2.imread(image_path)
if img is None:
return
h, w = img.shape[:2]
# 计算新的相机矩阵(优化视野)
new_camera_matrix, roi = cv2.getOptimalNewCameraMatrix(
camera_matrix, dist_coeffs, (w, h), 1, (w, h))
# 畸变校正
undistorted_img = cv2.undistort(img, camera_matrix, dist_coeffs,
None, new_camera_matrix)
# 裁剪图像(去除黑色边缘)
x, y, w, h = roi
undistorted_img = undistorted_img[y:y+h, x:x+w]
# 显示结果
cv2.imshow('Original Image', img)
cv2.imshow('Undistorted Image', undistorted_img)
# 保存结果
cv2.imwrite('original.jpg', img)
cv2.imwrite('undistorted.jpg', undistorted_img)
print("原图和校正后图像已保存")
print("按任意键关闭窗口...")
cv2.waitKey(0)
cv2.destroyAllWindows()
def load_and_use_calibration():
"""
加载标定结果并应用于新图像
"""
# 加载标定结果
with np.load('calibration_results.npz') as data:
camera_matrix = data['camera_matrix']
dist_coeffs = data['dist_coeffs']
# 创建标定对象
calibrator = CameraCalibration()
# 测试图像校正
test_image_path = "test_image.jpg"
if os.path.exists(test_image_path):
img = cv2.imread(test_image_path)
if img is not None:
h, w = img.shape[:2]
new_camera_matrix, _ = cv2.getOptimalNewCameraMatrix(
camera_matrix, dist_coeffs, (w, h), 1, (w, h))
# 畸变校正
dst = cv2.undistort(img, camera_matrix, dist_coeffs, None, new_camera_matrix)
cv2.imshow('Test Image', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
使用方法
步骤1:准备标定图像
# 创建目录存放标定图像 mkdir calibration_images # 将棋盘格照片(从不同角度拍摄15-20张)放入该目录
步骤2:运行标定程序
python camera_calibration.py
步骤3:使用标定结果
# 加载保存的标定结果
import numpy as np
import cv2
# 加载标定数据
with np.load('calibration_results.npz') as data:
camera_matrix = data['camera_matrix']
dist_coeffs = data['dist_coeffs']
# 校正新图像
def undistort_image(image, camera_matrix, dist_coeffs):
h, w = image.shape[:2]
new_camera_matrix, roi = cv2.getOptimalNewCameraMatrix(
camera_matrix, dist_coeffs, (w, h), 1, (w, h))
dst = cv2.undistort(image, camera_matrix, dist_coeffs, None, new_camera_matrix)
return dst
# 使用示例
img = cv2.imread('new_image.jpg')
undistorted = undistort_image(img, camera_matrix, dist_coeffs)
关键参数说明
棋盘格配置
CHESSBOARD_SIZE = (9, 6) # 内角点数量(不是方格数量) SQUARE_SIZE = 25 # 方格实际尺寸(单位:mm)
标定质量评估指标
- 重投影误差:< 0.5 像素表示标定质量良好
- 焦距参数:fx, fy 应接近相机规格
- 主点坐标:cx, cy 应在图像中心附近
注意事项
-
图像拍摄要求:
- 尽量从不同角度拍摄
- 棋盘格要占据图像的30-50%
- 保持图像清晰,避免模糊
- 保证光照均匀
-
棋盘格要求:
- 使用平整的棋盘格
- 避免棋盘格有污渍或损坏
- 确保棋盘格在图像中完整可见
-
常见问题:
- 角点检测失败:调整光照或使用亚像素处理
- 标定结果差:增加图像数量(至少15张)
- 畸变校正不理想:检查标定参数是否保存正确
这个案例提供了完整的相机标定解决方案,可以直接使用或根据具体需求修改。