双目标定实现流程
·
该双目标定所使用摄像头为单usb双目摄像。
- 双目相机采集黑白棋盘标定板数据。
# coding:utf-8
import cv2
import time
import os
import datetime
# 初始化摄像头(假设是单个设备,输出左右拼接画面)
camera = cv2.VideoCapture(1) # 根据实际情况调整摄像头索引(0, 1, 2...)
# 设置摄像头分辨率(通常双目摄像头是左右拼接,总宽度是单目两倍)
camera.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) # 双目标建议1280宽度(640x2)
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
# 检查摄像头是否成功打开
if not camera.isOpened():
print("错误:无法打开摄像头!")
exit()
# 创建左右视图的存储文件夹
left_folder = "......"
right_folder = "......"
os.makedirs(left_folder, exist_ok=True)
os.makedirs(right_folder, exist_ok=True)
# 自动拍摄设置
AUTO_MODE = False # 手动按's'保存,True则自动每隔INTERVAL秒保存
INTERVAL = 2 # 自动保存间隔(秒)
counter = 0 # 图片计数器
last_save_time = time.time() # 初始化最后保存时间
def save_stereo_images(frame):
"""分割左右视图并保存到不同文件夹"""
global counter
# 获取画面尺寸(假设是左右拼接)
height, width = frame.shape[:2]
half_width = width // 2
# 分割左、右视图
left_img = frame[:, :half_width] # 左半部分
right_img = frame[:, half_width:] # 右半部分
# 生成时间戳
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
# 保存左视图到left_images文件夹
left_path = os.path.join(left_folder, f"left_{timestamp}_{counter:04d}.jpg")
cv2.imwrite(left_path, left_img)
# 保存右视图到right_images文件夹
right_path = os.path.join(right_folder, f"right_{timestamp}_{counter:04d}.jpg")
cv2.imwrite(right_path, right_img)
print(f"已保存: {left_path} 和 {right_path}")
counter += 1
# 显示实时画面
cv2.namedWindow("Stereo Camera", cv2.WINDOW_NORMAL)
print("操作说明:")
print("1. 按 's' 手动保存左右视图")
print("2. 按 'a' 切换自动保存模式")
print("3. 按 'q' 退出程序")
auto_save = AUTO_MODE
try:
while True:
ret, frame = camera.read()
if not ret:
print("错误:无法读取帧!")
break
# 显示完整画面
cv2.imshow("Stereo Camera", frame)
# 自动保存逻辑
current_time = time.time()
if auto_save and (current_time - last_save_time >= INTERVAL):
save_stereo_images(frame)
last_save_time = current_time
# 键盘控制
key = cv2.waitKey(1) & 0xFF
if key == ord('q'): # 退出
break
elif key == ord('s'): # 手动保存
save_stereo_images(frame)
elif key == ord('a'): # 切换自动模式
auto_save = not auto_save
print(f"自动保存模式: {'开启' if auto_save else '关闭'}")
last_save_time = time.time() # 重置计时器
finally:
camera.release()
cv2.destroyAllWindows()
print("程序结束,摄像头已释放。")
- 相机参数标定流程
import numpy as np
import cv2
import glob
import os
import yaml
class ZhangStereoCalibrator:
def __init__(self, chessboard_size=(11, 8), square_size=2.5):
"""初始化标定器"""
self.chessboard_size = chessboard_size
self.square_size = square_size # 单位:厘米
self.criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
self.flags = (
cv2.CALIB_RATIONAL_MODEL |
cv2.CALIB_THIN_PRISM_MODEL |
cv2.CALIB_FIX_ASPECT_RATIO
)
# 生成棋盘格3D坐标
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) * square_size
# 存储标定数据
self.objpoints = [] # 3D点
self.imgpoints_left = [] # 左图像点
self.imgpoints_right = [] # 右图像点
self.calibration_results = {}
self.reprojection_errors = [] # 存储每张图像的重投影误差
def detect_corners(self, img, show=False):
"""检测棋盘格角点(使用更精确的findChessboardCornersSB)"""
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCornersSB(
gray, self.chessboard_size,
cv2.CALIB_CB_EXHAUSTIVE | cv2.CALIB_CB_ACCURACY
)
if ret:
corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), self.criteria)
if show:
vis = img.copy()
cv2.drawChessboardCorners(vis, self.chessboard_size, corners, ret)
cv2.imshow('Corners', vis)
cv2.waitKey(300)
return ret, corners
def find_chessboard_corners(self, left_paths, right_paths, min_pairs=15):
"""查找并筛选有效的棋盘格图像对"""
assert len(left_paths) == len(right_paths), "左右图像数量必须相同"
for left_path, right_path in zip(left_paths, right_paths):
img_left = cv2.imread(left_path)
img_right = cv2.imread(right_path)
ret_left, corners_left = self.detect_corners(img_left)
ret_right, corners_right = self.detect_corners(img_right)
if ret_left and ret_right:
self.objpoints.append(self.objp)
self.imgpoints_left.append(corners_left)
self.imgpoints_right.append(corners_right)
# 可视化
cv2.drawChessboardCorners(img_left, self.chessboard_size, corners_left, ret_left)
cv2.drawChessboardCorners(img_right, self.chessboard_size, corners_right, ret_right)
combined = np.hstack((img_left, img_right))
cv2.imshow('Stereo Corners', combined)
cv2.waitKey(300)
cv2.destroyAllWindows()
valid_pairs = len(self.objpoints)
print(f"成功检测到 {valid_pairs} 对有效图像")
if valid_pairs < min_pairs:
print(f"警告: 建议至少 {min_pairs} 对图像以获得稳定标定")
return valid_pairs >= 10 # 最低要求10对
def calibrate(self, image_size):
"""执行标定(不固定基线)"""
# 单目标定
print("标定左相机...")
ret_left, K_left, D_left, rvecs_left, tvecs_left = cv2.calibrateCamera(
self.objpoints, self.imgpoints_left, image_size, None, None, flags=self.flags)
print("标定右相机...")
ret_right, K_right, D_right, rvecs_right, tvecs_right = cv2.calibrateCamera(
self.objpoints, self.imgpoints_right, image_size, None, None, flags=self.flags)
# 立体标定(固定内参)
print("立体标定中...")
flags = self.flags | cv2.CALIB_FIX_INTRINSIC
ret, K_left, D_left, K_right, D_right, R, T, E, F = cv2.stereoCalibrate(
self.objpoints, self.imgpoints_left, self.imgpoints_right,
K_left, D_left, K_right, D_right, image_size,
criteria=self.criteria, flags=flags)
# 保存结果
self.calibration_results = {
'K_left': K_left, 'D_left': D_left,
'K_right': K_right, 'D_right': D_right,
'R': R, 'T': T, 'E': E, 'F': F,
'image_size': image_size,
'rvecs_left': rvecs_left, 'tvecs_left': tvecs_left,
'rvecs_right': rvecs_right, 'tvecs_right': tvecs_right
}
self.calculate_reprojection_error()
return self.calibration_results
def calibrate_with_fixed_baseline(self, image_size, target_baseline=6.5):
"""标定并强制固定基线距离(单位:厘米)"""
# 先执行常规标定
self.calibrate(image_size)
# 修正基线距离
T = self.calibration_results['T']
current_baseline = np.linalg.norm(T)
print(f"原始基线距离: {current_baseline:.3f} cm")
if current_baseline > 0:
# 保持方向,缩放幅度到目标基线
T_fixed = T * (target_baseline / current_baseline)
self.calibration_results['T'] = T_fixed
print(f"修正后基线: {np.linalg.norm(T_fixed):.3f} cm")
# 重新计算校正参数
self.rectify()
else:
print("错误: 基线距离为0,无法修正")
return self.calibration_results
def calculate_reprojection_error(self):
"""计算重投影误差"""
total_error = 0
self.reprojection_errors = [] # 重置误差列表
# 计算左相机重投影误差
left_errors = []
for i in range(len(self.objpoints)):
imgpoints_reproj, _ = cv2.projectPoints(
self.objpoints[i],
self.calibration_results['rvecs_left'][i],
self.calibration_results['tvecs_left'][i],
self.calibration_results['K_left'],
self.calibration_results['D_left'])
error = cv2.norm(self.imgpoints_left[i], imgpoints_reproj, cv2.NORM_L2) / len(imgpoints_reproj)
left_errors.append(error)
total_error += error
# 计算右相机重投影误差
right_errors = []
for i in range(len(self.objpoints)):
imgpoints_reproj, _ = cv2.projectPoints(
self.objpoints[i],
self.calibration_results['rvecs_right'][i],
self.calibration_results['tvecs_right'][i],
self.calibration_results['K_right'],
self.calibration_results['D_right'])
error = cv2.norm(self.imgpoints_right[i], imgpoints_reproj, cv2.NORM_L2) / len(imgpoints_reproj)
right_errors.append(error)
total_error += error
# 计算平均误差
mean_error = total_error / (2 * len(self.objpoints))
# 保存误差数据
self.reprojection_errors = {
'left_errors': left_errors,
'right_errors': right_errors,
'mean_error': mean_error,
'max_left_error': max(left_errors),
'max_right_error': max(right_errors),
'min_left_error': min(left_errors),
'min_right_error': min(right_errors)
}
print("\n=== 重投影误差 ===")
print(f"左相机平均误差: {np.mean(left_errors):.3f} 像素")
print(f"右相机平均误差: {np.mean(right_errors):.3f} 像素")
print(f"全局平均误差: {mean_error:.3f} 像素")
print(f"左相机最大误差: {max(left_errors):.3f} 像素 (图像#{left_errors.index(max(left_errors)) + 1})")
print(f"右相机最大误差: {max(right_errors):.3f} 像素 (图像#{right_errors.index(max(right_errors)) + 1})")
self.calibration_results['reprojection_errors'] = self.reprojection_errors
self.calibration_results['reproj_error'] = mean_error
def rectify(self):
"""立体校正(使用当前标定结果)"""
R1, R2, P1, P2, Q, _, _ = cv2.stereoRectify(
self.calibration_results['K_left'],
self.calibration_results['D_left'],
self.calibration_results['K_right'],
self.calibration_results['D_right'],
self.calibration_results['image_size'],
self.calibration_results['R'],
self.calibration_results['T'],
flags=cv2.CALIB_ZERO_DISPARITY,
alpha=0.9
)
self.calibration_results.update({'R1': R1, 'R2': R2, 'P1': P1, 'P2': P2, 'Q': Q})
# 计算校正映射
self.left_map1, self.left_map2 = cv2.initUndistortRectifyMap(
self.calibration_results['K_left'], self.calibration_results['D_left'],
R1, P1, self.calibration_results['image_size'], cv2.CV_16SC2)
self.right_map1, self.right_map2 = cv2.initUndistortRectifyMap(
self.calibration_results['K_right'], self.calibration_results['D_right'],
R2, P2, self.calibration_results['image_size'], cv2.CV_16SC2)
def save_as_yaml(self, filename='stereo_calibration1.yaml'):
"""保存标定结果为YAML文件"""
data = {
'K_left': self.calibration_results['K_left'].tolist(),
'D_left': self.calibration_results['D_left'].ravel().tolist(),
'K_right': self.calibration_results['K_right'].tolist(),
'D_right': self.calibration_results['D_right'].ravel().tolist(),
'R': self.calibration_results['R'].tolist(),
'T': self.calibration_results['T'].tolist(),
'reproj_error': float(self.calibration_results['reproj_error']),
'reprojection_errors': {
'mean_error': float(self.reprojection_errors['mean_error']),
'left_mean': float(np.mean(self.reprojection_errors['left_errors'])),
'right_mean': float(np.mean(self.reprojection_errors['right_errors'])),
'max_left': float(self.reprojection_errors['max_left_error']),
'max_right': float(self.reprojection_errors['max_right_error']),
'min_left': float(self.reprojection_errors['min_left_error']),
'min_right': float(self.reprojection_errors['min_right_error'])
}
}
with open(filename, 'w') as f:
yaml.dump(data, f, default_flow_style=False)
print(f"标定结果已保存到 {filename}")
def show_rectified_images(self, left_img, right_img):
"""显示校正后的图像(验证极线对齐)"""
left_rect = cv2.remap(left_img, self.left_map1, self.left_map2, cv2.INTER_LINEAR)
right_rect = cv2.remap(right_img, self.right_map1, self.right_map2, cv2.INTER_LINEAR)
# 绘制水平线
for y in range(0, left_rect.shape[0], 50):
cv2.line(left_rect, (0, y), (left_rect.shape[1], y), (0, 255, 0), 1)
cv2.line(right_rect, (0, y), (right_rect.shape[1], y), (0, 255, 0), 1)
combined = np.hstack((left_rect, right_rect))
cv2.imshow('Rectified Images', combined)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == '__main__':
# 初始化标定器
calibrator = ZhangStereoCalibrator(chessboard_size=(11, 8), square_size=2.5)
# 加载图像
left_images = sorted(glob.glob('left/*.jpg'))
right_images = sorted(glob.glob('right/*.jpg'))
if calibrator.find_chessboard_corners(left_images, right_images, min_pairs=15):
# 执行标定(强制基线为6.5cm)
results = calibrator.calibrate_with_fixed_baseline(
image_size=(640, 480),
target_baseline=6.5 # 65mm
)
# 打印关键结果
print("\n=== 标定结果 ===")
print(f"左相机内参:\n{results['K_left']}")
print(f"右相机内参:\n{results['K_right']}")
print(f"旋转矩阵 R:\n{results['R']}")
print(f"平移向量 T (基线):\n{results['T'].ravel()} cm")
print(f"基线长度: {np.linalg.norm(results['T']):.3f} cm")
# 打印详细的误差信息
errors = results['reprojection_errors']
print("\n=== 重投影误差详情 ===")
print(f"全局平均误差: {errors['mean_error']:.3f} 像素")
print(f"左相机误差范围: {errors['min_left_error']:.3f} - {errors['max_left_error']:.3f} 像素")
print(f"右相机误差范围: {errors['min_right_error']:.3f} - {errors['max_right_error']:.3f} 像素")
# 保存并验证
calibrator.save_as_yaml()
# 测试校正效果
test_left = cv2.imread(left_images[0])
test_right = cv2.imread(right_images[0])
calibrator.show_rectified_images(test_left, test_right)
else:
print("标定失败:未找到足够的有效图像对")```
- 误差测试及立体校正分析
import numpy as np
import cv2
import yaml
import open3d as o3d
from matplotlib import pyplot as plt
class StereoCalibrationValidator:
def __init__(self, calib_file='your_file'):
"""加载标定参数"""
with open(calib_file, 'r') as f:
self.calib_data = yaml.safe_load(f)
# 加载相机参数
self.K_left = np.array(self.calib_data['K_left'])
self.D_left = np.array(self.calib_data['D_left'][:14]) # 取前14个畸变系数
self.K_right = np.array(self.calib_data['K_right'])
self.D_right = np.array(self.calib_data['D_right'][:14]) # 取前14个畸变系数
self.R = np.array(self.calib_data['R'])
self.T = np.array(self.calib_data['T'])
# 如果没有指定image_size,根据相机矩阵推测
if 'image_size' in self.calib_data:
self.image_size = tuple(self.calib_data['image_size'])
else:
# 从相机矩阵推测图像尺寸 (假设主点接近图像中心)
self.image_size = (int(self.K_left[0, 2] * 2), int(self.K_left[1, 2] * 2))
# 初始化校正映射
self._init_rectify_maps()
# 打印基础信息
print(f"基线距离: {np.linalg.norm(self.T):.3f} 单位")
print(f"图像尺寸: {self.image_size}")
# 检查并打印重投影误差
if 'reprojection_errors' in self.calib_data:
errors = self.calib_data['reprojection_errors']
print("\n重投影误差统计:")
print(f"平均误差: {errors['mean_error']:.6f} px")
print(f"左相机平均误差: {errors['left_mean']:.6f} px")
print(f"右相机平均误差: {errors['right_mean']:.6f} px")
print(f"左相机最大误差: {errors['max_left']:.6f} px")
print(f"右相机最大误差: {errors['max_right']:.6f} px")
print(f"左相机最小误差: {errors['min_left']:.6f} px")
print(f"右相机最小误差: {errors['min_right']:.6f} px")
else:
print("警告: 标定文件中未找到重投影误差数据")
def _init_rectify_maps(self):
"""初始化校正映射"""
# 使用所有14个畸变系数
flags = cv2.CALIB_RATIONAL_MODEL + cv2.CALIB_THIN_PRISM_MODEL
R1, R2, P1, P2, Q, _, _ = cv2.stereoRectify(
self.K_left, self.D_left, self.K_right, self.D_right,
self.image_size, self.R, self.T,
flags=cv2.CALIB_ZERO_DISPARITY, alpha=0)
self.left_map1, self.left_map2 = cv2.initUndistortRectifyMap(
self.K_left, self.D_left, R1, P1, self.image_size, cv2.CV_32FC1)
self.right_map1, self.right_map2 = cv2.initUndistortRectifyMap(
self.K_right, self.D_right, R2, P2, self.image_size, cv2.CV_32FC1)
self.Q = Q
def _split_frame(self, frame):
"""分割合帧图像"""
h, w = frame.shape[:2]
return frame[:, :w // 2], frame[:, w // 2:] # 左图像在左半部分
def check_rectification(self, frame):
"""检查校正质量"""
left, right = self._split_frame(frame)
# 校正图像
left_rect = cv2.remap(left, self.left_map1, self.left_map2, cv2.INTER_LINEAR)
right_rect = cv2.remap(right, self.right_map1, self.right_map2, cv2.INTER_LINEAR)
# 检测棋盘格角点
pattern_size = (11, 8) # 根据实际棋盘格调整
gray_left = cv2.cvtColor(left_rect, cv2.COLOR_BGR2GRAY)
gray_right = cv2.cvtColor(right_rect, cv2.COLOR_BGR2GRAY)
ret_l, corners_l = cv2.findChessboardCornersSB(gray_left, pattern_size, None)
ret_r, corners_r = cv2.findChessboardCornersSB(gray_right, pattern_size, None)
if ret_l and ret_r:
# 计算垂直对齐误差
vert_errors = np.abs(corners_l[:, 0, 1] - corners_r[:, 0, 1])
max_error = np.max(vert_errors)
avg_error = np.mean(vert_errors)
# 可视化
vis = np.hstack((left_rect, right_rect))
for pt_l, pt_r in zip(corners_l[:, 0], corners_r[:, 0]):
pt_r[0] += left_rect.shape[1] # 右图x坐标偏移
cv2.line(vis, tuple(pt_l.astype(int)), tuple(pt_r.astype(int)), (0, 0, 255), 1)
# 绘制水平参考线
for y in range(0, vis.shape[0], 30):
cv2.line(vis, (0, y), (vis.shape[1], y), (0, 255, 0), 1)
cv2.putText(vis, f"Max Vertical Error: {max_error:.2f}px", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
cv2.putText(vis, f"Avg Vertical Error: {avg_error:.2f}px", (10, 70),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
cv2.imshow('Rectification Check', vis)
cv2.waitKey(0)
return max_error, avg_error
else:
print("未检测到棋盘格!")
return None, None
def check_disparity(self, frame):
"""检查视差图质量"""
left, right = self._split_frame(frame)
# 校正图像
left_rect = cv2.remap(left, self.left_map1, self.left_map2, cv2.INTER_LINEAR)
right_rect = cv2.remap(right, self.right_map1, self.right_map2, cv2.INTER_LINEAR)
# 计算视差图 (使用优化参数)
stereo = cv2.StereoSGBM_create(
minDisparity=0,
numDisparities=64,
blockSize=6,
P1=8 * 3 * 7 ** 2,
P2=32 * 3 * 7 ** 2,
disp12MaxDiff=1,
uniquenessRatio=15,
speckleWindowSize=100,
speckleRange=32
)
gray_left = cv2.cvtColor(left_rect, cv2.COLOR_BGR2GRAY)
gray_right = cv2.cvtColor(right_rect, cv2.COLOR_BGR2GRAY)
disparity = stereo.compute(gray_left, gray_right).astype(np.float32) / 16.0
# 可视化
plt.figure(figsize=(12, 6))
plt.subplot(131), plt.imshow(cv2.cvtColor(left_rect, cv2.COLOR_BGR2RGB))
plt.title('Left Rectified')
plt.subplot(132), plt.imshow(cv2.cvtColor(right_rect, cv2.COLOR_BGR2RGB))
plt.title('Right Rectified')
disp_vis = cv2.normalize(disparity, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)
disp_color = cv2.applyColorMap(disp_vis, cv2.COLORMAP_JET)
plt.subplot(133), plt.imshow(cv2.cvtColor(disp_color, cv2.COLOR_BGR2RGB))
plt.title('Disparity Map')
plt.show()
return disparity
def check_3d_reconstruction(self, frame):
"""3D重建质量检查"""
disparity = self.check_disparity(frame)
if disparity is None:
return
left, _ = self._split_frame(frame)
left_rect = cv2.remap(left, self.left_map1, self.left_map2, cv2.INTER_LINEAR)
# 生成点云
points_3d = cv2.reprojectImageTo3D(disparity, self.Q)
mask = (disparity > disparity.min()) & (disparity < disparity.max())
points = points_3d[mask].reshape(-1, 3)
colors = left_rect[mask].reshape(-1, 3) / 255.0
# 创建点云
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(points)
pcd.colors = o3d.utility.Vector3dVector(colors)
# 平面拟合检查(如果检测到棋盘格)
pattern_size = (11, 8)
gray = cv2.cvtColor(left_rect, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCornersSB(gray, pattern_size, None)
if ret:
# 创建棋盘格mask
mask = np.zeros_like(gray, dtype=np.uint8)
cv2.drawChessboardCorners(mask, pattern_size, corners, ret)
board_points = points_3d[mask.astype(bool)]
# 使用PCA拟合平面
mean = np.mean(board_points, axis=0)
centered = board_points - mean
cov = np.cov(centered.T)
eigenvalues, eigenvectors = np.linalg.eig(cov)
# 最小特征值对应的特征向量是平面法向量
normal = eigenvectors[:, np.argmin(eigenvalues)]
d = -np.dot(normal, mean)
plane = np.append(normal, d) # 平面方程: ax + by + cz + d = 0
# 计算点到平面的距离
distances = np.abs(np.dot(board_points, plane[:3]) + plane[3]) / np.linalg.norm(plane[:3])
print(f"标定板平面拟合误差: {np.mean(distances):.2f} 单位")
# 可视化平面区域点云
board_pcd = o3d.geometry.PointCloud()
board_pcd.points = o3d.utility.Vector3dVector(board_points)
o3d.visualization.draw_geometries([board_pcd], window_name="Board Plane")
# 可视化完整点云
o3d.visualization.draw_geometries([pcd], window_name="Full Point Cloud")
def run_live_validation(self, cam_idx=0):
"""实时验证"""
cap = cv2.VideoCapture(cam_idx)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.image_size[0] * 2)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.image_size[1])
print("\n实时验证模式:")
print("1. 按 'c' 检查当前帧的校正质量")
print("2. 按 'd' 显示视差图")
print("3. 按 '3' 生成3D点云")
print("4. 按 ESC 退出")
while True:
ret, frame = cap.read()
if not ret: break
cv2.imshow('Live Feed', frame)
key = cv2.waitKey(1)
if key == 27: # ESC
break
elif key == ord('c'):
self.check_rectification(frame.copy())
elif key == ord('d'):
self.check_disparity(frame.copy())
elif key == ord('3'):
self.check_3d_reconstruction(frame.copy())
cap.release()
cv2.destroyAllWindows()
if __name__ == '__main__':
validator = StereoCalibrationValidator()
validator.run_live_validation(cam_idx=1)
更多推荐


所有评论(0)