stereoRectify中的 P1 P2是将世界坐标系转到了什么坐标系

在OpenCV的stereoRectify函数中,输出的投影矩阵P1P2将世界坐标系转换到了校正后的相机坐标系,具体来说是:

  1. 坐标系转换流程

    • 原始世界点 (X,Y,Z)
    • 通过P1/P2 → 校正后的相机坐标系 (u,v,w)
  2. 关键特性

    • P1(左相机)和P2(右相机)是3×4投影矩阵
    • 它们已经包含了立体校正(rectification)的旋转矩阵R
    • 输出坐标系的x轴与基线平行(极线水平对齐)
  3. 数学表示

    [u, v, w]^T = P * [X, Y, Z, 1]^T
    

    其中:

    • u/wv/w得到校正后的像素坐标
    • w实际上等于校正后坐标系中的Z值
  4. 与传统投影矩阵的区别

    • 普通投影矩阵:世界→原始像素坐标系
    • P1/P2:世界→校正后的像素坐标系
    • 包含了双目系统的基线信息(P2的第4列包含Tx/f)

典型场景中,如果用reprojectImageTo3D计算视差图的三维坐标,得到的点云已经是校正后坐标系下的表示。如果需要原始相机坐标系的点云,需要应用校正旋转矩阵的逆变换。

建议使用时注意:

  1. 这个坐标系以左相机光心为原点
  2. X轴向右,Y轴向下,Z轴向前
  3. 极线完全水平对齐,便于立体匹配

用reprojectImageTo3D计算视差图的三维坐标XYZ后,我想将XYZ转换回相机坐标系,怎么计算?

要将 reprojectImageTo3D 计算出的三维坐标(校正后坐标系)转换回原始相机坐标系,需明确以下关键点并分步操作:


1. 坐标系关系

  • 校正后坐标系 (X_rect, Y_rect, Z_rect):
    reprojectImageTo3D(disparity, Q) 直接得到,已通过立体校正旋转(R1/R2)对齐极线。
  • 原始相机坐标系 (X_cam, Y_cam, Z_cam):
    校正前的相机坐标系,Z轴指向光轴方向。

二者的关系为:
image

其中 R1stereoRectify 返回的左相机校正旋转矩阵。


2. 转换步骤

方法一:直接逆旋转

import numpy as np
import cv2

# 假设已通过 stereoRectify 获取 R1, R2, P1, P2, Q
points_3d_rect = cv2.reprojectImageTo3D(disparity_map, Q)  # 校正后坐标系

# 转换为原始左相机坐标系
R1_inv = cv2.Rodrigues(R1)[0].T  # R1的逆矩阵(旋转矩阵的转置)
points_3d_cam = np.einsum('ij,hwj->hwi', R1_inv, points_3d_rect)  # 批量矩阵乘法

方法二:从 Q 矩阵推导

image

可直接从视差 d 和像素坐标 (u,v) 计算原始相机坐标,无需显式使用 R1


3. 验证方法

  1. 投影一致性检查
    将转换后的 (X_cam, Y_cam, Z_cam) 用原始相机内参矩阵 K 投影,应得到原始图像坐标:

    # 假设 K 是原始相机内参矩阵
    point_cam = np.array([X_cam, Y_cam, Z_cam])
    projected = K @ point_cam
    projected /= projected[2]  # 齐次坐标归一化
    print("投影像素坐标:", projected[:2])  # 应与原始匹配点一致
    
  2. 深度方向验证
    确保 Z_cam 为正且符合实际场景尺度(如人脸检测中典型值 0.5~2米)。


4. 注意事项

  • 旋转矩阵的维度
    R1 是 3x3 矩阵,需确保与坐标点乘法维度匹配。
  • 右相机坐标系
    若需转到右相机坐标系,需额外减去基线平移 T(来自 stereoRectify):
    points_3d_cam_right = (cv2.Rodrigues(R2)[0].T @ points_3d_rect.T).T - T
    
  • 单位统一
    确保 Q 中的 Tx 与相机标定的物理基线一致(毫米/米)。

总结

  • 核心公式
    image

  • 推荐方法
    直接对 reprojectImageTo3D 的结果左乘 R1 的逆矩阵(R1.T),简单高效。

  • 应用场景
    后续处理(如目标检测、SLAM)通常需要原始相机坐标系下的点云。

reprojectImageTo3D(disparity, Q)得到的XYZ是世界坐标吗?

reprojectImageTo3D(disparity, Q) 返回的三维坐标 (X, Y, Z) 既不是世界坐标系,也不是原始相机坐标系,而是校正后的左相机坐标系下的坐标。以下是关键解析:


1. 坐标系本质

坐标系类型 说明
校正后左相机坐标系 reprojectImageTo3D 的输出坐标系,原点在左相机光心,但坐标轴经过立体校正旋转(极线对齐)
原始左相机坐标系 校正前的相机坐标系,Z轴沿光轴方向
世界坐标系 用户定义的全局坐标系,需通过相机外参(R, t)转换得到

2. 坐标转换关系

(1)校正后坐标 → 原始相机坐标

通过校正旋转矩阵的逆变换:

import cv2
import numpy as np

# 假设已通过 stereoRectify 获取 R1 (左相机校正旋转矩阵)
points_3d_rect = cv2.reprojectImageTo3D(disparity, Q)  # 校正后坐标 (X_rect, Y_rect, Z_rect)
R1_inv = cv2.Rodrigues(R1)[0].T  # R1的逆矩阵
points_3d_cam = np.einsum('ij,hwj->hwi', R1_inv, points_3d_rect)  # 原始左相机坐标

(2)原始相机坐标 → 世界坐标

需通过相机外参(旋转矩阵 R 和平移向量 t):

# 假设 R 和 t 是左相机的外参(从世界坐标系到相机坐标系)
R_world_to_cam = R  # 3x3 旋转矩阵
t_world_to_cam = t  # 3x1 平移向量

# 将点云转换到世界坐标系
points_3d_world = np.einsum('ij,hwj->hwi', R_world_to_cam.T, points_3d_cam - t_world_to_cam)

3. 关键区别

方法 输出坐标系 是否需要额外转换
reprojectImageTo3D 校正后左相机坐标系 需用 R1 转原始相机坐标系
手动计算(Z=f*T/d) 原始左相机坐标系 可直接用于后续处理

4. 为什么不是世界坐标?

  • Q矩阵不包含世界坐标系信息
    Q 仅包含立体校正参数(基线 Tx、焦距 f、主点 (cx, cy)),无世界坐标系的外参(R, t)。
  • 默认原点在左相机
    若未提供世界坐标系的外参,所有坐标均相对于左相机光心。

5. 验证方法

# 检查校正后坐标的X轴是否平行于基线(极线水平)
print("校正后坐标系中基线的方向:", R1 @ np.array([1, 0, 0]))  # 应接近 [1, 0, 0]

# 验证Z_cam方向
test_point = np.array([0, 0, 1, 1])  # 假设Z=1米的点
projected = (P1 @ test_point.T).T    # 投影到校正图像
projected /= projected[2]            # 归一化
print("投影像素坐标:", projected[:2])  # 应与实际观测一致

总结

  • reprojectImageTo3D 输出的是校正后左相机坐标系,非世界坐标。
  • 🔄 转原始相机坐标:左乘 R1.TR1 来自 stereoRectify)。
  • 🌍 转世界坐标:需额外应用相机外参 (R, t) 的逆变换。
  • ⚠️ 注意:若未标定世界坐标系,所有坐标均为相机相对坐标。
posted @ 2025-07-07 09:28  aisuanfa  阅读(39)  评论(0)    收藏  举报