|
|
""" |
|
|
Coordinate system conversion utilities. |
|
|
ARKit uses Y-up, right-handed. |
|
|
DA3/COLMAP typically use Z-up, right-handed (OpenCV convention). |
|
|
""" |
|
|
|
|
|
import numpy as np |
|
|
|
|
|
|
|
|
def arkit_to_opencv_transform() -> np.ndarray: |
|
|
""" |
|
|
Transform from ARKit coordinate system to OpenCV/COLMAP convention. |
|
|
|
|
|
ARKit: Y-up, right-handed |
|
|
OpenCV: Z-up, right-handed |
|
|
|
|
|
Conversion: Rotate -90° around X axis (Y -> Z) |
|
|
""" |
|
|
|
|
|
|
|
|
R = np.array( |
|
|
[ |
|
|
[1, 0, 0], |
|
|
[0, 0, 1], |
|
|
[0, -1, 0], |
|
|
] |
|
|
) |
|
|
return R |
|
|
|
|
|
|
|
|
def convert_arkit_to_opencv(pose_c2w: np.ndarray) -> np.ndarray: |
|
|
""" |
|
|
Convert ARKit camera-to-world pose to OpenCV/COLMAP convention. |
|
|
|
|
|
Args: |
|
|
pose_c2w: (4, 4) ARKit c2w pose (Y-up) |
|
|
|
|
|
Returns: |
|
|
(4, 4) OpenCV c2w pose (Z-up) |
|
|
""" |
|
|
if pose_c2w.shape != (4, 4): |
|
|
raise ValueError(f"Expected (4, 4) pose, got {pose_c2w.shape}") |
|
|
|
|
|
R_convert = arkit_to_opencv_transform() |
|
|
|
|
|
|
|
|
R_arkit = pose_c2w[:3, :3] |
|
|
t_arkit = pose_c2w[:3, 3] |
|
|
|
|
|
|
|
|
R_opencv = R_convert @ R_arkit @ R_convert.T |
|
|
|
|
|
|
|
|
t_opencv = R_convert @ t_arkit |
|
|
|
|
|
|
|
|
pose_opencv = np.eye(4) |
|
|
pose_opencv[:3, :3] = R_opencv |
|
|
pose_opencv[:3, 3] = t_opencv |
|
|
|
|
|
return pose_opencv |
|
|
|
|
|
|
|
|
def convert_opencv_to_arkit(pose_c2w: np.ndarray) -> np.ndarray: |
|
|
""" |
|
|
Convert OpenCV/COLMAP camera-to-world pose to ARKit convention. |
|
|
|
|
|
Args: |
|
|
pose_c2w: (4, 4) OpenCV c2w pose (Z-up) |
|
|
|
|
|
Returns: |
|
|
(4, 4) ARKit c2w pose (Y-up) |
|
|
""" |
|
|
R_convert = arkit_to_opencv_transform() |
|
|
R_convert_inv = R_convert.T |
|
|
|
|
|
R_opencv = pose_c2w[:3, :3] |
|
|
t_opencv = pose_c2w[:3, 3] |
|
|
|
|
|
|
|
|
R_arkit = R_convert_inv @ R_opencv @ R_convert |
|
|
|
|
|
|
|
|
t_arkit = R_convert_inv @ t_opencv |
|
|
|
|
|
pose_arkit = np.eye(4) |
|
|
pose_arkit[:3, :3] = R_arkit |
|
|
pose_arkit[:3, 3] = t_arkit |
|
|
|
|
|
return pose_arkit |
|
|
|
|
|
|
|
|
def convert_arkit_c2w_to_w2c(pose_c2w: np.ndarray, convert_coords: bool = True) -> np.ndarray: |
|
|
""" |
|
|
Convert ARKit c2w pose to w2c (for DA3 compatibility). |
|
|
|
|
|
Args: |
|
|
pose_c2w: (4, 4) ARKit c2w pose |
|
|
convert_coords: If True, convert from ARKit to OpenCV coordinate system |
|
|
|
|
|
Returns: |
|
|
(3, 4) w2c pose in OpenCV convention (if convert_coords=True) or ARKit convention |
|
|
""" |
|
|
if convert_coords: |
|
|
pose_c2w = convert_arkit_to_opencv(pose_c2w) |
|
|
|
|
|
|
|
|
pose_w2c = np.linalg.inv(pose_c2w) |
|
|
|
|
|
|
|
|
return pose_w2c[:3, :] |
|
|
|
|
|
|
|
|
def test_coordinate_conversion(): |
|
|
"""Test coordinate conversion.""" |
|
|
|
|
|
|
|
|
pose_arkit = np.eye(4) |
|
|
pose_arkit[:3, 3] = [1.0, 2.0, 3.0] |
|
|
|
|
|
print("ARKit pose (Y-up):") |
|
|
print(pose_arkit) |
|
|
print(f" Translation: {pose_arkit[:3, 3]}") |
|
|
|
|
|
|
|
|
pose_opencv = convert_arkit_to_opencv(pose_arkit) |
|
|
print("\nOpenCV pose (Z-up):") |
|
|
print(pose_opencv) |
|
|
print(f" Translation: {pose_opencv[:3, 3]}") |
|
|
|
|
|
|
|
|
pose_arkit_back = convert_opencv_to_arkit(pose_opencv) |
|
|
print("\nARKit pose (converted back):") |
|
|
print(pose_arkit_back) |
|
|
print(f" Translation: {pose_arkit_back[:3, 3]}") |
|
|
|
|
|
|
|
|
assert np.allclose(pose_arkit, pose_arkit_back), "Round-trip conversion failed!" |
|
|
print("\n✓ Round-trip conversion successful!") |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
test_coordinate_conversion() |
|
|
|