diff --git a/README.md b/README.md index d27e896..eebb0b7 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,79 @@ # bvh-python Python module for parsing BVH (Biovision hierarchical data) mocap files + +#### Instance Bvh object from .bvh file +```python +>>> from bvh import Bvh +>>> with open('tests/test_freebvh.bvh') as f: +>>> mocap = Bvh(f.read()) +``` + #### Get mocap tree +```python +>>> [str(item) for item in mocap.root] +['HIERARCHY', 'ROOT mixamorig:Hips', 'MOTION', 'Frames: 69', 'Frame Time: 0.0333333'] +``` + #### Get ROOT OFFSET +```python +>>> next(mocap.root.filter('ROOT'))['OFFSET'] +['0.0000', '0.0000', '0.0000'] +``` + #### Get JOINT OFFSET +```python +>>> mocap.joint_offset('mixamorig:Head') +(-0.0, 10.3218, 3.1424) +``` + #### Get Frames +```python +>>> mocap.nframes +69 +``` + #### Get Frame Time +```python +>>> mocap.frame_time +0.0333333 +``` + #### Get JOINT CHANNELS +```python +>>> mocap.joint_channels('mixamorig:Neck') +['Zrotation', 'Yrotation', 'Xrotation'] +``` + #### Get Frame CHANNEL +```python +>>> mocap.frame_joint_channel(22, 'mixamorig:Spine', 'Xrotation') +11.8096 +``` + #### Get all JOINT names +```python +>>> mocap.get_joints_names() +['mixamorig:Hips', 'mixamorig:Spine', 'mixamorig:Spine1', 'mixamorig:Spine2', 'mixamorig:Neck', 'mixamorig:Head', 'mixamorig:HeadTop_End', 'mixamorig:LeftEye', 'mixamorig:RightEye', 'mixamorig:LeftShoulder', 'mixamorig:LeftArm', 'mixamorig:LeftForeArm', 'mixamorig:LeftHand', 'mixamorig:LeftHandMiddle1', 'mixamorig:LeftHandMiddle2', 'mixamorig:LeftHandMiddle3', 'mixamorig:LeftHandThumb1', 'mixamorig:LeftHandThumb2', 'mixamorig:LeftHandThumb3', 'mixamorig:LeftHandIndex1', 'mixamorig:LeftHandIndex2', 'mixamorig:LeftHandIndex3', 'mixamorig:LeftHandRing1', 'mixamorig:LeftHandRing2', 'mixamorig:LeftHandRing3', 'mixamorig:LeftHandPinky1', 'mixamorig:LeftHandPinky2', 'mixamorig:LeftHandPinky3', 'mixamorig:RightShoulder', 'mixamorig:RightArm', 'mixamorig:RightForeArm', 'mixamorig:RightHand', 'mixamorig:RightHandMiddle1', 'mixamorig:RightHandMiddle2', 'mixamorig:RightHandMiddle3', 'mixamorig:RightHandThumb1', 'mixamorig:RightHandThumb2', 'mixamorig:RightHandThumb3', 'mixamorig:RightHandIndex1', 'mixamorig:RightHandIndex2', 'mixamorig:RightHandIndex3', 'mixamorig:RightHandRing1', 'mixamorig:RightHandRing2', 'mixamorig:RightHandRing3', 'mixamorig:RightHandPinky1', 'mixamorig:RightHandPinky2', 'mixamorig:RightHandPinky3', 'mixamorig:RightUpLeg', 'mixamorig:RightLeg', 'mixamorig:RightFoot', 'mixamorig:RightToeBase', 'mixamorig:LeftUpLeg', 'mixamorig:LeftLeg', 'mixamorig:LeftFoot', 'mixamorig:LeftToeBase'] +``` + #### Get single JOINT name +```python +>>> mocap.get_joints_names()[17] +'mixamorig:LeftHandThumb2' +``` + #### Get JOINT parent index +```python +>>> mocap.joint_parent_index('mixamorig:Neck') +3 +``` + #### Get JOINT parent name +```python +>>> mocap.joint_parent('mixamorig:Head').name +'mixamorig:Neck' +``` + #### Search single item +```python +>>> [str(node) for node in mocap.search('JOINT', 'LeftShoulder')] +['JOINT LeftShoulder'] +``` + #### Search all items +```python +>>> [str(node) for node in mocap.search('JOINT')] +['JOINT mixamorig:Spine', 'JOINT mixamorig:Spine1', 'JOINT mixamorig:Spine2', 'JOINT mixamorig:Neck', 'JOINT mixamorig:Head', 'JOINT mixamorig:HeadTop_End', 'JOINT mixamorig:LeftEye', 'JOINT mixamorig:RightEye', 'JOINT mixamorig:LeftShoulder', 'JOINT mixamorig:LeftArm', 'JOINT mixamorig:LeftForeArm', 'JOINT mixamorig:LeftHand', 'JOINT mixamorig:LeftHandMiddle1', 'JOINT mixamorig:LeftHandMiddle2', 'JOINT mixamorig:LeftHandMiddle3', 'JOINT mixamorig:LeftHandThumb1', 'JOINT mixamorig:LeftHandThumb2', 'JOINT mixamorig:LeftHandThumb3', 'JOINT mixamorig:LeftHandIndex1', 'JOINT mixamorig:LeftHandIndex2', 'JOINT mixamorig:LeftHandIndex3', 'JOINT mixamorig:LeftHandRing1', 'JOINT mixamorig:LeftHandRing2', 'JOINT mixamorig:LeftHandRing3', 'JOINT mixamorig:LeftHandPinky1', 'JOINT mixamorig:LeftHandPinky2', 'JOINT mixamorig:LeftHandPinky3', 'JOINT mixamorig:RightShoulder', 'JOINT mixamorig:RightArm', 'JOINT mixamorig:RightForeArm', 'JOINT mixamorig:RightHand', 'JOINT mixamorig:RightHandMiddle1', 'JOINT mixamorig:RightHandMiddle2', 'JOINT mixamorig:RightHandMiddle3', 'JOINT mixamorig:RightHandThumb1', 'JOINT mixamorig:RightHandThumb2', 'JOINT mixamorig:RightHandThumb3', 'JOINT mixamorig:RightHandIndex1', 'JOINT mixamorig:RightHandIndex2', 'JOINT mixamorig:RightHandIndex3', 'JOINT mixamorig:RightHandRing1', 'JOINT mixamorig:RightHandRing2', 'JOINT mixamorig:RightHandRing3', 'JOINT mixamorig:RightHandPinky1', 'JOINT mixamorig:RightHandPinky2', 'JOINT mixamorig:RightHandPinky3', 'JOINT mixamorig:RightUpLeg', 'JOINT mixamorig:RightLeg', 'JOINT mixamorig:RightFoot', 'JOINT mixamorig:RightToeBase', 'JOINT mixamorig:LeftUpLeg', 'JOINT mixamorig:LeftLeg', 'JOINT mixamorig:LeftFoot', 'JOINT mixamorig:LeftToeBase'] +``` +#### Get joint's direct children +```python +>>> mocap.joint_direct_children('mixamorig:Hips') +[JOINT mixamorig:Spine, JOINT mixamorig:RightUpLeg, JOINT mixamorig:LeftUpLeg] +``` diff --git a/bvh.py b/bvh.py index 7c46a91..40fc5a2 100644 --- a/bvh.py +++ b/bvh.py @@ -113,6 +113,10 @@ def iterate_joints(joint): iterate_joints(next(self.root.filter('ROOT'))) return joints + def joint_direct_children(self, name): + joint = self.get_joint(name) + return [child for child in joint.filter('JOINT')] + def get_joint_index(self, name): return self.get_joints().index(self.get_joint(name)) @@ -141,9 +145,17 @@ def get_joint_channels_index(self, joint_name): index += int(joint['CHANNELS'][0]) raise LookupError('joint not found') + def get_joint_channel_index(self, joint, channel): + channels = self.joint_channels(joint) + if channel in channels: + channel_index = channels.index(channel) + else: + channel_index = -1 + return channel_index + def frame_joint_channel(self, frame_index, joint, channel, value=None): joint_index = self.get_joint_channels_index(joint) - channel_index = self.joint_channels(joint).index(channel) + channel_index = self.get_joint_channel_index(joint, channel) if channel_index == -1 and value is not None: return value return float(self.frames[frame_index][joint_index + channel_index]) @@ -152,7 +164,7 @@ def frame_joint_channels(self, frame_index, joint, channels, value=None): values = [] joint_index = self.get_joint_channels_index(joint) for channel in channels: - channel_index = self.joint_channels(joint).index(channel) + channel_index = self.get_joint_channel_index(joint, channel) if channel_index == -1 and value is not None: values.append(value) else: @@ -169,7 +181,7 @@ def frames_joint_channels(self, joint, channels, value=None): for frame in self.frames: values = [] for channel in channels: - channel_index = self.joint_channels(joint).index(channel) + channel_index = self.get_joint_channel_index(joint, channel) if channel_index == -1 and value is not None: values.append(value) else: diff --git a/tests/test_bvh.py b/tests/test_bvh.py index 1b9cdc1..08da46d 100644 --- a/tests/test_bvh.py +++ b/tests/test_bvh.py @@ -126,6 +126,11 @@ def test_frame_channel(self): self.assertEqual(mocap.frame_joint_channel(22, 'Neck', 'Xrotation'), -6.77) self.assertEqual(mocap.frame_joint_channel(22, 'Head', 'Yrotation'), 8.47) + def test_frame_channel_fallback(self): + with open('tests/test_mocapbank.bvh') as f: + mocap = Bvh(f.read()) + self.assertEqual(mocap.frame_joint_channel(22, 'Hips', 'Badrotation', 17), 17) + def test_frame_channel2(self): with open('tests/test_freebvh.bvh') as f: mocap = Bvh(f.read()) @@ -168,6 +173,14 @@ def test_frames_multi_channels(self): rotations = mocap.frames_joint_channels('Head', ['Xrotation', 'Yrotation', 'Zrotation']) self.assertEqual(len(rotations), mocap.nframes) - + def test_joint_children(self): + with open('tests/test_mocapbank.bvh') as f: + mocap = Bvh(f.read()) + self.assertEqual(mocap.joint_direct_children('Chest')[0].name, 'Chest2') + self.assertEqual(mocap.joint_direct_children('Hips')[0].name, 'Chest') + self.assertEqual(mocap.joint_direct_children('Hips')[1].name, 'LeftHip') + self.assertEqual(mocap.joint_direct_children('Hips')[2].name, 'RightHip') + self.assertEqual(mocap.joint_direct_children('RightWrist'), []) + if __name__ == '__main__': unittest.main()