biomechzoo 0.5.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- __init__.py +33 -0
- biomechzoo/__init__.py +0 -0
- biomechzoo/__main__.py +6 -0
- biomechzoo/biomech_ops/__init__.py +0 -0
- biomechzoo/biomech_ops/continuous_relative_phase_data.py +31 -0
- biomechzoo/biomech_ops/continuous_relative_phase_line.py +36 -0
- biomechzoo/biomech_ops/filter_data.py +58 -0
- biomechzoo/biomech_ops/filter_line.py +85 -0
- biomechzoo/biomech_ops/movement_onset.py +53 -0
- biomechzoo/biomech_ops/normalize_data.py +36 -0
- biomechzoo/biomech_ops/normalize_line.py +51 -0
- biomechzoo/biomech_ops/phase_angle_data.py +39 -0
- biomechzoo/biomech_ops/phase_angle_line.py +48 -0
- biomechzoo/biomechzoo.py +447 -0
- biomechzoo/conversion/__init__.py +0 -0
- biomechzoo/conversion/c3d2zoo_data.py +95 -0
- biomechzoo/conversion/mvnx2zoo_data.py +113 -0
- biomechzoo/conversion/opencap2zoo_data.py +23 -0
- biomechzoo/conversion/table2zoo_data.py +114 -0
- biomechzoo/imu/__init__.py +0 -0
- biomechzoo/imu/kinematics.py +0 -0
- biomechzoo/imu/tilt_algorithm.py +112 -0
- biomechzoo/linear_algebra_ops/__init__.py +0 -0
- biomechzoo/linear_algebra_ops/compute_magnitude_data.py +43 -0
- biomechzoo/mvn/__init__.py +0 -0
- biomechzoo/mvn/load_mvnx.py +514 -0
- biomechzoo/mvn/main_mvnx.py +75 -0
- biomechzoo/mvn/mvn.py +232 -0
- biomechzoo/mvn/mvnx_file_accessor.py +464 -0
- biomechzoo/processing/__init__.py +0 -0
- biomechzoo/processing/addchannel_data.py +71 -0
- biomechzoo/processing/addevent_data.py +116 -0
- biomechzoo/processing/explodechannel_data.py +69 -0
- biomechzoo/processing/partition_data.py +46 -0
- biomechzoo/processing/removechannel_data.py +46 -0
- biomechzoo/processing/removeevent_data.py +57 -0
- biomechzoo/processing/renamechannel_data.py +79 -0
- biomechzoo/processing/renameevent_data.py +62 -0
- biomechzoo/processing/split_trial_data.py +40 -0
- biomechzoo/statistics/eventval.py +118 -0
- biomechzoo/utils/__init__.py +0 -0
- biomechzoo/utils/batchdisp.py +21 -0
- biomechzoo/utils/compute_sampling_rate_from_time.py +25 -0
- biomechzoo/utils/engine.py +88 -0
- biomechzoo/utils/findfield.py +11 -0
- biomechzoo/utils/get_split_events.py +33 -0
- biomechzoo/utils/peak_sign.py +24 -0
- biomechzoo/utils/set_zoosystem.py +66 -0
- biomechzoo/utils/version.py +5 -0
- biomechzoo/utils/zload.py +57 -0
- biomechzoo/utils/zplot.py +61 -0
- biomechzoo/utils/zsave.py +54 -0
- biomechzoo-0.5.9.dist-info/METADATA +46 -0
- biomechzoo-0.5.9.dist-info/RECORD +58 -0
- biomechzoo-0.5.9.dist-info/WHEEL +5 -0
- biomechzoo-0.5.9.dist-info/entry_points.txt +2 -0
- biomechzoo-0.5.9.dist-info/licenses/LICENSE +21 -0
- biomechzoo-0.5.9.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Script to load an mvnx
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import xml.etree.ElementTree as ET
|
|
7
|
+
import collections
|
|
8
|
+
import numpy as np
|
|
9
|
+
from biomechzoo.mvn.mvnx_file_accessor import MvnxFileAccessor
|
|
10
|
+
from biomechzoo.mvn import mvn
|
|
11
|
+
|
|
12
|
+
# Xml namespace for mvnx files
|
|
13
|
+
ns = {'mvn': 'http://www.xsens.com/mvn/mvnx'}
|
|
14
|
+
|
|
15
|
+
# Map for easier conversion of foot contacts
|
|
16
|
+
FOOT_CONTACT_MAP = {'LeftFoot_Heel': {'type': 1, 'segment_index': mvn.SEGMENT_LEFT_FOOT, 'point_index': 2},
|
|
17
|
+
'LeftFoot_Toe': {'type': 1, 'segment_index': mvn.SEGMENT_LEFT_TOE, 'point_index': 1},
|
|
18
|
+
'RightFoot_Heel': {'type': 1, 'segment_index': mvn.SEGMENT_RIGHT_FOOT, 'point_index': 2},
|
|
19
|
+
'RightFoot_Toe': {'type': 1, 'segment_index': mvn.SEGMENT_RIGHT_TOE, 'point_index': 1}}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def load_mvnx(file_name):
|
|
23
|
+
"""
|
|
24
|
+
This function opens and reads the file as an mvnx formatted XML file
|
|
25
|
+
|
|
26
|
+
:param file_name: Name of the file to open, must have full path and .mvnx extension
|
|
27
|
+
:returns: A dictionary with the data from the mvnx file
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
mvnx_file = MvnxFileAccessor()
|
|
31
|
+
init_file_data(mvnx_file)
|
|
32
|
+
tree = ET.parse(file_name)
|
|
33
|
+
root = tree.getroot()
|
|
34
|
+
|
|
35
|
+
# Get the version
|
|
36
|
+
mvnx_file.file_data['meta_data']['version'] = root.get('version')
|
|
37
|
+
|
|
38
|
+
# Find the comment element
|
|
39
|
+
comment_element = root.find('mvn:comment', ns)
|
|
40
|
+
if comment_element is not None:
|
|
41
|
+
mvnx_file.file_data['meta_data']['comments'] = comment_element.text
|
|
42
|
+
|
|
43
|
+
# Find the subject element
|
|
44
|
+
subject_element = root.find('mvn:subject', ns)
|
|
45
|
+
mvnx_file.file_data['meta_data']['name'] = subject_element.get('label')
|
|
46
|
+
mvnx_file.file_data['meta_data']['color'] = subject_element.get('torsoColor')
|
|
47
|
+
mvnx_file.file_data['meta_data']['sample_rate'] = subject_element.get('frameRate')
|
|
48
|
+
mvnx_file.file_data['meta_data']['rec_date'] = subject_element.get('recDate')
|
|
49
|
+
mvnx_file.file_data['meta_data']['original_filename'] = subject_element.get('originalFilename')
|
|
50
|
+
mvnx_file.file_data['meta_data']['configuration'] = subject_element.get('configuration')
|
|
51
|
+
mvnx_file.file_data['meta_data']['scenario'] = subject_element.get('userScenario')
|
|
52
|
+
mvnx_file.file_data['meta_data']['quality'] = subject_element.get('processingQuality')
|
|
53
|
+
|
|
54
|
+
# Parse the segments and their points
|
|
55
|
+
segments_element = subject_element.find('mvn:segments', ns)
|
|
56
|
+
segment_elements = segments_element.findall('mvn:segment', ns)
|
|
57
|
+
mvnx_file.file_data['segments'] = parse_segments(segment_elements)
|
|
58
|
+
mvnx_file.create_index_to_segment_dict() # for later convenience on retrieving segment names
|
|
59
|
+
|
|
60
|
+
# Parse sensor information
|
|
61
|
+
sensors_element = subject_element.find('mvn:sensors', ns)
|
|
62
|
+
if sensors_element is not None:
|
|
63
|
+
mvnx_file.file_data['sensors'] = parse_sensor(sensors_element, mvnx_file.file_data['segments']['names'])
|
|
64
|
+
|
|
65
|
+
# Parse joint information
|
|
66
|
+
joints_element = subject_element.find('mvn:joints', ns)
|
|
67
|
+
if joints_element is not None:
|
|
68
|
+
mvnx_file.file_data['joints'] = parse_joints(joints_element, mvnx_file.file_data['segments'])
|
|
69
|
+
|
|
70
|
+
# Parse ergo joint information
|
|
71
|
+
ergo_joints_element = subject_element.find('mvn:ergonomicJointAngles', ns)
|
|
72
|
+
if ergo_joints_element is not None:
|
|
73
|
+
mvnx_file.file_data['ergo_joints'] = parse_ergo_joints(ergo_joints_element)
|
|
74
|
+
|
|
75
|
+
# Parse foot contact
|
|
76
|
+
foot_contact_definitions_element = subject_element.find('mvn:footContactDefinition', ns)
|
|
77
|
+
if foot_contact_definitions_element is not None:
|
|
78
|
+
foot_contact_definition_elements = foot_contact_definitions_element.findall('mvn:contactDefinition', ns)
|
|
79
|
+
for foot_contact_definition_element in foot_contact_definition_elements:
|
|
80
|
+
contact_label = foot_contact_definition_element.get('label')
|
|
81
|
+
contact_index = int(foot_contact_definition_element.get('index'))
|
|
82
|
+
mvnx_file.file_data['foot_contact_def'][contact_index] = FOOT_CONTACT_MAP[contact_label]
|
|
83
|
+
|
|
84
|
+
# Parse the finger segments and their points
|
|
85
|
+
for side in mvnx_file.file_data['finger_segments']['elements']:
|
|
86
|
+
finger_segment_elements = subject_element.find('mvn:fingerTrackingSegments' + side.capitalize(), ns)
|
|
87
|
+
if finger_segment_elements is not None:
|
|
88
|
+
finger_segments = parse_segments(finger_segment_elements)
|
|
89
|
+
mvnx_file.file_data['finger_segments']['elements'][side] = finger_segments['elements']
|
|
90
|
+
mvnx_file.file_data['finger_segments']['names'][side] = finger_segments['names']
|
|
91
|
+
|
|
92
|
+
# Parse finger joint information
|
|
93
|
+
for side in mvnx_file.file_data['finger_joints']['elements']:
|
|
94
|
+
finger_joints_element = subject_element.find('mvn:fingerTrackingJoints' + side.capitalize(), ns)
|
|
95
|
+
if finger_joints_element is not None:
|
|
96
|
+
finger_segments = {'names': mvnx_file.file_data['finger_segments']['names'][side],
|
|
97
|
+
'elements': mvnx_file.file_data['finger_segments']['elements'][side]}
|
|
98
|
+
finger_joints = parse_joints(finger_joints_element, finger_segments)
|
|
99
|
+
mvnx_file.file_data['finger_joints']['elements'][side] = finger_joints['elements']
|
|
100
|
+
mvnx_file.file_data['finger_joints']['names'][side] = finger_joints['names']
|
|
101
|
+
|
|
102
|
+
# At last, parse the actual frames
|
|
103
|
+
frames_element = subject_element.find('mvn:frames', ns)
|
|
104
|
+
mvnx_file.file_data['frames'], mvnx_file.file_data['tpose'],\
|
|
105
|
+
mvnx_file.file_data['tpose_isb'], mvnx_file.file_data['identity'] = parse_frames(frames_element, mvnx_file)
|
|
106
|
+
|
|
107
|
+
mvnx_file.reset_frame_window() # Reset window so frame count displays total frame count
|
|
108
|
+
return mvnx_file
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def parse_sensor(sensors_element, segment_names):
|
|
112
|
+
"""
|
|
113
|
+
Parse the sensor element
|
|
114
|
+
|
|
115
|
+
:param sensors_element: The joint element to parse
|
|
116
|
+
:param segment_names: a list with the segment names
|
|
117
|
+
:return: a dictionary with sensor data indexed by sensor name and a list with the sensor names
|
|
118
|
+
"""
|
|
119
|
+
sensor_elements = sensors_element.findall('mvn:sensor', ns)
|
|
120
|
+
sensor_number = 0
|
|
121
|
+
sensors = {}
|
|
122
|
+
sensor_names = []
|
|
123
|
+
for sensor_element in sensor_elements:
|
|
124
|
+
sensor_name = sensor_element.get('label')
|
|
125
|
+
sensor_names.append(sensor_name)
|
|
126
|
+
sensor = {'type': 'Sensor',
|
|
127
|
+
'label': sensor_name,
|
|
128
|
+
'info': {
|
|
129
|
+
'sensor_number': sensor_number,
|
|
130
|
+
'sensor_location': segment_names.index(sensor_name) + 1}}
|
|
131
|
+
|
|
132
|
+
sensors[sensor_name] = sensor
|
|
133
|
+
sensor_number += 1
|
|
134
|
+
return {'names': sensor_names, 'elements': sensors}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def parse_joints(joints_element, segments):
|
|
138
|
+
"""
|
|
139
|
+
Parse the joint element
|
|
140
|
+
|
|
141
|
+
:param joints_element: The joint element to parse
|
|
142
|
+
:param segments: The dictionary with segment data
|
|
143
|
+
:return: a dictionary with joint data indexed by joint name and a list with the joint names
|
|
144
|
+
"""
|
|
145
|
+
joint_elements = joints_element.findall('mvn:joint', ns)
|
|
146
|
+
|
|
147
|
+
joints = []
|
|
148
|
+
joint_names = []
|
|
149
|
+
|
|
150
|
+
for joint_element in joint_elements:
|
|
151
|
+
joint = {'label': joint_element.get('label')}
|
|
152
|
+
joint_names.append(joint['label'])
|
|
153
|
+
|
|
154
|
+
segment1_index, point1_index = get_connector_indices(joint_element, 'mvn:connector1', segments)
|
|
155
|
+
segment2_index, point2_index = get_connector_indices(joint_element, 'mvn:connector2', segments)
|
|
156
|
+
joint['seg_points'] = np.array([[segment1_index, point1_index], [segment2_index, point2_index]])
|
|
157
|
+
joints.append(joint)
|
|
158
|
+
|
|
159
|
+
return {'names': joint_names, 'elements': joints}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def parse_ergo_joints(ergo_joints_element):
|
|
163
|
+
"""
|
|
164
|
+
Parse the ergo joint element
|
|
165
|
+
|
|
166
|
+
:param ergo_joints_element: The joint element to parse
|
|
167
|
+
:return: a dictionary with ergo joint data indexed by joint name and a list with the ergo joint names
|
|
168
|
+
"""
|
|
169
|
+
ergo_joint_elements = ergo_joints_element.findall('mvn:ergonomicJointAngle', ns)
|
|
170
|
+
|
|
171
|
+
ergo_joints = []
|
|
172
|
+
ergo_joint_names = []
|
|
173
|
+
for ergo_joint_index in range(len(ergo_joint_elements)):
|
|
174
|
+
ergo_joint_element = ergo_joint_elements[ergo_joint_index]
|
|
175
|
+
ergo_joint = {'label': ergo_joint_element.get('label'),
|
|
176
|
+
'index': ergo_joint_index,
|
|
177
|
+
'parent_segment': ergo_joint_element.get('parentSegment'),
|
|
178
|
+
'child_segment': ergo_joint_element.get('childSegment')}
|
|
179
|
+
ergo_joint_names.append(ergo_joint['label'])
|
|
180
|
+
ergo_joints.append(ergo_joint)
|
|
181
|
+
|
|
182
|
+
return {'names': ergo_joint_names, 'elements': ergo_joints}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def parse_segments(segment_elements):
|
|
186
|
+
"""
|
|
187
|
+
Parse the segment element
|
|
188
|
+
|
|
189
|
+
:param segment_elements: The segment element to parse
|
|
190
|
+
:return: a dictionary a list with the segment names and segment data indexed by segment name
|
|
191
|
+
"""
|
|
192
|
+
segments = collections.OrderedDict()
|
|
193
|
+
segment_names = []
|
|
194
|
+
|
|
195
|
+
for segment_index in range(len(segment_elements)):
|
|
196
|
+
segment_element = segment_elements[segment_index]
|
|
197
|
+
segment_name = segment_element.get('label')
|
|
198
|
+
segment_names.append(segment_name)
|
|
199
|
+
segment = {'points_mvn': collections.OrderedDict(),
|
|
200
|
+
'type': 'Segment',
|
|
201
|
+
'info': {},
|
|
202
|
+
'label': segment_name}
|
|
203
|
+
|
|
204
|
+
info = {'segment_number': segment_index,
|
|
205
|
+
'point_label_from_index': {},
|
|
206
|
+
'point_origin': '',
|
|
207
|
+
'adjacent_joints': []}
|
|
208
|
+
|
|
209
|
+
points_element = segment_element.find('mvn:points', ns)
|
|
210
|
+
point_elements = points_element.findall('mvn:point', ns)
|
|
211
|
+
|
|
212
|
+
point_labels_from_index = {}
|
|
213
|
+
for point_index in range(len(point_elements)):
|
|
214
|
+
point_element = point_elements[point_index]
|
|
215
|
+
|
|
216
|
+
point_name = point_element.get('label')
|
|
217
|
+
point_labels_from_index[point_index] = point_name
|
|
218
|
+
|
|
219
|
+
pos_b = point_element.find('mvn:pos_b', ns)
|
|
220
|
+
segment['points_mvn'][point_name] = np.array([float(pos) for pos in pos_b.text.split(' ')])
|
|
221
|
+
|
|
222
|
+
# if the point offset is really small, consider it the origin (could just pick 1st or all 0's as well)
|
|
223
|
+
if np.sqrt(np.sum([sq * sq for sq in segment['points_mvn'][point_name]])) < 0.00001:
|
|
224
|
+
info['point_origin'] = point_name
|
|
225
|
+
|
|
226
|
+
# wild guess...
|
|
227
|
+
if point_name[0] == 'j':
|
|
228
|
+
info['adjacent_joints'].append(point_name)
|
|
229
|
+
|
|
230
|
+
info['point_label_from_index'] = point_labels_from_index
|
|
231
|
+
segment['info'] = info
|
|
232
|
+
segments[segment_name] = segment
|
|
233
|
+
|
|
234
|
+
return {'names': segment_names, 'elements': segments}
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def get_connector_indices(joint_element, connector, segments):
|
|
238
|
+
connector_element = joint_element.find(connector, ns)
|
|
239
|
+
tokens = connector_element.text.split('/')
|
|
240
|
+
segment_index = segments['names'].index(tokens[0])
|
|
241
|
+
point_index = -1
|
|
242
|
+
for key, value in segments['elements'][tokens[0]]['info']['point_label_from_index'].items():
|
|
243
|
+
if value == tokens[1]:
|
|
244
|
+
point_index = key
|
|
245
|
+
break
|
|
246
|
+
|
|
247
|
+
return segment_index, point_index
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def parse_frames(frames_element, mvnx_file):
|
|
251
|
+
"""
|
|
252
|
+
Parse the frames element
|
|
253
|
+
|
|
254
|
+
:param frames_element: The frames element to parse
|
|
255
|
+
:param mvnx_file: a dictionary containing, among others, a list of names
|
|
256
|
+
:return: a dictionary with frames data
|
|
257
|
+
"""
|
|
258
|
+
frames = {'time': [],
|
|
259
|
+
'segment_data': [],
|
|
260
|
+
'sensor_data': [],
|
|
261
|
+
'joint_data': [],
|
|
262
|
+
'joint_data_xzy': [],
|
|
263
|
+
'ergo_joint_data': [],
|
|
264
|
+
'ergo_joint_data_xzy': [],
|
|
265
|
+
'contacts_data': [],
|
|
266
|
+
'finger_segment_data': [],
|
|
267
|
+
'finger_joint_data': {'left': [], 'right': []},
|
|
268
|
+
'finger_joint_data_xzy': {'left': [], 'right': []}}
|
|
269
|
+
tpose = {}
|
|
270
|
+
tpose_isb = {}
|
|
271
|
+
identity = {}
|
|
272
|
+
|
|
273
|
+
get_count = lambda element: int(element) if element is not None else None
|
|
274
|
+
frames['segment_count'] = get_count(frames_element.get('segmentCount'))
|
|
275
|
+
frames['sensor_count'] = get_count(frames_element.get('sensorCount'))
|
|
276
|
+
frames['joint_count'] = get_count(frames_element.get('jointCount'))
|
|
277
|
+
frames['finger_joint_count'] = get_count(frames_element.get('fingerJointCount'))
|
|
278
|
+
frame_elements = frames_element.findall('mvn:frame', ns)
|
|
279
|
+
for frame_element in frame_elements:
|
|
280
|
+
if frame_element.get('type') == 'normal':
|
|
281
|
+
frames['time'].append(frame_element.get('time'))
|
|
282
|
+
frames['joint_data'].append(
|
|
283
|
+
get_joint_data_from_frame(frame_element, 'jointAngle', mvnx_file.file_data['joints']['names']))
|
|
284
|
+
frames['joint_data_xzy'].append(
|
|
285
|
+
get_joint_data_from_frame(frame_element, 'jointAngleXZY', mvnx_file.file_data['joints']['names']))
|
|
286
|
+
frames['ergo_joint_data'].append(
|
|
287
|
+
get_joint_data_from_frame(frame_element, 'jointAngleErgo', mvnx_file.file_data['ergo_joints']['names']))
|
|
288
|
+
frames['ergo_joint_data_xzy'].append(
|
|
289
|
+
get_joint_data_from_frame(frame_element, 'jointAngleErgoXZY', mvnx_file.file_data['ergo_joints']['names']))
|
|
290
|
+
frames['segment_data'].append(
|
|
291
|
+
get_segment_data_from_frame(frame_element, mvnx_file.file_data['segments']['names']))
|
|
292
|
+
frames['sensor_data'].append(
|
|
293
|
+
get_sensor_data_from_frame(frame_element, mvnx_file.file_data['sensors']['names']))
|
|
294
|
+
frames['contacts_data'].append(
|
|
295
|
+
get_contact_data_from_frame(frame_element, mvnx_file.file_data['foot_contact_def']))
|
|
296
|
+
frames['finger_segment_data'].append(
|
|
297
|
+
get_finger_data_from_frame(frame_element, mvnx_file.file_data['finger_segments']['names']))
|
|
298
|
+
for side in frames['finger_joint_data']:
|
|
299
|
+
element_name = 'jointAngleFingers' + side.capitalize()
|
|
300
|
+
frames['finger_joint_data'][side].append(get_joint_data_from_frame(
|
|
301
|
+
frame_element, element_name, mvnx_file.file_data['finger_joints']['names'][side]))
|
|
302
|
+
element_name = 'jointAngleFingers' + side.capitalize() + 'XZY'
|
|
303
|
+
frames['finger_joint_data_xzy'][side].append(get_joint_data_from_frame(
|
|
304
|
+
frame_element, element_name, mvnx_file.file_data['finger_joints']['names'][side]))
|
|
305
|
+
elif frame_element.get('type') == 'tpose':
|
|
306
|
+
tpose = get_t_pose_data_from_frame(frame_element, mvnx_file.file_data['segments']['names'])
|
|
307
|
+
elif frame_element.get('type') == 'tpose-isb':
|
|
308
|
+
tpose_isb = get_t_pose_data_from_frame(frame_element, mvnx_file.file_data['segments']['names'])
|
|
309
|
+
elif frame_element.get('type') == 'identity':
|
|
310
|
+
identity = get_t_pose_data_from_frame(frame_element, mvnx_file.file_data['segments']['names'])
|
|
311
|
+
|
|
312
|
+
return frames, tpose, tpose_isb, identity
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def get_joint_data_from_frame(frame_element, joint_element_name, joint_names):
|
|
316
|
+
"""
|
|
317
|
+
Extract joint data from a frame
|
|
318
|
+
|
|
319
|
+
:param frame_element: The frame element to process
|
|
320
|
+
:param joint_element_name: The name of the frame element to process
|
|
321
|
+
:param joint_names: a list with the joint names
|
|
322
|
+
:return: a dictionary with joint data indexed by joint name
|
|
323
|
+
"""
|
|
324
|
+
|
|
325
|
+
joint_data = collections.OrderedDict()
|
|
326
|
+
|
|
327
|
+
angles = frame_element_as_floats(frame_element, joint_element_name)
|
|
328
|
+
|
|
329
|
+
for index in range(len(joint_names)):
|
|
330
|
+
joint_data[joint_names[index]] = get_3d_vector(angles, index)
|
|
331
|
+
|
|
332
|
+
return joint_data
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def get_t_pose_data_from_frame(frame_element, segment_names):
|
|
336
|
+
"""
|
|
337
|
+
Extract segment data from a frame
|
|
338
|
+
|
|
339
|
+
:param frame_element: The frame element to process
|
|
340
|
+
:param segment_names: a list with the segment names
|
|
341
|
+
:return: a dictionary with segment data indexed by segment name
|
|
342
|
+
"""
|
|
343
|
+
|
|
344
|
+
t_pose = {'segments_counts': len(segment_names), 'segments': []}
|
|
345
|
+
|
|
346
|
+
orientations = frame_element_as_floats(frame_element, 'orientation')
|
|
347
|
+
offsets = frame_element_as_floats(frame_element, 'position')
|
|
348
|
+
|
|
349
|
+
for index in range(len(segment_names)):
|
|
350
|
+
segment = {'pos_g': get_3d_vector(offsets, index),
|
|
351
|
+
'q_gb': get_4d_vector(orientations, index)}
|
|
352
|
+
t_pose['segments'].append(segment)
|
|
353
|
+
|
|
354
|
+
return t_pose
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def get_segment_data_from_frame(frame_element, segment_names):
|
|
358
|
+
"""
|
|
359
|
+
Extract segment data from a frame
|
|
360
|
+
|
|
361
|
+
:param frame_element: The frame element to process
|
|
362
|
+
:param segment_names: a list with the segment names
|
|
363
|
+
:return: a dictionary with segment data indexed by segment name
|
|
364
|
+
"""
|
|
365
|
+
|
|
366
|
+
segment_data = collections.OrderedDict()
|
|
367
|
+
|
|
368
|
+
orientations = frame_element_as_floats(frame_element, 'orientation')
|
|
369
|
+
offsets = frame_element_as_floats(frame_element, 'position')
|
|
370
|
+
velocities = frame_element_as_floats(frame_element, 'velocity')
|
|
371
|
+
accelerations = frame_element_as_floats(frame_element, 'acceleration')
|
|
372
|
+
angular_velocity = frame_element_as_floats(frame_element, 'angularVelocity')
|
|
373
|
+
angular_acceleration = frame_element_as_floats(frame_element, 'angularAcceleration')
|
|
374
|
+
|
|
375
|
+
for index in range(len(segment_names)):
|
|
376
|
+
segment_name = segment_names[index]
|
|
377
|
+
segment_data[segment_name] = collections.OrderedDict()
|
|
378
|
+
segment_data[segment_name]['ori'] = get_4d_vector(orientations, index)
|
|
379
|
+
segment_data[segment_name]['pos'] = get_3d_vector(offsets, index)
|
|
380
|
+
segment_data[segment_name]['vel'] = get_3d_vector(velocities, index)
|
|
381
|
+
segment_data[segment_name]['acc'] = get_3d_vector(accelerations, index)
|
|
382
|
+
segment_data[segment_name]['ang_vel'] = get_3d_vector(angular_velocity, index)
|
|
383
|
+
segment_data[segment_name]['ang_acc'] = get_3d_vector(angular_acceleration, index)
|
|
384
|
+
|
|
385
|
+
center_of_mass = frame_element_as_floats(frame_element, 'centerOfMass')
|
|
386
|
+
if center_of_mass:
|
|
387
|
+
segment_data['com'] = {'pos': [], 'vel': [], 'acc': []}
|
|
388
|
+
index = 0
|
|
389
|
+
for com_field in segment_data['com']:
|
|
390
|
+
segment_data['com'][com_field] = get_3d_vector(center_of_mass, index)
|
|
391
|
+
index += 1
|
|
392
|
+
|
|
393
|
+
return segment_data
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def get_sensor_data_from_frame(frame_element, sensor_names):
|
|
397
|
+
"""
|
|
398
|
+
Extract sensor data from a frame
|
|
399
|
+
|
|
400
|
+
:param frame_element: The frame element to process
|
|
401
|
+
:param sensor_names: a list with the segment names
|
|
402
|
+
:return: a dictionary with sensor data indexed by sensor name
|
|
403
|
+
"""
|
|
404
|
+
|
|
405
|
+
sensor_data = collections.OrderedDict()
|
|
406
|
+
|
|
407
|
+
orientations = frame_element_as_floats(frame_element, 'sensorOrientation')
|
|
408
|
+
free_accelerations = frame_element_as_floats(frame_element, 'sensorFreeAcceleration')
|
|
409
|
+
magnetic_field = frame_element_as_floats(frame_element, 'sensorMagneticField')
|
|
410
|
+
|
|
411
|
+
for index in range(len(sensor_names)):
|
|
412
|
+
sensor_name = sensor_names[index]
|
|
413
|
+
sensor_data[sensor_name] = collections.OrderedDict()
|
|
414
|
+
sensor_data[sensor_name]["ori"] = get_4d_vector(orientations, index)
|
|
415
|
+
sensor_data[sensor_name]["mag"] = get_4d_vector(magnetic_field, index)
|
|
416
|
+
sensor_data[sensor_name]["acc"] = get_3d_vector(free_accelerations, index)
|
|
417
|
+
|
|
418
|
+
return sensor_data
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def get_finger_data_from_frame(frame_element, finger_segment_names):
|
|
422
|
+
"""
|
|
423
|
+
Extract finger data from a frame
|
|
424
|
+
|
|
425
|
+
:param frame_element: The frame element to process
|
|
426
|
+
:param finger_segment_names: a list with the finger segment names
|
|
427
|
+
:return: a dictionary with finger data indexed by finger name
|
|
428
|
+
"""
|
|
429
|
+
|
|
430
|
+
finger_data = {'left': {}, 'right': {}}
|
|
431
|
+
|
|
432
|
+
for side in finger_data:
|
|
433
|
+
orientations = frame_element_as_floats(frame_element, 'orientationFingers' + side.capitalize())
|
|
434
|
+
offsets = frame_element_as_floats(frame_element, 'positionFingers' + side.capitalize())
|
|
435
|
+
|
|
436
|
+
for index in range(len(finger_segment_names[side])):
|
|
437
|
+
finger_name = finger_segment_names[side][index]
|
|
438
|
+
finger_data[side][finger_name] = collections.OrderedDict()
|
|
439
|
+
finger_data[side][finger_name]["ori"] = get_4d_vector(orientations, index)
|
|
440
|
+
finger_data[side][finger_name]["pos"] = get_3d_vector(offsets, index)
|
|
441
|
+
|
|
442
|
+
return finger_data
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def get_contact_data_from_frame(frame_element, foot_contact_def):
|
|
446
|
+
"""
|
|
447
|
+
Extract contact data from a frame
|
|
448
|
+
|
|
449
|
+
:param frame_element: The frame element to process
|
|
450
|
+
:param foot_contact_def: a list with the foot contact definitions
|
|
451
|
+
:return: a list with contacts
|
|
452
|
+
"""
|
|
453
|
+
|
|
454
|
+
contact_data = []
|
|
455
|
+
element_value = frame_element.find('mvn:footContacts', ns)
|
|
456
|
+
if element_value is not None:
|
|
457
|
+
contacts = [int(value) for value in element_value.text.split(' ')]
|
|
458
|
+
|
|
459
|
+
for index in range(len(contacts)):
|
|
460
|
+
if contacts[index] == 1:
|
|
461
|
+
contact_data.append(foot_contact_def[index])
|
|
462
|
+
|
|
463
|
+
return contact_data
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def frame_element_as_floats(frame_element, element):
|
|
467
|
+
"""
|
|
468
|
+
Find a named element in a frame element, extract the text from it, split that and return
|
|
469
|
+
the values as an array of floats
|
|
470
|
+
|
|
471
|
+
:param frame_element: The mvnx frame element to process
|
|
472
|
+
:param element: The name of the sub element to find
|
|
473
|
+
:return: an array of floating point values
|
|
474
|
+
"""
|
|
475
|
+
|
|
476
|
+
element_value = frame_element.find('mvn:' + element, ns)
|
|
477
|
+
return [float(value) for value in element_value.text.split(' ')] if element_value is not None else []
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def get_4d_vector(raw_vector, index):
|
|
481
|
+
return np.array(raw_vector[index * 4:index * 4 + 4])
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def get_3d_vector(raw_vector, index):
|
|
485
|
+
return np.array(raw_vector[index * 3:index * 3 + 3])
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def init_file_data(mvnx_file):
|
|
489
|
+
meta_data = {'version': '',
|
|
490
|
+
'original_filename': '',
|
|
491
|
+
'rec_date': '',
|
|
492
|
+
'name': '',
|
|
493
|
+
'color': '',
|
|
494
|
+
'comments': '',
|
|
495
|
+
'scenario': '',
|
|
496
|
+
'quality': '',
|
|
497
|
+
'sample_rate': 240}
|
|
498
|
+
|
|
499
|
+
mvnx_file.file_data = {'segments': {},
|
|
500
|
+
'finger_segments': {'names': {'left': {}, 'right': {}},
|
|
501
|
+
'elements': {'left': collections.OrderedDict(),
|
|
502
|
+
'right': collections.OrderedDict()}},
|
|
503
|
+
'sensors': {'names': [], 'elements': collections.OrderedDict()},
|
|
504
|
+
'joints': {'names': [], 'elements': collections.OrderedDict()},
|
|
505
|
+
'ergo_joints': {'names': [], 'elements': collections.OrderedDict()},
|
|
506
|
+
'finger_joints': {'names': {'left': {}, 'right': {}},
|
|
507
|
+
'elements': {'left': collections.OrderedDict(),
|
|
508
|
+
'right': collections.OrderedDict()}},
|
|
509
|
+
'foot_contact_def': {},
|
|
510
|
+
'frames': {},
|
|
511
|
+
'tpose': {},
|
|
512
|
+
'tpose_isb': {},
|
|
513
|
+
'identity': {},
|
|
514
|
+
'meta_data': meta_data}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import argparse
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
from biomechzoo.mvn.load_mvnx import load_mvnx
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Convert mvnx file to python data
|
|
8
|
+
def main(file_name):
|
|
9
|
+
# Check for file existence
|
|
10
|
+
if not os.path.isfile(file_name):
|
|
11
|
+
raise Exception("File %s could not be found" % file_name)
|
|
12
|
+
|
|
13
|
+
tokens = file_name.lower().split('.')
|
|
14
|
+
extension = tokens[-1]
|
|
15
|
+
|
|
16
|
+
# Check for file extension
|
|
17
|
+
if not extension == 'mvnx':
|
|
18
|
+
raise Exception("File must be an .mvnx file")
|
|
19
|
+
|
|
20
|
+
# Load data
|
|
21
|
+
mvnx_file = load_mvnx(file_name)
|
|
22
|
+
|
|
23
|
+
# Read some basic data from the file
|
|
24
|
+
comments = mvnx_file.comments
|
|
25
|
+
frame_rate = mvnx_file.frame_rate
|
|
26
|
+
configuration = mvnx_file.configuration
|
|
27
|
+
original_file_name = mvnx_file.original_file_name
|
|
28
|
+
recording_date = mvnx_file.recording_date
|
|
29
|
+
actor_name = mvnx_file.actor_name
|
|
30
|
+
frame_count = mvnx_file.frame_count
|
|
31
|
+
version = mvnx_file.version
|
|
32
|
+
segment_count = mvnx_file.segment_count
|
|
33
|
+
joint_count = mvnx_file.joint_count
|
|
34
|
+
|
|
35
|
+
# Read the data from the structure e.g. first segment
|
|
36
|
+
idx = 0
|
|
37
|
+
segment_name = mvnx_file.segment_name_from_index(idx)
|
|
38
|
+
segment_pos = mvnx_file.get_segment_pos(idx)
|
|
39
|
+
|
|
40
|
+
# Alternatively, use the generic method get_data() with the data set and field. E.g.:
|
|
41
|
+
# segment_pos = mvnx_file.get_data('segment_data', 'pos', idx)
|
|
42
|
+
|
|
43
|
+
if segment_pos:
|
|
44
|
+
# Plot position of a segment
|
|
45
|
+
plt.figure(0)
|
|
46
|
+
plt.plot(segment_pos)
|
|
47
|
+
plt.xlabel('frames')
|
|
48
|
+
plt.ylabel('Position in the global frame')
|
|
49
|
+
plt.title('Position of ' + segment_name + ' segment')
|
|
50
|
+
plt.legend(['x', 'y', 'z'])
|
|
51
|
+
plt.draw()
|
|
52
|
+
|
|
53
|
+
# Plot 3D displacement of a segment
|
|
54
|
+
x, y, z = map(list, zip(*[[frame[0], frame[1], frame[2]] for frame in segment_pos]))
|
|
55
|
+
plt.figure(1)
|
|
56
|
+
plt.axes(projection="3d")
|
|
57
|
+
plt.plot(x, y, z)
|
|
58
|
+
plt.xlabel('frames')
|
|
59
|
+
plt.title('Position of ' + segment_name + ' segment in 3D')
|
|
60
|
+
plt.draw()
|
|
61
|
+
|
|
62
|
+
plt.show()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
if __name__ == '__main__':
|
|
66
|
+
|
|
67
|
+
# Program entry point
|
|
68
|
+
parser = argparse.ArgumentParser()
|
|
69
|
+
parser.add_argument('--mvnx_file', required=True, type=str, help='The MVNX file to load', nargs='?')
|
|
70
|
+
args = parser.parse_args()
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
main(args.mvnx_file)
|
|
74
|
+
except Exception as e:
|
|
75
|
+
print("Error: %s" % e)
|