feat(viz): ARKit 91 -> MP 33 joint map

This commit is contained in:
L'électron rare
2026-05-14 12:03:55 +02:00
parent 8d6b36c523
commit 1b1ff62f5f
2 changed files with 77 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
"""ARKit ARSkeleton3D 91-joint indices → MediaPipe Pose 33 indices.
The ARKit ARSkeleton.JointName enum (Apple SDK) orders 91 joints
starting with the root, hips, spine chain, shoulders, etc. We pick
only the joints with a clear 1:1 anatomical correspondence to the
MediaPipe Pose 33 landmark set (which is what AVLiveBody renders).
Face/hand sub-joints (fingers, eyes) are skipped — those keep their
existing data sources (MediaPipe Face/Hand + HaMeR MANO).
Reference for ARKit joint order : Apple developer docs
"ARSkeleton.JointName" — the canonical 91-joint list runs from
root_joint=0 down to right_handThumbEndJoint=90.
The selection here mirrors `multi.py::SMPLX_TO_MP33` so the same 14
body slots are overridden by ARKit when fresh. Confidence comes
from ARKit's tracking state but is not currently fanned out — we
trust ARKit body tracking when its OSC frame is present.
"""
from __future__ import annotations
# MediaPipe Pose 33 cardinality (cf. mediapipe pose_world_landmarks).
MP33_NUM_LANDMARKS = 33
# Pelvis = ARKit hips_joint, slot 1 in the canonical enum order.
# Used by multi_hmr_worker for cam-translation z lock.
ARKIT_PELVIS_IDX = 1
# (arkit_joint_idx, mediapipe_pose_idx). Match the body slots used
# by the SMPL-X body fusion in multi.py.
ARKIT91_TO_MP33: tuple[tuple[int, int], ...] = (
(50, 11), # left_shoulder_1_joint -> L_SHOULDER
(32, 12), # right_shoulder_1_joint -> R_SHOULDER
(53, 13), # left_arm_joint -> L_ELBOW
(35, 14), # right_arm_joint -> R_ELBOW
(54, 15), # left_forearm_joint -> L_WRIST
(36, 16), # right_forearm_joint -> R_WRIST
(62, 23), # left_upLeg_joint -> L_HIP
(57, 24), # right_upLeg_joint -> R_HIP
(63, 25), # left_leg_joint -> L_KNEE
(58, 26), # right_leg_joint -> R_KNEE
(64, 27), # left_foot_joint -> L_ANKLE
(59, 28), # right_foot_joint -> R_ANKLE
(65, 31), # left_toes_joint -> L_FOOT_INDEX
(60, 32), # right_toes_joint -> R_FOOT_INDEX
)
@@ -0,0 +1,32 @@
"""ARKit 91 joints → MediaPipe Pose 33 mapping integrity."""
from data_only_viz.arkit_joint_map import (
ARKIT91_TO_MP33, ARKIT_PELVIS_IDX, MP33_NUM_LANDMARKS,
)
def test_mapping_is_tuple_of_pairs():
assert isinstance(ARKIT91_TO_MP33, tuple)
assert len(ARKIT91_TO_MP33) > 0
for pair in ARKIT91_TO_MP33:
assert isinstance(pair, tuple)
assert len(pair) == 2
def test_mapping_indices_in_range():
for arkit_idx, mp33_idx in ARKIT91_TO_MP33:
assert 0 <= arkit_idx < 91, f"arkit idx out of range: {arkit_idx}"
assert 0 <= mp33_idx < MP33_NUM_LANDMARKS, \
f"mp33 idx out of range: {mp33_idx}"
def test_pelvis_index_valid():
assert 0 <= ARKIT_PELVIS_IDX < 91
def test_no_duplicate_mp33_targets():
"""Each MediaPipe slot must be written by at most one ARKit joint."""
mp33_seen = set()
for _, mp33_idx in ARKIT91_TO_MP33:
assert mp33_idx not in mp33_seen, \
f"mp33 slot {mp33_idx} mapped twice"
mp33_seen.add(mp33_idx)