1414# limitations under the License.
1515#
1616"""
17- Convert SOMASkeleton30 NPZ motion files to ProtoMotions format for soma23.
17+ Convert SOMA NPZ motion files to ProtoMotions format for soma23.
1818
19- NPZ files contain data from the SOMA pipeline with 30-body SOMASkeleton30:
20- local_rot_mats — (T, 30, 3, 3) local rotation matrices
21- root_positions — (T, 3) root translation
19+ NPZ files contain the full 77-joint SOMA skeleton:
20+ local_rot_mats — (T, 77, 3, 3) local rotation matrices
21+ posed_joints — (T, 77, 3) global joint positions (root pos = index 0)
22+ global_rot_mats — (T, 77, 3, 3) global rotation matrices (unused by converter)
23+ foot_contacts — (T, 4) foot contact labels (unused by converter)
2224
23- A legacy batch dimension (B=1) is also accepted and squeezed automatically.
24-
25- The 30 bodies are subselected to the 23 MJCF bodies by dropping 7 leaf
26- end-effectors: Jaw, LeftEye, RightEye, LeftHandThumbEnd, LeftHandMiddleEnd,
27- RightHandThumbEnd, RightHandMiddleEnd.
25+ The 77 joints are subselected to the 23 MJCF bodies using SOMASKEL77_TO_MJCF_INDICES
26+ (same mapping as the npy and BVH pipelines).
2827
2928Usage:
3029 python data/scripts/convert_soma23_npz_to_proto.py \
4241from protomotions .components .pose_lib import extract_kinematic_info
4342
4443from convert_soma23_to_proto import (
45- MJCF_BODY_NAMES ,
44+ SOMASKEL77_TO_MJCF_INDICES ,
4645 create_motion_from_soma23_data ,
4746)
48- from contact_detection import compute_contact_labels_from_pos_and_vel
4947from motion_filter import passes_exclude_motion_filter
5048
5149app = typer .Typer (pretty_exceptions_enable = False )
5250
53- # SOMASkeleton30 bone order (from SOMASkeleton30.bone_order_names_with_parents)
54- SOMASKEL30_BONE_NAMES = [
55- "Hips" , "Spine1" , "Spine2" , "Chest" ,
56- "Neck1" , "Neck2" , "Head" , "Jaw" , "LeftEye" , "RightEye" ,
57- "LeftShoulder" , "LeftArm" , "LeftForeArm" , "LeftHand" ,
58- "LeftHandThumbEnd" , "LeftHandMiddleEnd" ,
59- "RightShoulder" , "RightArm" , "RightForeArm" , "RightHand" ,
60- "RightHandThumbEnd" , "RightHandMiddleEnd" ,
61- "LeftLeg" , "LeftShin" , "LeftFoot" , "LeftToeBase" ,
62- "RightLeg" , "RightShin" , "RightFoot" , "RightToeBase" ,
63- ]
64-
65- SOMASKEL30_TO_MJCF_INDICES = [
66- SOMASKEL30_BONE_NAMES .index (name ) for name in MJCF_BODY_NAMES
67- ]
68-
6951
7052@app .command ()
7153def main (
@@ -82,7 +64,7 @@ def main(
8264 duration_height_seconds : float = typer .Option (0.6 ),
8365 yaml_output_name : Optional [str ] = None ,
8466):
85- """Convert SOMASkeleton30 NPZ motion files to ProtoMotions format."""
67+ """Convert SOMA NPZ motion files (77 joints) to ProtoMotions format."""
8668 device = torch .device ("cpu" )
8769 dtype = torch .float32
8870
@@ -119,33 +101,26 @@ def main(
119101 try :
120102 data = np .load (npz_file , allow_pickle = True )
121103
122- if "local_rot_mats" not in data or "root_positions" not in data :
123- print (
124- f"Skipping { npz_file } : missing 'local_rot_mats' or 'root_positions'"
125- )
126- continue
127-
128- local_rot_mats = data ["local_rot_mats" ]
129- root_pos = data ["root_positions" ]
104+ for required in ("local_rot_mats" , "posed_joints" ):
105+ if required not in data :
106+ print (f"Skipping { npz_file } : missing '{ required } '" )
107+ continue
130108
131- # Squeeze leading batch dimension if present
132- if local_rot_mats .ndim == 5 :
133- local_rot_mats = local_rot_mats [0 ]
134- if root_pos .ndim == 3 :
135- root_pos = root_pos [0 ]
109+ local_rot_mats = data ["local_rot_mats" ] # (T, 77, 3, 3)
110+ root_pos = data ["posed_joints" ][:, 0 , :] # (T, 3)
136111
137- if local_rot_mats .shape [1 ] != 30 :
112+ if local_rot_mats .shape [1 ] != 77 :
138113 print (
139- f"Skipping { npz_file } : expected 30 bodies , "
114+ f"Skipping { npz_file } : expected 77 joints , "
140115 f"got { local_rot_mats .shape [1 ]} "
141116 )
142117 continue
143118
144119 print (f" local_rot_mats: { local_rot_mats .shape } " )
145120 print (f" root_pos: { root_pos .shape } " )
146121
147- # Subselect 30→ 23 MJCF bodies
148- local_rot_mats = local_rot_mats [:, SOMASKEL30_TO_MJCF_INDICES , :, :]
122+ # Subselect to 23 MJCF bodies
123+ local_rot_mats = local_rot_mats [:, SOMASKEL77_TO_MJCF_INDICES , :, :]
149124
150125 # Downsample
151126 local_rot_mats = local_rot_mats [::downsample_factor ]
0 commit comments