使用 OpenCV 进行双目相机标定的完整指南

在计算机视觉领域,双目相机标定是一个非常重要的过程,它可以帮助我们校正两台相机之间的几何关系,从而获得真实场景的深度信息。本指南将逐步带您了解如何使用 Python 和 OpenCV 来实现双目相机的标定。

一、双目相机标定的流程

在开始之前,我们需要明确整个标定过程的基本步骤。以下是流程的表格展示:

步骤 说明
1. 准备标定数据 准备一组用于标定的图像对,通常是棋盘格图案
2. 提取角点 在每对图像中找到棋盘格的角点
3. 保存角点坐标 保存世界坐标系和图像坐标系中的角点坐标
4. 执行标定 使用提取的角点坐标进行相机标定
5. 结果分析 分析和验证标定结果

二、逐步实现

1. 准备标定数据

标定过程首先需要一组棋盘格图案的图像对。您可以使用相机拍摄一组标准棋盘格图案的图像。确保图像包含棋盘格的不同角度和距离。

2. 提取角点

接下来,我们需要在这些图像中提取棋盘格的角点。下面是实现此步骤的代码示例:

import cv2
import numpy as np
import glob

# 定义棋盘格尺寸
chessboard_size = (9, 6)  # 内部角点数量,行列都是减1

# 准备对象点
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)

# 存储对象点和图像点
objpoints = []  # 3D 点
imgpoints_left = []  # 左相机的2D点
imgpoints_right = []  # 右相机的2D点

# 加载所有棋盘格图像
images_left = glob.glob('left/*.png')
images_right = glob.glob('right/*.png')

for img_left, img_right in zip(images_left, images_right):
    gray_left = cv2.cvtColor(cv2.imread(img_left), cv2.COLOR_BGR2GRAY)
    gray_right = cv2.cvtColor(cv2.imread(img_right), cv2.COLOR_BGR2GRAY)

    # 查找棋盘格角点
    ret_left, corners_left = cv2.findChessboardCorners(gray_left, chessboard_size, None)
    ret_right, corners_right = cv2.findChessboardCorners(gray_right, chessboard_size, None)

    # 如果找到角点,添加对象点和图像点
    if ret_left and ret_right:
        objpoints.append(objp)
        imgpoints_left.append(corners_left)
        imgpoints_right.append(corners_right)

print("角点提取完成")

代码说明:

  • 创建棋盘格的对象点。
  • 遍历棋盘格图像,对每对图像找出角点,并将其添加到对象点和图像点列表中。

3. 保存角点坐标

此步骤中,我们已经在上一步完成了角点的保存。

4. 执行标定

现在我们需要使用获取的角点来执行标定。以下是进行标定的代码示例:

# 标定相机
ret, mtx_left, dist_left, rvecs_left, tvecs_left = cv2.calibrateCamera(objpoints, imgpoints_left, gray_left.shape[::-1], None, None)
ret, mtx_right, dist_right, rvecs_right, tvecs_right = cv2.calibrateCamera(objpoints, imgpoints_right, gray_right.shape[::-1], None, None)

# 立体标定
flags = cv2.CALIB_FIX_INTRINSIC
ret, mtx_left, dist_left, mtx_right, dist_right, R, T, E, F = cv2.stereoCalibrate(objpoints, imgpoints_left, imgpoints_right, mtx_left, dist_left, mtx_right, dist_right, gray_left.shape[::-1], None, None, flags=flags)

print("相机标定完成")

代码说明:

  • 使用 cv2.calibrateCamera 函数进行单独的相机标定。
  • 使用 cv2.stereoCalibrate 进行双目相机的立体标定。

5. 结果分析

最后,我们可以分析标定结果,并绘制一些统计图表。以下是分析并生成饼状图的示例代码:

import matplotlib.pyplot as plt

# 计算重投影误差
def compute_reprojection_error(objpoints, imgpoints_left, imgpoints_right, mtx_left, dist_left, mtx_right, dist_right, R, T):
    total_error = 0
    for i in range(len(objpoints)):
        imgpoints2_left, _ = cv2.projectPoints(objpoints[i], rvecs_left[i], tvecs_left[i], mtx_left, dist_left)
        imgpoints2_right, _ = cv2.projectPoints(objpoints[i], rvecs_right[i], tvecs_right[i], mtx_right, dist_right)
        error_left = cv2.norm(imgpoints_left[i], imgpoints2_left, cv2.NORM_L2) / len(imgpoints2_left)
        error_right = cv2.norm(imgpoints_right[i], imgpoints2_right, cv2.NORM_L2) / len(imgpoints2_right)
        total_error += (error_left + error_right) / 2
    return total_error / len(objpoints)

error = compute_reprojection_error(objpoints, imgpoints_left, imgpoints_right, mtx_left, dist_left, mtx_right, dist_right, R, T)

# 绘制饼状图
labels = ['重投影误差', '其他']
sizes = [error, 1 - error]

plt.figure(figsize=(8, 6))
plt.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90)
plt.axis('equal')
plt.title("重投影误差分析")
plt.show()

代码说明:

  • 定义计算重投影误差的函数。
  • 绘制饼状图,显示重投影误差与其他部分的比例。

结论

通过以上步骤,您应该能够成功完成 Python OpenCV 的双目相机标定。确保在整个过程中仔细检查每一步的输出,特别是角点提取的质量。在标定后,您可以利用标定结果进行更高级的计算机视觉任务,例如深度测量和三维重建。希望本教程对您有所帮助!如果您对标定过程或计算机视觉有更多疑问,请随时联系我。