[4de1c7]: / app / scratch / Leap2BVH_rot_vec.py

Download this file

256 lines (211 with data), 10.4 kB

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