c3d-parser 1.5.2__tar.gz → 1.5.3__tar.gz
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.
- {c3d_parser-1.5.2/src/c3d_parser.egg-info → c3d_parser-1.5.3}/PKG-INFO +2 -2
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/requirements.txt +1 -1
- c3d_parser-1.5.3/src/c3d_parser/__init__.py +2 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/core/c3d_parser.py +20 -6
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/core/osim_resources/ik_task_set.xml +6 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/main_window.py +15 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3/src/c3d_parser.egg-info}/PKG-INFO +2 -2
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser.egg-info/requires.txt +1 -1
- c3d_parser-1.5.2/src/c3d_parser/__init__.py +0 -2
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/LICENSE +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/README.md +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/pyproject.toml +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/setup.cfg +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/application.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/core/c3d_patch.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/core/osim.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/core/osim_resources/external_loads_template.xml +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/core/utils.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/settings/general.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/settings/logging.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/settings/marker_maps/AUC.json +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/settings/marker_maps/FMC.json +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/settings/marker_maps/MH.json +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/settings/marker_maps/QCMAS.json +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/settings/marker_maps/RBWH.json +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/settings/marker_maps/RCH.json +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/settings/marker_maps/Sydney.json +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/splash_rc.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/splashscreen.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/dialogs/about_dialog.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/dialogs/delete_marker_set_dialog.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/dialogs/marker_set_dialog.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/dialogs/marker_set_import_dialog.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/dialogs/options_dialog.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/ui/resources_rc.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/ui/ui_delete_marker_set_dialog.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/ui/ui_main_window.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/ui/ui_marker_set_dialog.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/ui/ui_marker_set_import_dialog.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/ui/ui_options_dialog.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/utils.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/widgets.py +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser.egg-info/SOURCES.txt +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser.egg-info/dependency_links.txt +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser.egg-info/entry_points.txt +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser.egg-info/top_level.txt +0 -0
- {c3d_parser-1.5.2 → c3d_parser-1.5.3}/tests/test_parser.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: c3d-parser
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.3
|
|
4
4
|
Summary: C3D parser for gait data harmonisation.
|
|
5
5
|
Author-email: Timothy Salemink <tim.nicolas@outlook.com>, Sally Jack <sallyjaack@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -12,7 +12,7 @@ Requires-Dist: numpy==1.26.4
|
|
|
12
12
|
Requires-Dist: pandas
|
|
13
13
|
Requires-Dist: c3d==0.5.2
|
|
14
14
|
Requires-Dist: trc-data-reader>=0.4.1
|
|
15
|
-
Requires-Dist: opensim-model-creator==0.8.
|
|
15
|
+
Requires-Dist: opensim-model-creator==0.8.2
|
|
16
16
|
Requires-Dist: PySide6
|
|
17
17
|
Requires-Dist: matplotlib
|
|
18
18
|
Requires-Dist: mplcursors
|
|
@@ -525,8 +525,10 @@ def trim_frames(frame_data):
|
|
|
525
525
|
if not drop_frames.empty:
|
|
526
526
|
frame_data.drop(drop_frames, inplace=True)
|
|
527
527
|
|
|
528
|
+
# TODO: If we convert this to a dictionary we can keep the markers that are missing and check if they are required.
|
|
528
529
|
remaining_frames = [frame for frame in incomplete_frames.keys() if trim_start <= frame <= trim_end]
|
|
529
530
|
if remaining_frames:
|
|
531
|
+
# TODO: We should raise a ParserError (skip this trial) if the missing markers are required...?
|
|
530
532
|
logger.warn(f"Frames {remaining_frames} are incomplete.")
|
|
531
533
|
|
|
532
534
|
return trim_start, trim_end
|
|
@@ -1090,6 +1092,8 @@ def scale_grf_data(analog_data):
|
|
|
1090
1092
|
analog_data[columns] = analog_data[columns] / 1000
|
|
1091
1093
|
|
|
1092
1094
|
|
|
1095
|
+
# TODO: This incorrectly classifies Adelaide's static trials as dynamic.
|
|
1096
|
+
# Use `calculate_distance_covered` instead?
|
|
1093
1097
|
def is_dynamic(file_path):
|
|
1094
1098
|
with open(file_path, 'rb') as handle:
|
|
1095
1099
|
try:
|
|
@@ -1265,6 +1269,11 @@ def normalise_kinetics(kinetic_data, events):
|
|
|
1265
1269
|
normalised_data[foot][file_name] = {}
|
|
1266
1270
|
data_segment = data.iloc[start:frame, 1:]
|
|
1267
1271
|
|
|
1272
|
+
# TODO: If the model +tve and -tve are wrong, then we can't just flip these.
|
|
1273
|
+
# Because the non-GRF parts of the moments would also be flipped.
|
|
1274
|
+
# Just visually check the results against some clinical kinetics.
|
|
1275
|
+
# ...
|
|
1276
|
+
# Looks pretty good so far.
|
|
1268
1277
|
# Perform sign transformations.
|
|
1269
1278
|
data_segment[f"hip_flexion_{side}_moment"] = -data_segment[f"hip_flexion_{side}_moment"]
|
|
1270
1279
|
data_segment[f"hip_adduction_{side}_moment"] = -data_segment[f"hip_adduction_{side}_moment"]
|
|
@@ -1357,6 +1366,9 @@ def write_normalised_data(data, column_names, selected_trials, excluded_cycles,
|
|
|
1357
1366
|
file.write('\n')
|
|
1358
1367
|
|
|
1359
1368
|
|
|
1369
|
+
# TODO: Rework this function to be less sequential.
|
|
1370
|
+
# It should be able to calculate metrics for walking and running gait.
|
|
1371
|
+
# Put this at the start of the sequential bit.
|
|
1360
1372
|
def calculate_spatiotemporal_data(frame_data, events, static_data):
|
|
1361
1373
|
stride_lengths = {"Left": {}, "Right": {}}
|
|
1362
1374
|
step_lengths = {"Left": {}, "Right": {}}
|
|
@@ -1379,6 +1391,8 @@ def calculate_spatiotemporal_data(frame_data, events, static_data):
|
|
|
1379
1391
|
for event_time, (event_type, event_plate) in stride_events.items():
|
|
1380
1392
|
time_ordered_events[event_time][foot] = [event_type, stride_number]
|
|
1381
1393
|
|
|
1394
|
+
# TODO: We should be able to use this to calculate ALL measurements non-sequentially.
|
|
1395
|
+
# TODO: Double check that we don't have situations where the events are not ordered in increasing stride number.
|
|
1382
1396
|
# Add terminal foot-strike to events for each cycle.
|
|
1383
1397
|
full_cycle_events = copy.deepcopy(events)
|
|
1384
1398
|
for foot, foot_events in full_cycle_events.items():
|
|
@@ -1617,13 +1631,13 @@ def add_medial_knee_markers(frame_data, left_knee_width, right_knee_width, marke
|
|
|
1617
1631
|
This function takes a pandas.DataFrame of TRC data, extracts a single frame from the data
|
|
1618
1632
|
and adds the medial knee markers if they are missing. It returns the frame as a pandas.Series.
|
|
1619
1633
|
|
|
1620
|
-
The ``padding``
|
|
1621
|
-
|
|
1634
|
+
The ``padding`` values are the thickness of the baseplate used to attach the markers. Knee
|
|
1635
|
+
widths are measured over the skin.
|
|
1622
1636
|
"""
|
|
1623
1637
|
frame = frame_data.iloc[len(frame_data) // 2].copy()
|
|
1624
1638
|
|
|
1625
|
-
medial_padding =
|
|
1626
|
-
lateral_padding =
|
|
1639
|
+
medial_padding = 2
|
|
1640
|
+
lateral_padding = 2
|
|
1627
1641
|
|
|
1628
1642
|
for side in ['L', 'R']:
|
|
1629
1643
|
medial_label = f'{side}KNEM'
|
|
@@ -1637,7 +1651,7 @@ def add_medial_knee_markers(frame_data, left_knee_width, right_knee_width, marke
|
|
|
1637
1651
|
magnitude = np.sqrt((np.array(axix_vector) ** 2.0).sum(-1))
|
|
1638
1652
|
knee_axis = np.divide(axix_vector, magnitude)
|
|
1639
1653
|
|
|
1640
|
-
# Adjust knee width to account for marker-radius,
|
|
1654
|
+
# Adjust knee width to account for marker-radius, baseplate.
|
|
1641
1655
|
knee_width = left_knee_width if side == 'L' else right_knee_width
|
|
1642
1656
|
knee_width = knee_width + marker_diameter + medial_padding + lateral_padding
|
|
1643
1657
|
medial_marker = lateral_marker + knee_width * knee_axis
|
|
@@ -1654,7 +1668,7 @@ def create_osim_model(static_trc, dynamic_trc, static_marker_data, marker_diamet
|
|
|
1654
1668
|
rotation_matrix = np.array([[1, 0, 0], [0, 0, 1], [0, -1, 0]])
|
|
1655
1669
|
static_marker_data = {k: np.dot(rotation_matrix, v) for k, v in static_marker_data.items()}
|
|
1656
1670
|
subject_info = get_subject_info(static_data)
|
|
1657
|
-
marker_radius = marker_diameter / 2
|
|
1671
|
+
marker_radius = marker_diameter / 2 + 2 # Include the 2mm marker baseplate.
|
|
1658
1672
|
|
|
1659
1673
|
model_path = create_model(static_trc, dynamic_trc, output_directory, static_marker_data, subject_info,
|
|
1660
1674
|
marker_radius, left_foot_flat, right_foot_flat,
|
|
@@ -74,6 +74,12 @@
|
|
|
74
74
|
<!--Weight given to the task when solving inverse kinematics problems, default is 0.-->
|
|
75
75
|
<weight>1</weight>
|
|
76
76
|
</IKMarkerTask>
|
|
77
|
+
<IKMarkerTask name="SACR">
|
|
78
|
+
<!--Whether or not this task will be used during inverse kinematics solve, default is true.-->
|
|
79
|
+
<apply>true</apply>
|
|
80
|
+
<!--Weight given to the task when solving inverse kinematics problems, default is 0.-->
|
|
81
|
+
<weight>2</weight>
|
|
82
|
+
</IKMarkerTask>
|
|
77
83
|
<IKMarkerTask name="RHEE">
|
|
78
84
|
<!--Whether or not this task will be used during inverse kinematics solve, default is true.-->
|
|
79
85
|
<apply>true</apply>
|
|
@@ -57,6 +57,7 @@ class ProgressTracker(QObject):
|
|
|
57
57
|
progress = Signal(str, str)
|
|
58
58
|
|
|
59
59
|
|
|
60
|
+
# TODO: CGM2 event detector on GitHub...?
|
|
60
61
|
class MainWindow(QMainWindow):
|
|
61
62
|
|
|
62
63
|
def __init__(self):
|
|
@@ -258,6 +259,12 @@ class MainWindow(QMainWindow):
|
|
|
258
259
|
table.deleteLater()
|
|
259
260
|
self._spatiotemporal_tables = []
|
|
260
261
|
|
|
262
|
+
# TODO: The legend is not correctly displayed if the ST tab is open while processing...
|
|
263
|
+
# Repositioning and updating the legend do nothing.
|
|
264
|
+
|
|
265
|
+
# TODO: The border is still having issues.
|
|
266
|
+
# Seems to be related to the area being scrollable.
|
|
267
|
+
# Only happens on some PCs/monitors...
|
|
261
268
|
def _visualise_spatiotemporal_data(self):
|
|
262
269
|
|
|
263
270
|
# Temporarily disable spatio-temporal visualisation for running gait.
|
|
@@ -903,6 +910,8 @@ class MainWindow(QMainWindow):
|
|
|
903
910
|
if (file_name, f"{foot}_{cycle_number}") not in excluded_cycles:
|
|
904
911
|
counts[foot] += 1
|
|
905
912
|
|
|
913
|
+
# TODO: This is being displayed before processing has finished.
|
|
914
|
+
# Which should not be happening.
|
|
906
915
|
self._ui.labelCount.setText(f"Cycles: L {counts['Left']} / R {counts['Right']}")
|
|
907
916
|
|
|
908
917
|
tab = self._ui.tabWidget.currentWidget()
|
|
@@ -930,6 +939,7 @@ class MainWindow(QMainWindow):
|
|
|
930
939
|
self._update_s_t_colours()
|
|
931
940
|
self._update_legend()
|
|
932
941
|
|
|
942
|
+
# TODO: For some reason the legend doesn't get created at all if we are visualising the ST tab while processing...
|
|
933
943
|
def _update_legend(self):
|
|
934
944
|
if self._boxes:
|
|
935
945
|
for side, boxes in self._boxes.items():
|
|
@@ -1021,6 +1031,8 @@ class MainWindow(QMainWindow):
|
|
|
1021
1031
|
settings.beginGroup('MainWindow')
|
|
1022
1032
|
settings.setValue('size', self.size())
|
|
1023
1033
|
settings.setValue('pos', self.pos())
|
|
1034
|
+
# TODO: The taskbar icon fails if the application opens in full-screen...
|
|
1035
|
+
# DON'T open in full screen...?
|
|
1024
1036
|
settings.setValue('is_maximized', self.isMaximized())
|
|
1025
1037
|
settings.setValue('lab', self._ui.comboBoxLab.currentText())
|
|
1026
1038
|
settings.setValue('marker_diameter', self._ui.doubleSpinBoxMarkerDiameter.value())
|
|
@@ -1225,6 +1237,9 @@ class GaitCurves(defaultdict):
|
|
|
1225
1237
|
line.set_color(colour)
|
|
1226
1238
|
line.set_zorder(z_order)
|
|
1227
1239
|
|
|
1240
|
+
# TODO: Replace all these with `draw_idle`?
|
|
1241
|
+
# Just the ones that handle many curves...?
|
|
1242
|
+
# Probably all...?
|
|
1228
1243
|
self._canvas.draw()
|
|
1229
1244
|
|
|
1230
1245
|
def show_curve_menu(self, event):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: c3d-parser
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.3
|
|
4
4
|
Summary: C3D parser for gait data harmonisation.
|
|
5
5
|
Author-email: Timothy Salemink <tim.nicolas@outlook.com>, Sally Jack <sallyjaack@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -12,7 +12,7 @@ Requires-Dist: numpy==1.26.4
|
|
|
12
12
|
Requires-Dist: pandas
|
|
13
13
|
Requires-Dist: c3d==0.5.2
|
|
14
14
|
Requires-Dist: trc-data-reader>=0.4.1
|
|
15
|
-
Requires-Dist: opensim-model-creator==0.8.
|
|
15
|
+
Requires-Dist: opensim-model-creator==0.8.2
|
|
16
16
|
Requires-Dist: PySide6
|
|
17
17
|
Requires-Dist: matplotlib
|
|
18
18
|
Requires-Dist: mplcursors
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/core/osim_resources/external_loads_template.xml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/dialogs/delete_marker_set_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{c3d_parser-1.5.2 → c3d_parser-1.5.3}/src/c3d_parser/view/dialogs/marker_set_import_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|