在双目视觉中,标定摄像机是一个非常重要的步骤。本文将展示如何用Python和OpenCV对双目相机进行标定。
首先,需要准备标定图片,将分别从左侧和右侧拍摄多张不同角度的标定图片,并把它们存储在两个不同的文件夹中。接下来,需要定义一个棋盘格参数,这里我使用了一个8x11格的黑白棋盘格,每个方块大小为15毫米。
接着,使用OpenCV读取所有的标定图片,并检测棋盘格角点。使用cornerSubPix函数进行亚像素级别的角点检测,以得到更加准确的角点坐标。如果左右图像的角点都被检测到,则将角点存储到对应的列表中。
存储完角点之后,使用cv2.calibrateCamera函数计算摄像机的内参数矩阵和畸变系数。需要注意的是,需要分别对左右摄像机进行标定。有了左右摄像机的内参数矩阵和畸变系数之后,接下来就可以进行双目相机的标定了。
使用cv2.stereoCalibrate函数来标定双目相机。在此函数中,需要传递左右摄像机的内参数矩阵和畸变系数,以及之前存储的角点坐标。函数将返回一些重要的参数供后续使用,比如旋转矩阵、平移矩阵、本质矩阵、基础矩阵等等。
最后,打印出计算出来的一些重要参数的值,比如左右摄像机的内参数矩阵、畸变系数、旋转矩阵、平移矩阵、本质矩阵和基础矩阵。
import numpy as np
import cv2
import os
# 获取标定图片路径
path_left = "./data/left/"
path_right = "./data/right/"
# 定义棋盘格参数
CHESSBOARD_SIZE = (8, 11)
CHESSBOARD_SQUARE_SIZE = 15 # mm
# 获取图片列表
img_list_left = sorted(os.listdir(path_left))
img_list_right = sorted(os.listdir(path_right))
# 定义物理坐标系下的角点坐标
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) * CHESSBOARD_SQUARE_SIZE
# 定义检测角点的终止准则
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 定义用于存储角点坐标的列表
obj_points = []
img_points_left = []
img_points_right = []
# 读取标定图片并检测角点
for i in range(len(img_list_left)):
img_l = cv2.imread(path_left + img_list_left[i])
print(img_list_left[i])
img_r = cv2.imread(path_right + img_list_right[i])
gray_l = cv2.cvtColor(img_l, cv2.COLOR_BGR2GRAY)
gray_r = cv2.cvtColor(img_r, cv2.COLOR_BGR2GRAY)
# 检测角点
ret_l, corners_l = cv2.findChessboardCorners(gray_l, CHESSBOARD_SIZE, None)
ret_r, corners_r = cv2.findChessboardCorners(gray_r, CHESSBOARD_SIZE, None)
# 如果左右图像的角点都被检测到,则将角点存储到对应的列表中
if ret_l and ret_r:
obj_points.append(objp)
# 通过亚像素级别的角点检测得到更准确的角点坐标
cv2.cornerSubPix(gray_l, corners_l, (11, 11), (-1, -1), criteria)
img_points_left.append(corners_l)
cv2.drawChessboardCorners(img_l, CHESSBOARD_SIZE, corners_l, ret_l)
cv2.imshow("Chessboard Corners - Left", cv2.resize(img_l,(img_l.shape[1]//2,img_l.shape[0]//2)))
cv2.waitKey(50)
cv2.cornerSubPix(gray_r, corners_r, (11, 11), (-1, -1), criteria)
img_points_right.append(corners_r)
cv2.drawChessboardCorners(img_r, CHESSBOARD_SIZE, corners_r, ret_r)
cv2.imshow("Chessboard Corners - Right", cv2.resize(img_r,(img_r.shape[1]//2,img_r.shape[0]//2)))
cv2.waitKey(50)
cv2.destroyAllWindows()
print("Number of chessboard images used for calibration: ", len(obj_points))
# 标定摄像机
ret_l, mtx_l, dist_l, rvecs_l, tvecs_l = cv2.calibrateCamera(obj_points, img_points_left, gray_l.shape[::-1],None,None)
ret_r, mtx_r, dist_r, rvecs_r, tvecs_r = cv2.calibrateCamera(obj_points, img_points_right, gray_r.shape[::-1],None,None)
# 标定双目摄像机
flags = 0
flags |= cv2.CALIB_FIX_INTRINSIC
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
ret, M1, d1, M2, d2, R, T, E, F = cv2.stereoCalibrate(
obj_points, img_points_left, img_points_right,
mtx_l, dist_l, mtx_r, dist_r,
gray_l.shape[::-1], criteria=criteria, flags=flags)
print("内参数矩阵M1:\n", M1)
print("畸变系数d1:\n", d1)
print("内参数矩阵M2:\n", M2)
print("畸变系数d2:\n", d2)
print("旋转矩阵R:\n", R)
print("平移矩阵T:\n", T)
print("本质矩阵E:\n", E)
print("基础矩阵F:\n", F)