该双目标定所使用摄像头为单usb双目摄像。

  1. 双目相机采集黑白棋盘标定板数据。
# 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("程序结束,摄像头已释放。")
  1. 相机参数标定流程
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("标定失败:未找到足够的有效图像对")```
  1. 误差测试及立体校正分析
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)
Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐