| |
| """ |
| Test rotation computation on actual Unity export data. |
| This will show what's actually causing the 168° roll error. |
| """ |
| import json |
| import numpy as np |
| from scipy.spatial.transform import Rotation as R |
| from pathlib import Path |
| from glob import glob |
|
|
| def test_actual_frame(): |
| """Load first frame from Unity export and compare rotation computations.""" |
|
|
| |
| jsonl_files = sorted(glob("./raw_dataset/sequence_*.jsonl")) |
| if not jsonl_files: |
| print("ERROR: No Unity export found in ./raw_dataset/") |
| print("Please run this from GENMO/ directory") |
| return |
|
|
| with open(jsonl_files[0], "r") as f: |
| lines = f.readlines() |
|
|
| if len(lines) < 2: |
| print("ERROR: No data in JSONL file") |
| return |
|
|
| |
| row = json.loads(lines[1]) |
|
|
| print("="*70) |
| print("TESTING ACTUAL UNITY EXPORT DATA (Frame 0)") |
| print("="*70) |
| print() |
|
|
| |
| cam_quat = np.array(row["cam_rot_world"], dtype=np.float64) |
| pel_quat = np.array(row["pelvis_rot_world"], dtype=np.float64) |
|
|
| print(f"Unity quaternions (XYZW):") |
| print(f" cam_rot_world: {cam_quat}") |
| print(f" pelvis_rot_world: {pel_quat}") |
| print() |
|
|
| |
| R_cam_w_unity = R.from_quat(cam_quat).as_matrix() |
| R_pel_w_unity = R.from_quat(pel_quat).as_matrix() |
|
|
| print("Unity rotations (Euler YXZ):") |
| print(f" Camera: {R.from_matrix(R_cam_w_unity).as_euler('YXZ', degrees=True)}") |
| print(f" Pelvis: {R.from_matrix(R_pel_w_unity).as_euler('YXZ', degrees=True)}") |
| print() |
|
|
| |
| C = np.diag([1.0, -1.0, 1.0]) |
|
|
| |
| print("--- OLD METHOD (Compute relative, then convert) ---") |
| R_w2c_unity = R_cam_w_unity.T |
| R_rel_unity = R_w2c_unity @ R_pel_w_unity |
| R_cv_old = C @ R_rel_unity @ C |
|
|
| euler_old = R.from_matrix(R_cv_old).as_euler('YXZ', degrees=True) |
| print(f"global_orient_c (YXZ): yaw={euler_old[0]:7.2f}°, pitch={euler_old[1]:7.2f}°, roll={euler_old[2]:7.2f}°") |
| print() |
|
|
| |
| print("--- NEW METHOD (Convert, then compute relative) ---") |
| R_cam_w_cv = C @ R_cam_w_unity @ C |
| R_pel_w_cv = C @ R_pel_w_unity @ C |
| R_w2c_cv = R_cam_w_cv.T |
| R_pel_c_cv = R_w2c_cv @ R_pel_w_cv |
|
|
| euler_new = R.from_matrix(R_pel_c_cv).as_euler('YXZ', degrees=True) |
| print(f"global_orient_c (YXZ): yaw={euler_new[0]:7.2f}°, pitch={euler_new[1]:7.2f}°, roll={euler_new[2]:7.2f}°") |
| print() |
|
|
| |
| if "smpl_incam_rot" in row: |
| unity_incam_quat = np.array(row["smpl_incam_rot"], dtype=np.float64) |
| R_unity_incam = R.from_quat(unity_incam_quat).as_matrix() |
| |
| R_unity_incam_cv = C @ R_unity_incam @ C |
| euler_unity = R.from_matrix(R_unity_incam_cv).as_euler('YXZ', degrees=True) |
|
|
| print("--- UNITY'S EXPORTED smpl_incam_rot (converted to CV) ---") |
| print(f"global_orient_c (YXZ): yaw={euler_unity[0]:7.2f}°, pitch={euler_unity[1]:7.2f}°, roll={euler_unity[2]:7.2f}°") |
| print() |
|
|
| print("--- COMPARISON ---") |
| diff_old = euler_old - euler_unity |
| diff_new = euler_new - euler_unity |
| print(f"OLD vs Unity: yaw={diff_old[0]:7.2f}°, pitch={diff_old[1]:7.2f}°, roll={diff_old[2]:7.2f}°") |
| print(f"NEW vs Unity: yaw={diff_new[0]:7.2f}°, pitch={diff_new[1]:7.2f}°, roll={diff_new[2]:7.2f}°") |
|
|
| print() |
| print("="*70) |
| print("DIAGNOSIS:") |
| diff = euler_new - euler_old |
| if np.allclose(diff, 0, atol=1.0): |
| print(" Both methods give same result!") |
| print(" The 168° roll error must come from somewhere else:") |
| print(" - Check body_pose export") |
| print(" - Check if Unity's smpl_incam_rot matches derived rotation") |
| print(" - Check SMPL model forward pass") |
| else: |
| print(f" Methods differ: yaw={diff[0]:.2f}°, pitch={diff[1]:.2f}°, roll={diff[2]:.2f}°") |
| print(" This explains the rotation error!") |
|
|
| if __name__ == "__main__": |
| test_actual_frame() |
|
|