Poser API documentation?
-
I'd like to programmatically pose the head in different positions (e.g., yaw by 5 degrees to the right, followed by pitch, etc) using the XPoser API. The workflow I'd like is something like this:
- Move head to a position
- Run EM FDTD simulation
- Run some postprocessing
- Repeat 1-3 for entire set of positions (~100 positions)
I have Sim4Life version 8.0. I see from this forum post that there exists an XPoser API, but it's not not yet in the s4l_v1. Would it be possible to get more information and examples with XPoser (like the apply_postures.py script in that post)? For instance, I see that we can apply a user-defined pose from a .pose file, but I'm wondering if I can just specify the pose programmatically instead. Thank you!
-
Unfortunately, you can't currently position the head directly using the API. Additionally, posing of the head is also only currently available for some of the Virtual Population models (including the V4.0 models Yoon-sun and Jeduk, and the prone Ella). We are investigating adding posing to the API for future releases.
Assuming you are using a model where the head is posable, the only option for the time being would be to generate the .pose files using Python, and then load them as in the post you have referenced.
I have an example to show how you can generate .pose files using Python scripting. The script does not depend on features of the Sim4Life API. Note that this example will only work for one bone, but it will allow you to specify many rotation angles in its frame of reference.
import json import numpy as np from collections import Counter, defaultdict from scipy.spatial.transform import Rotation as R # Configuration project_folder = r'C:\Users\...\\' # The default pose is model dependent. Generated by clicking 'Export' # after selecting the Poser for a ViP model when no stored postures baseline_pose_file = project_folder+'DefaultPose.pose' output_pose_file = project_folder+'NewPoses.pose' bone_to_pose = 'Humerus_cortical_right' # The angles are defined in the reference frame of the bone angles = [np.array([0, 10, 0]), np.array([0, 20, 0]), np.array([0, 30, 0])] # Get initial pose info from file # Helper function to modify key names from imported pose json data def make_unique(pairs): d = {} k_counter = Counter(defaultdict(int)) for k, v in pairs: suffix = str(k_counter[k]) if k_counter[k] else '' d[k+suffix] = v k_counter[k] += 1 return d # Process baseline pose file. Every bone is named 'bone', so give unique ID with open(baseline_pose_file) as f: old_bones = json.load(f, object_pairs_hook=make_unique) with open(baseline_pose_file) as f: original_pose_data = f.readlines() new_bones = {} for bone in old_bones['all_bones'].keys(): bone_id = bone[4:] bone_name = old_bones['all_bones'][bone]['name'] new_bones[bone_name] = old_bones['all_bones'][bone] new_bones[bone_name]['idn'] = bone_id old_bones['all_bones'] = new_bones # Get the transformation matrices so we can start applying rotation parse_transform_matrix = lambda s : np.fromstring(s.replace('\n',''),sep=" ").reshape(4,4) bone_dict = new_bones[bone_to_pose] ref_tr = parse_transform_matrix(bone_dict['ref_tr']) parent_ref_tr = parse_transform_matrix(bone_dict['parent_ref_tr']) # Calculate transform matrices for new .pose file posture_strings = [] for angle in angles: rotation_radians = angle*np.pi/180 r1 = R.from_rotvec(rotation_radians).as_matrix().T r2 = np.identity(4) r2[:3,:3] = r1 # Apply transforms abs_tr = np.matmul(ref_tr, r2) rel_tr = np.matmul(np.linalg.inv(parent_ref_tr), abs_tr) # Convert 4x4 transformation array to string writeable to .pose file to_pose_string = lambda s : np.array2string(s).replace('[','').replace(']','').replace('\n ',' \n')+' \n' abs_tr_str = to_pose_string(abs_tr) rel_tr_str = to_pose_string(rel_tr) # Write pose info to file # Defining json structure using dicts posture, postures, bones, bone = {}, {}, {}, {} bone['name'] = bone_to_pose bone['rel_tr'] = rel_tr_str bone['index'] = bone_dict['idn'] bone['abs_tr'] = abs_tr_str bone['parent_abs_tr'] = bone_dict['parent_ref_tr'] bones['bone'] = bone posture['name'] = str(angle) posture['bones'] = bones postures['posture'] = posture json_string = json.dumps(postures, indent=4) json_string = json_string[1:-1]+',' # removing first and last brackets and add , so multiple can be written posture_strings.append(json_string) posture_strings[-1] = posture_strings[-1].strip(',') # Write new .pose file. Posture data gets appended to the original data with open(output_pose_file,'w') as f: for line in original_pose_data[:-2]: f.write(line) f.write('\t"postures": {') for line in posture_strings: f.write(line) f.write('}\n}')