Package: upc-pymotion
PyMotion: A Python Library for Motion DataPyMotion is a Python library that provides various functions for manipulating and processing motion data in NumPy or PyTorch. It is designed to facilitate the development of neural networks for character animation.Some features of PyMotion are:A comprehensive set of quaternion operations and conversions to other rotation representations, such as rotation matrix, axis-angle, euler, and 6D representationA dual quaternion representation for rigid displacements, which can help neural networks better understand poses, as proposed byAndreou et al. [2022]and later adopted byPonton et al. [2023]A continuous 6D rotation representation, as introduced byZhou et al. [2019]A BVH file reader and preprocessor for loading and transforming motion dataSkeletal operations such as Forward Kinematics for computing global joint positions from local joint rotations[Beta] A blender visualizer for debugging and visualizing character animationNumPy and PyTorch implementations and tests for all functionsContentsInstallationExamplesRoadmapLicenseInstallation[Optional]Install PyTorch using Pip as instructed in theirwebpage.Install PyMotion:pip install upc-pymotionExamplesRead and save a BVH fileimportnumpyasnpfrompymotion.io.bvhimportBVHbvh=BVH()bvh.load("test.bvh")print(bvh.data["names"])# Example Output: ['Hips', 'LeftHip', 'LeftKnee', 'LeftAnkle', 'LeftToe', 'RightHip', 'RightKnee', 'RightAnkle', 'RightToe', 'Chest', 'Chest3', 'Chest4', 'Neck', 'Head', 'LeftCollar', 'LeftShoulder', 'LeftElbow', 'LeftWrist', 'RightCollar', 'RightShoulder', 'RightElbow', 'RightWrist']# Move root joint to (0, 0, 0)local_rotations,local_positions,parents,offsets=bvh.get_data()local_positions[:,0,:]=np.zeros((local_positions.shape[0],3))bvh.set_data(local_rotations,local_positions)# Scale the skeletonbvh.set_scale(0.75)bvh.save("test_out.bvh")Compute world positions and rotations from a BVH fileNumPyfrompymotion.io.bvhimportBVHfrompymotion.ops.forward_kinematicsimportfkbvh=BVH()bvh.load("test.bvh")local_rotations,local_positions,parents,offsets=bvh.get_data()global_positions=local_positions[:,0,:]# root jointpos,rotmats=fk(local_rotations,global_positions,offsets,parents)PyTorchfrompymotion.io.bvhimportBVHfrompymotion.ops.forward_kinematics_torchimportfkimporttorchbvh=BVH()bvh.load("test.bvh")local_rotations,local_positions,parents,offsets=bvh.get_data()global_positions=local_positions[:,0,:]# root jointpos,rotmats=fk(torch.from_numpy(local_rotations),torch.from_numpy(global_positions),torch.from_numpy(offsets),torch.from_numpy(parents),)Quaternion conversion to other representationsNumPyimportpymotion.rotations.quatasquatimportnumpyasnpangles=np.array([np.pi/2,np.pi,np.pi/4])[...,np.newaxis]# angles.shape = [3, 1]axes=np.array([[1,0,0],[0,1,0],[0,0,1]])# axes.shape = [3, 3]q=quat.from_angle_axis(angles,axes)rotmats=quat.to_matrix(q)euler=quat.to_euler(q,np.array([["x","y","z"],["z","y","x"],["y","z","x"]]))euler_degrees=np.degrees(euler)scaled_axis=quat.to_scaled_angle_axis(q)PyTorchimportpymotion.rotations.quat_torchasquatimportnumpyasnpimporttorchangles=torch.Tensor([torch.pi/2,torch.pi,torch.pi/4]).unsqueeze(-1)# angles.shape = [3, 1]axes=torch.Tensor([[1,0,0],[0,1,0],[0,0,1]])# axes.shape = [3, 3]q=quat.from_angle_axis(angles,axes)rotmats=quat.to_matrix(q)euler=quat.to_euler(q,np.array([["x","y","z"],["z","y","x"],["y","z","x"]]))euler_degrees=torch.rad2deg(euler)scaled_axis=quat.to_scaled_angle_axis(q)Root-centered dual quaternions from a BVH fileNumPyfrompymotion.io.bvhimportBVHimportpymotion.ops.skeletonasskimportnumpyasnpbvh=BVH()bvh.load("test.bvh")local_rotations,local_positions,parents,offsets=bvh.get_data()root_dual_quats=sk.to_root_dual_quat(local_rotations,local_positions[:,0,:],parents,offsets)local_translations,local_rotations=sk.from_root_dual_quat(root_dual_quats,parents)global_positions=local_translations[:,0,:]offsets=local_translations.copy()offsets[:,0,:]=np.zeros((offsets.shape[0],3))PyTorchfrompymotion.io.bvhimportBVHimportpymotion.ops.skeleton_torchasskimporttorchbvh=BVH()bvh.load("test.bvh")local_rotations,local_positions,parents,offsets=bvh.get_data()root_dual_quats=sk.to_root_dual_quat(torch.from_numpy(local_rotations),torch.from_numpy(local_positions[:,0,:]),torch.from_numpy(parents),torch.from_numpy(offsets),)local_translations,local_rotations=sk.from_root_dual_quat(root_dual_quats,parents)global_positions=local_translations[:,0,:]offsets=local_translations.clone()offsets[:,0,:]=torch.zeros((offsets.shape[0],3))6D representation from a BVH fileNumPyfrompymotion.io.bvhimportBVHimportpymotion.rotations.ortho6dassixdbvh=BVH()bvh.load("test.bvh")local_rotations,_,_,_=bvh.get_data()continuous=sixd.from_quat(local_rotations)local_rotations=sixd.to_quat(continuous)PyTorchfrompymotion.io.bvhimportBVHimportpymotion.rotations.ortho6d_torchassixdimporttorchbvh=BVH()bvh.load("test.bvh")local_rotations,_,_,_=bvh.get_data()continuous=sixd.from_quat(torch.from_numpy(local_rotations))local_rotations=sixd.to_quat(continuous)Visualize a pose in BlenderOpen the Test Exitor window in BlenderOpen the the fileblender/pymotion_blender.pythat can be found in this repositoryRun the script (Blender will freeze)Run the following Python code in a seperate environment:frompymotion.io.bvhimportBVHfrompymotion.ops.forward_kinematicsimportfkfrompymotion.visualizer.blenderimportBlenderConnectionbvh=BVH()bvh.load("test.bvh")local_rotations,local_positions,parents,offsets=bvh.get_data()global_positions=local_positions[:,0,:]# root jointpos,_=fk(local_rotations,global_positions,offsets,parents)# Render pointsframe=0conn=BlenderConnection("127.0.0.1",2222)conn.render_points(pos[0])conn.close()Press ESC key in Blender to stop the serverRoadmapThis repository is authored and maintained byJose Luis Pontonas part of his Ph.D.Features will be added when new operations or rotation representations are needed in the development of research projects. Here it is a list of possible features and improvements for the future:Extend documentation and add examples in the description of each function.Include new animation importers such as FBXImprove the usability of the Blender visualization workflowInclude useful operations for data augmentation such as animation mirroringCreate an Inverse Kinematics moduleLicenseThis work is licensed under the MIT license. Please, see theLICENSEfor further details.