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.
Files changed (58) hide show
  1. __init__.py +33 -0
  2. biomechzoo/__init__.py +0 -0
  3. biomechzoo/__main__.py +6 -0
  4. biomechzoo/biomech_ops/__init__.py +0 -0
  5. biomechzoo/biomech_ops/continuous_relative_phase_data.py +31 -0
  6. biomechzoo/biomech_ops/continuous_relative_phase_line.py +36 -0
  7. biomechzoo/biomech_ops/filter_data.py +58 -0
  8. biomechzoo/biomech_ops/filter_line.py +85 -0
  9. biomechzoo/biomech_ops/movement_onset.py +53 -0
  10. biomechzoo/biomech_ops/normalize_data.py +36 -0
  11. biomechzoo/biomech_ops/normalize_line.py +51 -0
  12. biomechzoo/biomech_ops/phase_angle_data.py +39 -0
  13. biomechzoo/biomech_ops/phase_angle_line.py +48 -0
  14. biomechzoo/biomechzoo.py +447 -0
  15. biomechzoo/conversion/__init__.py +0 -0
  16. biomechzoo/conversion/c3d2zoo_data.py +95 -0
  17. biomechzoo/conversion/mvnx2zoo_data.py +113 -0
  18. biomechzoo/conversion/opencap2zoo_data.py +23 -0
  19. biomechzoo/conversion/table2zoo_data.py +114 -0
  20. biomechzoo/imu/__init__.py +0 -0
  21. biomechzoo/imu/kinematics.py +0 -0
  22. biomechzoo/imu/tilt_algorithm.py +112 -0
  23. biomechzoo/linear_algebra_ops/__init__.py +0 -0
  24. biomechzoo/linear_algebra_ops/compute_magnitude_data.py +43 -0
  25. biomechzoo/mvn/__init__.py +0 -0
  26. biomechzoo/mvn/load_mvnx.py +514 -0
  27. biomechzoo/mvn/main_mvnx.py +75 -0
  28. biomechzoo/mvn/mvn.py +232 -0
  29. biomechzoo/mvn/mvnx_file_accessor.py +464 -0
  30. biomechzoo/processing/__init__.py +0 -0
  31. biomechzoo/processing/addchannel_data.py +71 -0
  32. biomechzoo/processing/addevent_data.py +116 -0
  33. biomechzoo/processing/explodechannel_data.py +69 -0
  34. biomechzoo/processing/partition_data.py +46 -0
  35. biomechzoo/processing/removechannel_data.py +46 -0
  36. biomechzoo/processing/removeevent_data.py +57 -0
  37. biomechzoo/processing/renamechannel_data.py +79 -0
  38. biomechzoo/processing/renameevent_data.py +62 -0
  39. biomechzoo/processing/split_trial_data.py +40 -0
  40. biomechzoo/statistics/eventval.py +118 -0
  41. biomechzoo/utils/__init__.py +0 -0
  42. biomechzoo/utils/batchdisp.py +21 -0
  43. biomechzoo/utils/compute_sampling_rate_from_time.py +25 -0
  44. biomechzoo/utils/engine.py +88 -0
  45. biomechzoo/utils/findfield.py +11 -0
  46. biomechzoo/utils/get_split_events.py +33 -0
  47. biomechzoo/utils/peak_sign.py +24 -0
  48. biomechzoo/utils/set_zoosystem.py +66 -0
  49. biomechzoo/utils/version.py +5 -0
  50. biomechzoo/utils/zload.py +57 -0
  51. biomechzoo/utils/zplot.py +61 -0
  52. biomechzoo/utils/zsave.py +54 -0
  53. biomechzoo-0.5.9.dist-info/METADATA +46 -0
  54. biomechzoo-0.5.9.dist-info/RECORD +58 -0
  55. biomechzoo-0.5.9.dist-info/WHEEL +5 -0
  56. biomechzoo-0.5.9.dist-info/entry_points.txt +2 -0
  57. biomechzoo-0.5.9.dist-info/licenses/LICENSE +21 -0
  58. biomechzoo-0.5.9.dist-info/top_level.txt +2 -0
@@ -0,0 +1,447 @@
1
+ import os
2
+ import inspect
3
+ import time
4
+
5
+ from biomechzoo.imu.tilt_algorithm import tilt_algorithm_data
6
+ from biomechzoo.utils.engine import engine # assumes this returns .zoo files in folder
7
+ from biomechzoo.utils.zload import zload
8
+ from biomechzoo.utils.zsave import zsave
9
+ from biomechzoo.utils.batchdisp import batchdisp
10
+ from biomechzoo.utils.get_split_events import get_split_events
11
+ from biomechzoo.processing.split_trial_data import split_trial_data
12
+ from biomechzoo.conversion.c3d2zoo_data import c3d2zoo_data
13
+ from biomechzoo.conversion.table2zoo_data import table2zoo_data
14
+ from biomechzoo.conversion.mvnx2zoo_data import mvnx2zoo_data
15
+ from biomechzoo.processing.removechannel_data import removechannel_data
16
+ from biomechzoo.processing.renamechannel_data import renamechannel_data
17
+ from biomechzoo.processing.removeevent_data import removeevent_data
18
+ from biomechzoo.processing.explodechannel_data import explodechannel_data
19
+ from biomechzoo.processing.addevent_data import addevent_data
20
+ from biomechzoo.processing.partition_data import partition_data
21
+ from biomechzoo.processing.renameevent_data import renameevent_data
22
+ from biomechzoo.biomech_ops.normalize_data import normalize_data
23
+ from biomechzoo.biomech_ops.phase_angle_data import phase_angle_data
24
+ from biomechzoo.biomech_ops.continuous_relative_phase_data import continuous_relative_phase_data
25
+ from biomechzoo.biomech_ops.filter_data import filter_data
26
+ from biomechzoo.linear_algebra_ops.compute_magnitude_data import compute_magnitude_data
27
+
28
+ class BiomechZoo:
29
+ def __init__(self, in_folder, inplace=False, subfolders=None, name_contains=None, verbose=0):
30
+ self.verbose = verbose
31
+ self.in_folder = in_folder
32
+ self.verbose = verbose
33
+ self.inplace = inplace # choice to save processed files to new folder
34
+ self.subfolders = subfolders # only run processes on list in subfolder
35
+ self.name_contains = name_contains # only run processes on files with name_contains in file name
36
+
37
+ batchdisp('BiomechZoo initialized', level=1, verbose=verbose)
38
+ batchdisp('verbosity set to: {}'.format(verbose), level=1, verbose=verbose)
39
+ batchdisp('root processing folder set to: {}'.format(self.in_folder), level=1, verbose=verbose)
40
+ if name_contains is not None:
41
+ batchdisp('only include files containing name_contains string: {}'.format(self.name_contains), level=1, verbose=verbose)
42
+ if subfolders is not None:
43
+ if type(subfolders) is list:
44
+ batchdisp('only process files in subfolder(s):', level=1, verbose=verbose)
45
+ for subfolder in self.subfolders:
46
+ batchdisp('{}'.format(os.path.join(self.in_folder, subfolder)), level=1, verbose=verbose)
47
+ else:
48
+ batchdisp('only process files in subfolder(s): {}'.format(os.path.join(self.in_folder, self.subfolders)), level=1, verbose=verbose)
49
+
50
+ if inplace:
51
+ batchdisp('Processing mode: overwrite (inplace=True) (each step will be applied to same folder)', level=1, verbose=verbose)
52
+ else:
53
+ batchdisp('Processing mode: backup (inplace=False)(each step will be applied to a new folder)', level=1, verbose=verbose)
54
+
55
+ def _update_folder(self, out_folder, inplace, in_folder):
56
+ """
57
+ Utility to update self.in_folder if not inplace.
58
+
59
+ Parameters:
60
+ - out_folder (str or None): The output folder provided by user
61
+ - inplace (bool): Whether processing is inplace
62
+ - in_folder (str): The current input folder
63
+ """
64
+ if not inplace:
65
+ # get full path for out_folder
66
+ in_folder_path = os.path.dirname(in_folder)
67
+ self.in_folder = os.path.join(in_folder_path, out_folder)
68
+
69
+ batchdisp('all files saved to: {}'.format(self.in_folder ), level=1, verbose=self.verbose)
70
+
71
+ def mvnx2zoo(self, out_folder=None, inplace=False):
72
+ """ Converts all .mvnx files in the folder to .zoo format """
73
+ start_time = time.time()
74
+ verbose = self.verbose
75
+ in_folder = self.in_folder
76
+ if inplace is None:
77
+ inplace = self.inplace
78
+ fl = engine(in_folder, extension='.mvnx', name_contains=self.name_contains, subfolders=self.subfolders)
79
+ for f in fl:
80
+ batchdisp('converting mvnx to zoo for {}'.format(f), level=2, verbose=verbose)
81
+ data = mvnx2zoo_data(f)
82
+ f_zoo = f.replace('.mvnx', '.zoo')
83
+ zsave(f_zoo, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
84
+ method_name = inspect.currentframe().f_code.co_name
85
+ batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
86
+ # Update self.folder after processing
87
+ self._update_folder(out_folder, inplace, in_folder)
88
+
89
+ def c3d2zoo(self, out_folder=None, inplace=None):
90
+ """ Converts all .c3d files in the folder to .zoo format """
91
+ start_time = time.time()
92
+ from ezc3d import c3d
93
+ verbose = self.verbose
94
+ in_folder = self.in_folder
95
+ if inplace is None:
96
+ inplace = self.inplace
97
+ fl = engine(in_folder, extension='.c3d', name_contains=self.name_contains, subfolders=self.subfolders)
98
+ for f in fl:
99
+ batchdisp('converting c3d to zoo for {}'.format(f), level=2, verbose=verbose)
100
+ c3d_obj = c3d(f)
101
+ data = c3d2zoo_data(c3d_obj)
102
+ f_zoo = f.replace('.c3d', '.zoo')
103
+ zsave(f_zoo, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
104
+ method_name = inspect.currentframe().f_code.co_name
105
+ batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
106
+ # Update self.folder after processing
107
+ self._update_folder(out_folder, inplace, in_folder)
108
+
109
+ def table2zoo(self, extension, out_folder=None, inplace=None, skip_rows=0, freq=None):
110
+ """ Converts generic .csv file in the folder to .zoo format """
111
+ start_time = time.time()
112
+ verbose = self.verbose
113
+ in_folder = self.in_folder
114
+
115
+ if extension.startswith('.'):
116
+ extension = extension[1:]
117
+
118
+ if inplace is None:
119
+ inplace = self.inplace
120
+ fl = engine(in_folder, extension=extension, name_contains=self.name_contains, subfolders=self.subfolders)
121
+ for f in fl:
122
+ batchdisp('converting {} to zoo for {}'.format(extension, f), level=2, verbose=verbose)
123
+ data = table2zoo_data(f, extension=extension, skip_rows=skip_rows, freq=freq)
124
+ f_zoo = f.replace(extension, '.zoo')
125
+ zsave(f_zoo, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
126
+ method_name = inspect.currentframe().f_code.co_name
127
+ batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
128
+ # Update self.folder after processing
129
+ self._update_folder(out_folder, inplace, in_folder)
130
+
131
+ def xls2zoo(self, out_folder=None, inplace=None):
132
+ raise NotImplementedError('Use table2zoo instead')
133
+
134
+ def csv2zoo(self, out_folder=None, inplace=None):
135
+ raise NotImplementedError('Use table2zoo instead')
136
+
137
+ def parquet2zoo(self, out_folder=None, inplace=None):
138
+ raise NotImplementedError('Use table2zoo instead')
139
+
140
+ def rectify(self):
141
+ raise NotImplementedError
142
+ def tilt_algorithm(self, chname_avert, chname_medlat, chname_antpost, out_folder=None, inplace=False):
143
+ """ tilt correction for acceleration data """
144
+ start_time = time.time()
145
+ verbose = self.verbose
146
+ in_folder = self.in_folder
147
+ if inplace is None:
148
+ inplace = self.inplace
149
+ fl = engine(in_folder, name_contains=self.name_contains, subfolders=self.subfolders)
150
+ for f in fl:
151
+ batchdisp('tilt correction of acceleration channels for {}'.format(f), level=2, verbose=verbose)
152
+ data = zload(f)
153
+ data = tilt_algorithm_data(data, chname_avert, chname_medlat, chname_antpost)
154
+ zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
155
+ method_name = inspect.currentframe().f_code.co_name
156
+ batchdisp(
157
+ '{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time),
158
+ level=1, verbose=verbose)
159
+ # Update self.folder after processing
160
+ self._update_folder(out_folder, inplace, in_folder)
161
+
162
+ def compute_magnitude(self, chname1, chname2, chname3, out_folder=None, inplace=False):
163
+ """ tilt correction for acceleration data """
164
+ start_time = time.time()
165
+ verbose = self.verbose
166
+ in_folder = self.in_folder
167
+ if inplace is None:
168
+ inplace = self.inplace
169
+ fl = engine(in_folder, name_contains=self.name_contains, subfolders=self.subfolders)
170
+ for f in fl:
171
+ batchdisp('compute magnitude from channels {}, {}, {} for {}'.format(chname1, chname2, chname3, f), level=2, verbose=verbose)
172
+ data = zload(f)
173
+ data = compute_magnitude_data(data, chname1, chname2, chname3)
174
+ zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
175
+ method_name = inspect.currentframe().f_code.co_name
176
+ batchdisp(
177
+ '{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time),
178
+ level=1, verbose=verbose)
179
+ # Update self.folder after processing
180
+ self._update_folder(out_folder, inplace, in_folder)
181
+
182
+ def phase_angle(self, ch, out_folder=None, inplace=None):
183
+ """ computes phase angles"""
184
+ start_time = time.time()
185
+ verbose = self.verbose
186
+ in_folder = self.in_folder
187
+ if inplace is None:
188
+ inplace = self.inplace
189
+ fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
190
+ for f in fl:
191
+ if verbose:
192
+ batchdisp('computing phase angles for {}'.format(f), level=2, verbose=verbose)
193
+ data = zload(f)
194
+ data = phase_angle_data(data, ch)
195
+ zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
196
+ method_name = inspect.currentframe().f_code.co_name
197
+ batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
198
+
199
+ # Update self.folder after processing
200
+ self._update_folder(out_folder, inplace, in_folder)
201
+
202
+ def continuous_relative_phase(self, ch_prox, ch_dist, out_folder=None, inplace=None):
203
+ """ computes CRP angles"""
204
+ start_time = time.time()
205
+ verbose = self.verbose
206
+ in_folder = self.in_folder
207
+ if inplace is None:
208
+ inplace = self.inplace
209
+ fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
210
+ for f in fl:
211
+ if verbose:
212
+ batchdisp('computing CRP angles between channel {} (prox) and {} (dist) for {}'.format(ch_prox, ch_dist, f), level=2, verbose=verbose)
213
+ data = zload(f)
214
+ data = continuous_relative_phase_data(data, ch_dist, ch_prox)
215
+ zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
216
+ method_name = inspect.currentframe().f_code.co_name
217
+ batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
218
+
219
+ # Update self.folder after processing
220
+ self._update_folder(out_folder, inplace, in_folder)
221
+
222
+ def split_trial_by_gait_cycle(self, first_event_name, out_folder=None, inplace=None):
223
+ """ split by gait cycle according to event_name"""
224
+ start_time = time.time()
225
+ verbose = self.verbose
226
+ in_folder = self.in_folder
227
+ if inplace is None:
228
+ inplace = self.inplace
229
+ fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
230
+ for f in fl:
231
+ f_name = os.path.splitext(os.path.basename(f))[0]
232
+ data = zload(f)
233
+ split_events = get_split_events(data, first_event_name)
234
+ if split_events is None:
235
+ print('no event {} found, saving original file'.format(first_event_name))
236
+ zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
237
+ else:
238
+ for i, _ in enumerate(split_events[0:-1]):
239
+ fl_new = f.replace(f_name, f_name + '_' + str(i + 1))
240
+ start = split_events[i]
241
+ end = split_events[i + 1]
242
+ batchdisp('splitting by gait cycle from {} to {} for {}'.format(start, end, f), level=2,
243
+ verbose=verbose)
244
+ data_new = split_trial_data(data, start, end)
245
+ if data_new is not None:
246
+ zsave(fl_new, data_new, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
247
+ method_name = inspect.currentframe().f_code.co_name
248
+ batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
249
+
250
+ # Update self.folder after processing
251
+ self._update_folder(out_folder, inplace, in_folder)
252
+
253
+
254
+ # def mean_absolute_relative_phase_deviation_phase(self, channels, out_folder=None, inplace=None):
255
+ # verbose = self.verbose
256
+ # in_folder = self.in_folder
257
+ # if inplace is None:
258
+ # inplace = self.inplace
259
+ #
260
+ # fl = engine(in_folder)
261
+ # for f in fl:
262
+ # for channel in channels:
263
+ # batchdisp('collecting trials for marp and dp for {}'.format(f), level=2, verbose=verbose)
264
+ # data = zload(f)
265
+ # data = removechannel_data(data, ch, mode)
266
+ # zsave(f, data, inplace=inplace, root_folder=in_folder, out_folder=out_folder)
267
+ # batchdisp('remove channel complete', level=1, verbose=verbose)
268
+ #
269
+ # # Update self.folder after processing
270
+ # self._update_folder(out_folder, inplace, in_folder)
271
+ def renameevent(self, evt, nevt, out_folder=None, inplace=None):
272
+ """ renames event evt to nevt in all zoo files """
273
+ start_time = time.time()
274
+ verbose = self.verbose
275
+ in_folder = self.in_folder
276
+ if inplace is None:
277
+ inplace = self.inplace
278
+ fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
279
+ for f in fl:
280
+ batchdisp('renaming events from {} to {} for {}'.format(evt, nevt ,f), level=2, verbose=verbose)
281
+ data = zload(f)
282
+ data = renameevent_data(data, evt, nevt)
283
+ zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
284
+ method_name = inspect.currentframe().f_code.co_name
285
+ batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
286
+
287
+ # Update self.folder after processing
288
+ self._update_folder(out_folder, inplace, in_folder)
289
+
290
+ def renamechannnel(self, ch, ch_new, out_folder=None, inplace=None):
291
+ """ renames channels from ch to ch_new in all zoo files """
292
+ start_time = time.time()
293
+ verbose = self.verbose
294
+ in_folder = self.in_folder
295
+ if inplace is None:
296
+ inplace = self.inplace
297
+ fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
298
+ for f in fl:
299
+ batchdisp('renaming channels from {} to {} for {}'.format(ch, ch_new ,f), level=2, verbose=verbose)
300
+ data = zload(f)
301
+ data = renamechannel_data(data, ch, ch_new)
302
+ zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
303
+ method_name = inspect.currentframe().f_code.co_name
304
+ batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
305
+
306
+ # Update self.folder after processing
307
+ self._update_folder(out_folder, inplace, in_folder)
308
+
309
+ def removechannel(self, ch, mode='remove', out_folder=None, inplace=None):
310
+ """ removes channels from zoo files """
311
+ start_time = time.time()
312
+ verbose = self.verbose
313
+ in_folder = self.in_folder
314
+ if inplace is None:
315
+ inplace = self.inplace
316
+ fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
317
+ for f in fl:
318
+ batchdisp('removing channels for {}'.format(f), level=2, verbose=verbose)
319
+ data = zload(f)
320
+ data = removechannel_data(data, ch, mode)
321
+ zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
322
+ method_name = inspect.currentframe().f_code.co_name
323
+ batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
324
+
325
+ # Update self.folder after processing
326
+ self._update_folder(out_folder, inplace, in_folder)
327
+
328
+
329
+ def removeevent(self, events, mode='remove', out_folder=None, inplace=None):
330
+ """ removes channels from zoo files """
331
+ start_time = time.time()
332
+ verbose = self.verbose
333
+ in_folder = self.in_folder
334
+ if inplace is None:
335
+ inplace = self.inplace
336
+ fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
337
+ for f in fl:
338
+ batchdisp('removing events {} for {}'.format(events, f), level=2, verbose=verbose)
339
+ data = zload(f)
340
+ data = removeevent_data(data, events, mode)
341
+ zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
342
+ method_name = inspect.currentframe().f_code.co_name
343
+ batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
344
+
345
+ # Update self.folder after processing
346
+ self._update_folder(out_folder, inplace, in_folder)
347
+
348
+
349
+ def explodechannel(self, out_folder=None, inplace=None):
350
+ """ explodes all channels in a zoo file """
351
+ start_time = time.time()
352
+ verbose = self.verbose
353
+ in_folder = self.in_folder
354
+ if inplace is None:
355
+ inplace = self.inplace
356
+ fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
357
+ for f in fl:
358
+ if verbose:
359
+ batchdisp('removing channels for {}'.format(f), level=2, verbose=verbose)
360
+ data = zload(f)
361
+ data = explodechannel_data(data)
362
+ zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
363
+ method_name = inspect.currentframe().f_code.co_name
364
+ batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
365
+
366
+ # Update self.folder after processing
367
+ self._update_folder(out_folder, inplace, in_folder)
368
+
369
+ def normalize(self, nlen=101, out_folder=None, inplace=None):
370
+ """ time normalizes all channels to length nlen """
371
+ start_time = time.time()
372
+ verbose = self.verbose
373
+ in_folder = self.in_folder
374
+ if inplace is None:
375
+ inplace = self.inplace
376
+ fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
377
+ for f in fl:
378
+ if verbose:
379
+ batchdisp('normalizing channels to length {} for {}'.format(nlen, f), level=2, verbose=verbose)
380
+ data = zload(f)
381
+ data = normalize_data(data, nlen)
382
+ zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
383
+ method_name = inspect.currentframe().f_code.co_name
384
+ batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
385
+
386
+ # Update self.folder after processing
387
+ self._update_folder(out_folder, inplace, in_folder)
388
+
389
+ def addevent(self, ch, event_type, event_name, out_folder=None, inplace=None, constant=None):
390
+ """ adds events of type evt_type with name evt_name to channel ch """
391
+ start_time = time.time()
392
+ verbose = self.verbose
393
+ in_folder = self.in_folder
394
+ if inplace is None:
395
+ inplace = self.inplace
396
+ fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
397
+ for f in fl:
398
+ if verbose:
399
+ batchdisp('adding event {} to channel {} for {}'.format(event_type, ch, f), level=2, verbose=verbose)
400
+ data = zload(f)
401
+ data = addevent_data(data, ch, event_type, event_name, constant)
402
+ zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
403
+ method_name = inspect.currentframe().f_code.co_name
404
+ batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
405
+
406
+ # Update self.folder after processing
407
+ self._update_folder(out_folder, inplace, in_folder)
408
+
409
+ def partition(self, evt_start, evt_end, out_folder=None, inplace=None):
410
+ """ partitions data between events evt_start and evt_end """
411
+ start_time = time.time()
412
+ verbose = self.verbose
413
+ in_folder = self.in_folder
414
+ if inplace is None:
415
+ inplace = self.inplace
416
+ fl = engine(in_folder, extension='.zoo', name_contains=self.name_contains, subfolders=self.subfolders)
417
+ for f in fl:
418
+ if verbose:
419
+ batchdisp('partitioning data between events {} and {} for {}'.format(evt_start, evt_end, f), level=2, verbose=verbose)
420
+ data = zload(f)
421
+ data = partition_data(data, evt_start, evt_end)
422
+ zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
423
+ method_name = inspect.currentframe().f_code.co_name
424
+ batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time), level=1, verbose=verbose)
425
+ # Update self.folder after processing
426
+ self._update_folder(out_folder, inplace, in_folder)
427
+
428
+ def filter(self, ch, filt=None, out_folder=None, inplace=None):
429
+ """ filter data"""
430
+ start_time = time.time()
431
+ verbose = self.verbose
432
+ in_folder = self.in_folder
433
+ if inplace is None:
434
+ inplace = self.inplace
435
+ fl = engine(in_folder, name_contains=self.name_contains, subfolders=self.subfolders)
436
+ for f in fl:
437
+ if verbose:
438
+ batchdisp('filtering data for channel {} in {}'.format(ch, f), level=2, verbose=verbose)
439
+ data = zload(f)
440
+ data = filter_data(data, ch, filt)
441
+ zsave(f, data, inplace=inplace, out_folder=out_folder, root_folder=in_folder)
442
+ method_name = inspect.currentframe().f_code.co_name
443
+ batchdisp('{} process complete for {} file(s) in {:.2f} secs'.format(method_name, len(fl), time.time() - start_time),
444
+ level=1, verbose=verbose)
445
+ # Update self.folder after processing
446
+ self._update_folder(out_folder, inplace, in_folder)
447
+
File without changes
@@ -0,0 +1,95 @@
1
+ from biomechzoo.utils.set_zoosystem import set_zoosystem
2
+
3
+ def c3d2zoo_data(c3d_obj):
4
+ """
5
+ Converts an ezc3d C3D object to zoo format.
6
+
7
+ Returns:
8
+ - data (dict): Zoo dictionary with 'line' and 'event' fields per channel.
9
+ """
10
+ data = {}
11
+ data['zoosystem'] = set_zoosystem()
12
+ video_freq = None
13
+ analog_freq = None
14
+ # extract "video" data
15
+ if 'points' in c3d_obj['data']:
16
+ points = c3d_obj['data']['points'] # shape: (4, n_markers, n_frames)
17
+ labels = list(c3d_obj['parameters']['POINT']['LABELS']['value'])
18
+ video_freq = int(c3d_obj['parameters']['POINT']['RATE']['value'][0])
19
+ for i, label in enumerate(labels):
20
+ line_data = points[:3, i, :].T # shape: (frames, 3)
21
+ data[label] = {
22
+ 'line': line_data,
23
+ 'event': {} # empty for now
24
+ }
25
+
26
+ data['zoosystem']['Video']['Freq'] = video_freq
27
+ data['zoosystem']['Video']['Channels'] = labels
28
+
29
+ if 'analogs' in c3d_obj['data']:
30
+ analog_data = c3d_obj['data']['analogs'] # shape: (subframes, n_analog_channels, n_frames)
31
+ analog_labels = list(c3d_obj['parameters']['ANALOG']['LABELS']['value'])
32
+ analog_freq = int(c3d_obj['parameters']['ANALOG']['RATE']['value'][0])
33
+ # Flatten to 2D: (n_samples, n_channels)
34
+ # ezc3d stores analogs as subframes per frame, so we flatten across all
35
+ n_subframes, n_channels, n_frames = analog_data.shape
36
+ analog_data = analog_data.reshape(n_subframes * n_frames, n_channels)
37
+
38
+ for i, label in enumerate(analog_labels):
39
+ line_data = analog_data[:, i].reshape(-1, 1) # shape: (samples, 1)
40
+ data[label] = {
41
+ 'line': line_data,
42
+ 'event': {},
43
+ }
44
+
45
+ data['zoosystem']['Analog']['Freq'] = analog_freq
46
+ data['zoosystem']['Analog']['Channels'] = analog_labels
47
+
48
+ # extract event information
49
+ params = c3d_obj['parameters']
50
+ if 'EVENT' in params and 'TIMES' in params['EVENT']:
51
+ if 'points' in c3d_obj['data']:
52
+ times_array = params['EVENT']['TIMES']['value']
53
+ frames = times_array[1] # should be time depending on C3D file
54
+
55
+ # Extract sides, types, subjects
56
+ contexts = params['EVENT']['CONTEXTS']['value'] if 'CONTEXTS' in params['EVENT'] else ['']
57
+ labels = params['EVENT']['LABELS']['value']
58
+ subjects = params['EVENT']['SUBJECTS']['value'] if 'SUBJECTS' in params['EVENT'] else ['']
59
+
60
+ events = {}
61
+
62
+ for i in range(len(labels)):
63
+ side = contexts[i].strip()
64
+ label = labels[i].strip()
65
+ subject = subjects[i].strip()
66
+
67
+ # Event channel name: e.g. 'Right_FootStrike' -> 'RightFootStrike'
68
+ event_name = f"{side}_{label}".replace(' ', '')
69
+ event_name = ''.join(c for c in event_name if c.isalnum() or c == '_') # make it a valid field name
70
+
71
+ if event_name not in events:
72
+ events[event_name] = []
73
+
74
+ events[event_name].append(frames[i]) # This is in seconds or frame number?
75
+
76
+ original_start = 1
77
+
78
+ for event_name, time_list in events.items():
79
+ # Clean and sort times
80
+ valid_times = sorted([t for t in time_list if t != 0])
81
+ for j, time_val in enumerate(valid_times):
82
+ frame = round(time_val * video_freq) - original_start + 1 # MATLAB logic
83
+ key_name = f"{event_name}{j + 1}"
84
+
85
+ # Place in correct channel
86
+ if 'SACR' in data:
87
+ data['SACR']['event'][key_name] = [frame-1, 0, 0] # remove 1 to follow python
88
+ else:
89
+ data[labels[0]]['event'][key_name] = [frame-1, 0, 0] # remove 1 to follow python
90
+
91
+ # add more zoosystem
92
+ if analog_freq is not None and video_freq is not None:
93
+ data['zoosystem']['AVR'] = analog_freq/video_freq
94
+
95
+ return data
@@ -0,0 +1,113 @@
1
+ import numpy as np
2
+ from biomechzoo.mvn.load_mvnx import load_mvnx
3
+ from biomechzoo.mvn.mvn import JOINTS, SEGMENTS
4
+ from biomechzoo.utils.set_zoosystem import set_zoosystem
5
+
6
+ def mvnx2zoo_data(fl):
7
+ """ loads mvnx file from xsens"""
8
+
9
+ mvnx_file = load_mvnx(fl)
10
+
11
+ # create zoo data dict
12
+ data = {'zoosystem': set_zoosystem()}
13
+ # extract joint angle data (All JOINTS may not exist in a given dataset)
14
+ for key, val in JOINTS.items():
15
+ try:
16
+ r = mvnx_file.get_joint_angle(joint=key)
17
+ data[val] = {
18
+ 'line': np.array(r),
19
+ 'event': {}
20
+ }
21
+ except KeyError:
22
+ print('joint {} does not exist, skipping'.format(val))
23
+
24
+ # extract segment orientations (All SEGMENTS may not exist in a given dataset)
25
+ for key, val in SEGMENTS.items():
26
+ try:
27
+ r = mvnx_file.get_sensor_ori(segment=key)
28
+
29
+ data[val] = {
30
+ 'line': np.array(r),
31
+ 'event': {}
32
+ }
33
+ except KeyError:
34
+ print('segment {} does not exist, skipping'.format(val))
35
+
36
+ # get foot strike events
37
+ data = _get_foot_strike_events(mvnx_file, data)
38
+
39
+ # add meta information
40
+ data = _get_meta_info(fl, mvnx_file, data)
41
+
42
+ return data
43
+
44
+
45
+ def is_valid_for_zoo(val):
46
+ """
47
+ Returns True if the value is valid for a MATLAB-compatible zoo structure.
48
+ """
49
+ if val is None:
50
+ return False
51
+ if isinstance(val, list) and len(val) == 0:
52
+ return False
53
+ if isinstance(val, np.ndarray) and val.size == 0:
54
+ return False
55
+ return True
56
+
57
+
58
+ def _get_meta_info(fl, mvnx_file, data):
59
+ # todo: add more, see mvnx_file object
60
+ data['zoosystem']['Video']['Freq'] = int(mvnx_file.frame_rate)
61
+ data['zoosystem']['mvnx_version'] = mvnx_file.version
62
+ data['zoosystem']['mvnx_configuration'] = mvnx_file.configuration
63
+ data['zoosystem']['recording_date'] = mvnx_file.recording_date
64
+ data['zoosystem']['original_file_name'] = mvnx_file.original_file_name
65
+ data['zoosystem']['frame_count'] = mvnx_file.frame_count
66
+ return data
67
+
68
+
69
+ def _get_foot_strike_events(mvnx_file, data):
70
+ RHeel = np.zeros(mvnx_file.frame_count)
71
+ LHeel = np.zeros(mvnx_file.frame_count)
72
+
73
+ for n in range(mvnx_file.frame_count):
74
+ list_contact = mvnx_file.get_foot_contacts(n)
75
+ for contact in list_contact:
76
+ if contact['segment_index'] == 17:
77
+ RHeel[n] = True
78
+ elif contact['segment_index'] == 21:
79
+ LHeel[n] = True
80
+
81
+ hs_r = []
82
+ hs_l = []
83
+ for i in range(1, len(LHeel)): # Start from 1 to avoid i-1 out-of-range
84
+ if RHeel[i - 1] == 0 and RHeel[i] == 1:
85
+ hs_r.append(i)
86
+ if LHeel[i - 1] == 0 and LHeel[i] == 1:
87
+ hs_l.append(i)
88
+
89
+ # add to event branch of any channel
90
+ if 'jL5S1' in data:
91
+ ch = 'jL5S1'
92
+ else:
93
+ ch = next(iter(data))
94
+
95
+ if hs_r:
96
+ for i, rHS in enumerate(hs_r):
97
+ data[ch]['event']['R_FS' + str(i + 1)] = [rHS, 0, 0]
98
+ if hs_l:
99
+ for i, lHS in enumerate(hs_l):
100
+ data[ch]['event']['L_FS' + str(i + 1)] = [lHS, 0, 0]
101
+
102
+ return data
103
+
104
+
105
+ if __name__ == '__main__':
106
+ """ testing """
107
+ import os
108
+ from biomechzoo.processing.split_trial_data import split_trial_data
109
+ # -------TESTING--------
110
+ project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
111
+ fl = os.path.join(project_root, 'data', 'other', 'Flat001.mvnx')
112
+ fl_zoo = fl.replace('.mvnx', '.zoo')
113
+ data = mvnx2zoo_data(fl)
@@ -0,0 +1,23 @@
1
+ import os
2
+ import pandas as pd
3
+
4
+ #todo: review. do not use in current form
5
+
6
+ def opencap2zoo_data(ik_file):
7
+ raise NotImplementedError
8
+ ik_data = pd.read_csv(ik_file, sep='\t', comment='#') # OpenSim .mot format
9
+ a = 1
10
+
11
+ def _compute_opencap_quantities():
12
+ b = 2
13
+
14
+
15
+ if __name__ == '__main__':
16
+ """ for unit testing"""
17
+ # -------TESTING--------
18
+ current_dir = os.path.dirname(os.path.abspath(__file__))
19
+ project_root = os.path.dirname(current_dir)
20
+
21
+ fl = os.path.join(project_root, 'data', 'OpenCap', 'gait_3', 'OpenSimData', 'Kinematics', 'gait_3.mot')
22
+
23
+ data = opencap2zoo_data(fl)