Switch to unified view

a b/app/scratch/Leap2BVH_rot_vec.py
1
import inspect
2
import os
3
import re
4
import sys
5
6
import numpy as np
7
# import math
8
from pymo.RotationUtil import vec2eul, rot2eul
9
10
from leapmotion_setup_rot import root_name, skeleton, framerate
11
from pymo.data import MocapData
12
13
src_dir = os.path.dirname(inspect.getfile(inspect.currentframe()))
14
# Windows and Linux
15
arch_dir = '../../lib/x64' if sys.maxsize > 2 ** 32 else '../../lib/x86'
16
17
sys.path.insert(0, os.path.abspath(os.path.join(src_dir, arch_dir)))
18
import Leap
19
20
21
class Leap2BVH:
22
    """
23
    A class to convert LeapMotion frames to PyMO data structure (MocapData) to be parsed to a BVH file
24
25
    Calculates translations (offsets) and rotation data for the joints
26
    """
27
28
    def __init__(self, filename=None):
29
        self._skeleton = {}
30
        self.bone_context = []
31
        self._motion_channels = []
32
        self._motions = []
33
        self.current_token = 0
34
        self.framerate = 0.0
35
        self.root_name = ''
36
        self.data = MocapData()
37
38
        self.do()
39
40
    def parse(self):
41
        self.data.skeleton = self._skeleton
42
        self.data.channel_names = self._motion_channels
43
        self.data.values = self._to_DataFrame()
44
        self.data.root_name = self.root_name
45
        self.data.framerate = self.framerate
46
47
        return self.data
48
49
    def do(self):
50
        self._skeleton = skeleton
51
        # self._motion_channels = motion_channels
52
        self.root_name = root_name
53
        self.framerate = framerate  # TODO: framerate
54
55
        # frame_count = 188#TODO:frame_count
56
        # frame_time = 0.0
57
        # self._motions = [()] * frame_count
58
59
        for key, value in self._skeleton.items():
60
            value['offsets'] = [0, 0, 0]
61
            for channel in value['channels']:
62
                self._motion_channels.append((key, channel))
63
64
        # for ii in range(frame_count):
65
        #     channel_values = []
66
        #     for key, value in self._skeleton.items():
67
        #         for channel in value['channels']:
68
        #             channel_values.append((key, channel, float(1)))
69
        #     self._motions[ii] = (frame_time, channel_values)
70
        #     frame_time = frame_time + framerate
71
72
    def add_frame(self, frame_id, hand):
73
        channel_values = []
74
        for key, value in self._skeleton.items():
75
            if frame_id == 0:
76
                # offsets
77
                if key == 'Leap_Root':
78
                    x_offset, y_offset, z_offset, _, _, _ = self._get_root_values()
79
                elif key == 'RightHand':
80
                    x_offset, y_offset, z_offset, _, _, _ = self._get_wrist_values(hand)
81
                elif 'End' in key:
82
                    # Workaround for getting motion data also from finger tip by adding a not used end
83
                    x_offset = y_offset = z_offset = 0.0
84
                else:
85
                    x_offset, y_offset, z_offset, _, _, _ = self._get_finger_values(key, hand)
86
                value['offsets'] = [x_offset, y_offset, z_offset]
87
88
                # print("pitch hand x = {}, angle hand x = {}".format(hand.basis.x_basis.pitch * Leap.RAD_TO_DEG,
89
                #                                                     hand.basis.x_basis.angle_to(Leap.Vector.x_axis) * Leap.RAD_TO_DEG))
90
91
            for channel in value['channels']:
92
                # motion data with rotations
93
                if key == 'Leap_Root':
94
                    x_pos, y_pos, z_pos, x_rot, y_rot, z_rot = self._get_root_values()
95
                elif key == 'RightHand':
96
                    x_pos, y_pos, z_pos, x_rot, y_rot, z_rot = self._get_wrist_values(hand)
97
                else:
98
                    x_pos, y_pos, z_pos, x_rot, y_rot, z_rot = self._get_finger_values(key, hand)
99
100
                if channel == 'Xposition':
101
                    channel_values.append((key, channel, x_pos))
102
                if channel == 'Yposition':
103
                    channel_values.append((key, channel, y_pos))
104
                if channel == 'Zposition':
105
                    channel_values.append((key, channel, z_pos))
106
                if channel == 'Xrotation':
107
                    channel_values.append((key, channel, x_rot))
108
                if channel == 'Yrotation':
109
                    channel_values.append((key, channel, y_rot))
110
                if channel == 'Zrotation':
111
                    channel_values.append((key, channel, z_rot))
112
113
        self._motions.append((frame_id, channel_values))
114
115
    @staticmethod
116
    def _get_root_values():
117
        return 0, 0, 0, 0, 0, 0
118
119
    def _get_wrist_values(self, hand):
120
        x_wrist = hand.wrist_position.x
121
        y_wrist = hand.wrist_position.y
122
        z_wrist = hand.wrist_position.z
123
124
        # rotation matrix from basis vectors
125
        rotmat = self._basis2rot(hand.basis)
126
        eul_x, eul_y, eul_z = rot2eul(rotmat)
127
128
        return \
129
            x_wrist, \
130
            y_wrist, \
131
            z_wrist, \
132
            eul_x * Leap.RAD_TO_DEG, \
133
            eul_y * Leap.RAD_TO_DEG, \
134
            eul_z * Leap.RAD_TO_DEG
135
136
    def _get_finger_values(self, key, hand):
137
        key, bone_number = self._split_key(key)
138
139
        fingerlist = hand.fingers.finger_type(self._get_finger_type(key))
140
141
        # vector between wrist and metacarpal proximal (carpals)
142
        if bone_number == 1 or ('Thumb' in key and bone_number == 2):
143
            bone = fingerlist[0].bone(self._get_bone_type(bone_number))
144
            x_wrist, y_wrist, z_wrist, _, _, _ = self._get_wrist_values(hand)
145
            # print("1: key: {}, bone_number: {}, bone: {}, prev_joint: {}"
146
            #       .format(key, bone_number, bone, bone.prev_joint))
147
            # print("p1_rot = [{}; {}; {}];".format(x_wrist, y_wrist, z_wrist))
148
            # print("p2_rot = [{}; {}; {}];".format(bone.prev_joint.x, bone.prev_joint.y, bone.prev_joint.z))
149
            # print("p3_rot = [{}; {}; {}];".format(bone.next_joint.x, bone.next_joint.y, bone.next_joint.z))
150
151
            # print('bone.next_joint.x = {}, bone.prev_joint.x = {}, x_wrist = {}, vec_prev = {}'.format(bone.next_joint.x, bone.prev_joint.x, x_wrist, vec_prev[0]))
152
            # print('bone.next_joint.y = {}, bone.prev_joint.y = {}, y_wrist = {}, vec_prev = {}'.format(bone.next_joint.y, bone.prev_joint.y, y_wrist, vec_prev[1]))
153
            # print('bone.next_joint.z = {}, bone.prev_joint.z = {}, z_wrist = {}, vec_prev = {}'.format(bone.next_joint.z, bone.prev_joint.z, z_wrist, vec_prev[2]))
154
155
            return \
156
                bone.prev_joint.x - x_wrist, \
157
                bone.prev_joint.y - y_wrist, \
158
                bone.prev_joint.z - z_wrist, \
159
                0.0, \
160
                0.0, \
161
                0.0
162
163
        # vector for bones metacarpal, proximal, intermediate, distal
164
        bone_prev = fingerlist[0].bone(self._get_bone_type(bone_number - 1))
165
166
        #  no rotation for finger tip
167
        if not bone_number == 5:
168
            bone_next = fingerlist[0].bone(self._get_bone_type(bone_number))
169
170
            # # rotation matrix from basis vectors
171
            # rotmat_prev = self._basis2rot(bone_prev.basis)
172
            # rotmat_next = self._basis2rot(bone_next.basis)
173
            #
174
            # # rotation matrix between rotmat_prev and rotmat_next by multiplying
175
            # eul_x, eul_y, eul_z = rot2eul(np.matmul(rotmat_next, np.transpose(rotmat_prev)))
176
177
            vec_prev = np.array([bone_prev.next_joint.x - bone_prev.prev_joint.x,
178
                                 bone_prev.next_joint.y - bone_prev.prev_joint.y,
179
                                 bone_prev.next_joint.z - bone_prev.prev_joint.z])
180
181
            vec_next = np.array([bone_next.next_joint.x - bone_next.prev_joint.x,
182
                                 bone_next.next_joint.y - bone_next.prev_joint.y,
183
                                 bone_next.next_joint.z - bone_next.prev_joint.z])
184
185
            eul_x, eul_y, eul_z = vec2eul(vec_prev, vec_next)
186
187
            return \
188
                bone_prev.next_joint.x - bone_prev.prev_joint.x, \
189
                bone_prev.next_joint.y - bone_prev.prev_joint.y, \
190
                bone_prev.next_joint.z - bone_prev.prev_joint.z, \
191
                eul_x * Leap.RAD_TO_DEG, \
192
                eul_y * Leap.RAD_TO_DEG, \
193
                eul_z * Leap.RAD_TO_DEG
194
195
        return \
196
            bone_prev.next_joint.x - bone_prev.prev_joint.x, \
197
            bone_prev.next_joint.y - bone_prev.prev_joint.y, \
198
            bone_prev.next_joint.z - bone_prev.prev_joint.z, \
199
            0.0, \
200
            0.0, \
201
            0.0
202
203
    @staticmethod
204
    def _split_key(key):
205
        key_split = re.split('(\d)', key)
206
        key = key_split[0]
207
        if key_split[-1] == '_Nub':
208
            return key, 5
209
        else:
210
            return key, int(key_split[1])
211
212
    @staticmethod
213
    def _get_finger_type(key):
214
        if key == 'RightHandThumb':
215
            return Leap.Finger.TYPE_THUMB
216
        if key == 'RightHandIndex':
217
            return Leap.Finger.TYPE_INDEX
218
        if key == 'RightHandMiddle':
219
            return Leap.Finger.TYPE_MIDDLE
220
        if key == 'RightHandRing':
221
            return Leap.Finger.TYPE_RING
222
        if key == 'RightHandPinky':
223
            return Leap.Finger.TYPE_PINKY
224
        else:
225
            raise Exception('Key ({}) did not match'.format(key))
226
227
    @staticmethod
228
    def _get_bone_type(bone_number):
229
        if bone_number == 4:
230
            return Leap.Bone.TYPE_DISTAL
231
        if bone_number == 3:
232
            return Leap.Bone.TYPE_INTERMEDIATE
233
        if bone_number == 2:
234
            return Leap.Bone.TYPE_PROXIMAL
235
        if bone_number == 1:
236
            return Leap.Bone.TYPE_METACARPAL
237
        else:
238
            raise Exception('bone number ({}) did not match'.format(bone_number))
239
240
    @staticmethod
241
    def _basis2rot(basis):
242
        return np.array([[basis.x_basis.x, basis.y_basis.x, basis.z_basis.x],
243
                        [basis.x_basis.y, basis.y_basis.y, basis.z_basis.y],
244
                        [basis.x_basis.z, basis.y_basis.z, basis.z_basis.z]])
245
246
    def _to_DataFrame(self):
247
        """Returns all of the channels parsed from the file as a pandas DataFrame"""
248
249
        import pandas as pd
250
        time_index = pd.to_timedelta([f[0] for f in self._motions], unit='s')
251
        frames = [f[1] for f in self._motions]
252
        channels = np.asarray([[channel[2] for channel in frame] for frame in frames])
253
        column_names = ['%s_%s' % (c[0], c[1]) for c in self._motion_channels]
254
255
        return pd.DataFrame(data=channels, index=time_index, columns=column_names)