seanschneeweiss/RoSeMotion

About BVH

miooooooooooo opened this issue · 1 comments

Can you share the code about converting 3D coordinate of hand to .bvh file.

All files can be viewed within GitHub, its Open Source.
Take a look at

RoSeMotion/app/LeapData.py

Lines 172 to 205 in 9743e40

def _calculate_euler_angles(self, hand, joint_name):
initial_hand = self.first_frame.hands[0]
# special case for root and finger tip
if joint_name == self._root_name or not self._skeleton[joint_name]['children']:
return 0.0, 0.0, 0.0
if self.anybody_basis:
# compare basis to anybody basis
parent_initial_basis = self._get_basis_first_frame(self._skeleton[joint_name]['parent'])
initial_basis = self._get_basis_first_frame(joint_name)
else:
# compare basis to first frame from Leap Motion
parent_initial_basis = self._get_basis(initial_hand, self._skeleton[joint_name]['parent'])
initial_basis = self._get_basis(initial_hand, joint_name)
parent_basis = self._get_basis(hand, self._skeleton[joint_name]['parent'])
basis = self._get_basis(hand, joint_name)
# if joint_name == 'RightHand':
# print(basis)
# calculation of local rotation matrix - important!!!
rot = np.matmul(
np.matmul(
initial_basis, np.transpose(basis)
),
np.transpose(
np.matmul(
parent_initial_basis, np.transpose(parent_basis)
)
)
)
return rot2eul(rot)

Especially the case where we use the basis from the first frame for all ongoing angle calculations

RoSeMotion/app/LeapData.py

Lines 184 to 186 in 9743e40

# compare basis to first frame from Leap Motion
parent_initial_basis = self._get_basis(initial_hand, self._skeleton[joint_name]['parent'])
initial_basis = self._get_basis(initial_hand, joint_name)

We calculate the rotation matrices between a selected bone and its parent bone. The skeleton is defined here:

self.skeleton = \
{self.root_name: {'channels': [], 'children': ['RightElbow'], 'parent': None},
'RightElbow': {'channels': [], 'children': ['RightHand'], 'parent': self.root_name},
'RightHand': {'channels': [], 'children': ['RightHandThumb2', 'RightHandIndex1', 'RightHandMiddle1', 'RightHandRing1', 'RightHandPinky1'], 'parent': 'RightElbow'},
'RightHandIndex1': {'channels': [], 'children': ['RightHandIndex2'], 'parent': 'RightHand'},
'RightHandIndex2': {'channels': [], 'children': ['RightHandIndex3'], 'parent': 'RightHandIndex1'},
'RightHandIndex3': {'channels': [], 'children': ['RightHandIndex4'], 'parent': 'RightHandIndex2'},
'RightHandIndex4': {'channels': [], 'children': ['RightHandIndex4_Nub'], 'parent': 'RightHandIndex3'},
'RightHandIndex4_Nub': {'channels': [], 'children': [], 'parent': 'RightHandIndex4'},
'RightHandMiddle1': {'channels': [], 'children': ['RightHandMiddle2'], 'parent': 'RightHand'},
'RightHandMiddle2': {'channels': [], 'children': ['RightHandMiddle3'], 'parent': 'RightHandMiddle1'},
'RightHandMiddle3': {'channels': [], 'children': ['RightHandMiddle4'], 'parent': 'RightHandMiddle2'},
'RightHandMiddle4': {'channels': [], 'children': ['RightHandMiddle4_Nub'], 'parent': 'RightHandMiddle3'},
'RightHandMiddle4_Nub': {'channels': [], 'children': [], 'parent': 'RightHandMiddle4'},
'RightHandPinky1': {'channels': [], 'children': ['RightHandPinky2'], 'parent': 'RightHand'},
'RightHandPinky2': {'channels': [], 'children': ['RightHandPinky3'], 'parent': 'RightHandPinky1'},
'RightHandPinky3': {'channels': [], 'children': ['RightHandPinky4'], 'parent': 'RightHandPinky2'},
'RightHandPinky4': {'channels': [], 'children': ['RightHandPinky4_Nub'], 'parent': 'RightHandPinky3'},
'RightHandPinky4_Nub': {'channels': [], 'children': [], 'parent': 'RightHandPinky4'},
'RightHandRing1': {'channels': [], 'children': ['RightHandRing2'], 'parent': 'RightHand'},
'RightHandRing2': {'channels': [], 'children': ['RightHandRing3'], 'parent': 'RightHandRing1'},
'RightHandRing3': {'channels': [], 'children': ['RightHandRing4'], 'parent': 'RightHandRing2'},
'RightHandRing4': {'channels': [], 'children': ['RightHandRing4_Nub'], 'parent': 'RightHandRing3'},
'RightHandRing4_Nub': {'channels': [], 'children': [], 'parent': 'RightHandRing4'},
'RightHandThumb2': {'channels': [], 'children': ['RightHandThumb3'], 'parent': 'RightHand'},
'RightHandThumb3': {'channels': [], 'children': ['RightHandThumb4'], 'parent': 'RightHandThumb2'},
'RightHandThumb4': {'channels': [], 'children': ['RightHandThumb4_Nub'], 'parent': 'RightHandThumb3'},
'RightHandThumb4_Nub': {'channels': [], 'children': [], 'parent': 'RightHandThumb4'}}

To give an example: The root's basis is the identity matrix. The difference between the identity matrix and the forearm basis is the forearm basis itself.
If you look at the code, we have basis and parent_basis. basis is defined by the forearm basis, parent_basis is defined by root (identity). The difference is calculated by matrix multiplication: basis * parent_basis'
The ' stands for transposing the matrices.

In case of the hand palm, the parent_basis is the one from the forearm and the basis is defined by the palm's basis. And so on.

We calculate the rotation matrices dependent from the first recorded basis (extracted from the first recorded frame). That is why the calculation looks as follows:
rotation = (initial_basis * basis') * (parent_initial_basis, parent_basis')'

The angles required for the BVH file are calculated from the rotation matrices (euler angles).

Let me know if this helps you and what requires more explanation. I would kindly inform you that this is part of a hopefully soon to be published publication (Open Access), although we just started the peer review (so no guarantee).