c3d-parser 0.3.0__tar.gz → 0.4.0__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-0.3.0/src/c3d_parser.egg-info → c3d_parser-0.4.0}/PKG-INFO +4 -2
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/requirements.txt +3 -1
- c3d_parser-0.4.0/src/c3d_parser/__init__.py +2 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/application.py +15 -3
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/core/c3d_parser.py +27 -7
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/core/osim.py +9 -0
- c3d_parser-0.4.0/src/c3d_parser/splash_rc.py +1454 -0
- c3d_parser-0.4.0/src/c3d_parser/splashscreen.py +50 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/main_window.py +104 -32
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/ui/resources_rc.py +6 -6
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/ui/ui_main_window.py +63 -35
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/widgets.py +29 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0/src/c3d_parser.egg-info}/PKG-INFO +4 -2
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser.egg-info/SOURCES.txt +2 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser.egg-info/requires.txt +3 -1
- c3d_parser-0.3.0/src/c3d_parser/__init__.py +0 -2
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/LICENSE +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/README.md +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/pyproject.toml +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/setup.cfg +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/core/c3d_patch.py +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/core/osim_resources/external_loads_template.xml +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/core/osim_resources/ik_task_set.xml +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/core/utils.py +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/general.py +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/logging.py +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/marker_maps/AUC.json +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/marker_maps/FMC.json +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/marker_maps/MH.json +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/marker_maps/QCMAS.json +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/marker_maps/RBWH.json +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/marker_maps/RCH.json +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/marker_maps/Sydney.json +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/dialogs/about_dialog.py +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/dialogs/marker_set_dialog.py +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/dialogs/marker_set_import_dialog.py +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/dialogs/options_dialog.py +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/ui/ui_marker_set_dialog.py +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/ui/ui_marker_set_import_dialog.py +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/ui/ui_options_dialog.py +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/utils.py +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser.egg-info/dependency_links.txt +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser.egg-info/entry_points.txt +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser.egg-info/top_level.txt +0 -0
- {c3d_parser-0.3.0 → c3d_parser-0.4.0}/tests/test_parser.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: c3d-parser
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
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,10 +12,12 @@ 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
|
|
15
|
-
Requires-Dist: opensim-model-creator>=0.
|
|
15
|
+
Requires-Dist: opensim-model-creator>=0.2.0
|
|
16
16
|
Requires-Dist: PySide6
|
|
17
17
|
Requires-Dist: matplotlib
|
|
18
18
|
Requires-Dist: mplcursors
|
|
19
|
+
Requires-Dist: pyvistaqt
|
|
20
|
+
Requires-Dist: ll-visualiser
|
|
19
21
|
Dynamic: license-file
|
|
20
22
|
|
|
21
23
|
# C3D-parser
|
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
import sys
|
|
3
3
|
import ctypes
|
|
4
4
|
|
|
5
|
-
from PySide6.QtWidgets import QApplication
|
|
6
|
-
|
|
7
|
-
from c3d_parser.view.main_window import MainWindow
|
|
8
5
|
from c3d_parser.settings.general import (set_applications_settings, application_instance_exists,
|
|
9
6
|
start_application_server, setup_marker_maps_dir)
|
|
10
7
|
from c3d_parser.settings.logging import initialise_logger, filter_c3d_warnings
|
|
@@ -15,7 +12,14 @@ def main():
|
|
|
15
12
|
my_app_id = 'Motion_Connect.C3D_Parser'
|
|
16
13
|
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(my_app_id)
|
|
17
14
|
|
|
15
|
+
from PySide6.QtWidgets import QApplication
|
|
18
16
|
app = QApplication(sys.argv)
|
|
17
|
+
|
|
18
|
+
from c3d_parser.splashscreen import SplashScreen
|
|
19
|
+
splash = SplashScreen()
|
|
20
|
+
splash.show()
|
|
21
|
+
|
|
22
|
+
splash.showMessage("Loading settings ...", 5)
|
|
19
23
|
set_applications_settings(app)
|
|
20
24
|
setup_marker_maps_dir()
|
|
21
25
|
|
|
@@ -25,12 +29,20 @@ def main():
|
|
|
25
29
|
sys.exit(1)
|
|
26
30
|
app_server = start_application_server()
|
|
27
31
|
|
|
32
|
+
splash.showMessage('Initialising logger ...', 20)
|
|
28
33
|
initialise_logger()
|
|
29
34
|
filter_c3d_warnings()
|
|
30
35
|
|
|
36
|
+
splash.showMessage('Importing dependencies ...', 40)
|
|
37
|
+
from c3d_parser.view.main_window import MainWindow
|
|
38
|
+
splash.showMessage('Creating main window ...', 80)
|
|
31
39
|
window = MainWindow()
|
|
40
|
+
splash.showMessage('Showing main window ...', 90)
|
|
32
41
|
window.show()
|
|
33
42
|
|
|
43
|
+
splash.showMessage('Load complete ...', 100)
|
|
44
|
+
splash.finish(window)
|
|
45
|
+
|
|
34
46
|
sys.exit(app.exec())
|
|
35
47
|
|
|
36
48
|
|
|
@@ -76,11 +76,11 @@ def parse_session(static_trial, dynamic_trials, input_directory, output_director
|
|
|
76
76
|
grf_file_paths[trial] = grf_file_path
|
|
77
77
|
deidentified_file_names[trial] = os.path.basename(trc_file_path).rsplit(".", 1)[0]
|
|
78
78
|
|
|
79
|
-
dynamic_trc_path = list(trc_file_paths.values())[0] if
|
|
79
|
+
dynamic_trc_path = list(trc_file_paths.values())[0] if trc_file_paths else ""
|
|
80
80
|
osim_model = create_osim_model(static_trc_path, dynamic_trc_path, frame, marker_diameter, static_data,
|
|
81
81
|
output_directory, optimise_knee_axis, progress_tracker)
|
|
82
82
|
|
|
83
|
-
if not
|
|
83
|
+
if not trc_file_paths:
|
|
84
84
|
raise CancelException("No dynamic trials found.")
|
|
85
85
|
|
|
86
86
|
progress_tracker.progress.emit("Running IK and ID", "black")
|
|
@@ -329,6 +329,8 @@ def harmonise_markers(frame_data, lab, required_markers):
|
|
|
329
329
|
if None in frame_data.columns:
|
|
330
330
|
frame_data.drop(columns=frame_data.columns[frame_data.columns.isna()], inplace=True)
|
|
331
331
|
|
|
332
|
+
# TODO: This error window does not indicate which trial has the issue.
|
|
333
|
+
# Update this.
|
|
332
334
|
# Ensure required markers are present.
|
|
333
335
|
available = set(frame_data.columns)
|
|
334
336
|
for item in required_markers:
|
|
@@ -382,8 +384,10 @@ def trim_frames(frame_data):
|
|
|
382
384
|
if not drop_frames.empty:
|
|
383
385
|
frame_data.drop(drop_frames, inplace=True)
|
|
384
386
|
|
|
387
|
+
# TODO: If we convert this to a dictionary we can keep the markers that are missing and check if they are required.
|
|
385
388
|
remaining_frames = [frame for frame in incomplete_frames.keys() if trim_start <= frame <= trim_end]
|
|
386
389
|
if remaining_frames:
|
|
390
|
+
# TODO: We should raise a ParserError (skip this trial) if the missing markers are required...?
|
|
387
391
|
logger.warn(f"Frames {remaining_frames} are incomplete.")
|
|
388
392
|
|
|
389
393
|
return trim_start, trim_end
|
|
@@ -408,7 +412,7 @@ def resample_data(frame_data, data_rate, frequency=100):
|
|
|
408
412
|
|
|
409
413
|
start_time = frame_data.iloc[:, 0].iat[0]
|
|
410
414
|
end_time = frame_data.iloc[:, 0].iat[-1]
|
|
411
|
-
number_of_frames =
|
|
415
|
+
number_of_frames = round((end_time - start_time) * frequency) + 1
|
|
412
416
|
time_array = np.linspace(start_time, end_time, number_of_frames)
|
|
413
417
|
|
|
414
418
|
resampled_frame_data = pd.DataFrame(columns=frame_data.columns)
|
|
@@ -435,7 +439,7 @@ def resample_data(frame_data, data_rate, frequency=100):
|
|
|
435
439
|
for i in range(resampled_trajectory.shape[0]):
|
|
436
440
|
flattened_array[i] = resampled_trajectory[i]
|
|
437
441
|
resampled_frame_data.loc[:, column] = flattened_array
|
|
438
|
-
resampled_frame_data.index = pd.RangeIndex(start=
|
|
442
|
+
resampled_frame_data.index = pd.RangeIndex(start=0, stop=len(resampled_frame_data), step=1)
|
|
439
443
|
|
|
440
444
|
return resampled_frame_data
|
|
441
445
|
|
|
@@ -552,9 +556,12 @@ def extract_data(file_path, start_frame, end_frame):
|
|
|
552
556
|
|
|
553
557
|
for i in range(event_count):
|
|
554
558
|
foot = contexts[i].strip()
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
559
|
+
if foot:
|
|
560
|
+
label = labels[i].strip()
|
|
561
|
+
event_time = times[i][1]
|
|
562
|
+
events[foot][event_time] = label
|
|
563
|
+
if not any(events.values()):
|
|
564
|
+
raise ParserError("Event context (side) missing.")
|
|
558
565
|
|
|
559
566
|
for foot in events.keys():
|
|
560
567
|
events[foot] = dict(sorted(events[foot].items()))
|
|
@@ -594,6 +601,14 @@ def extract_data(file_path, start_frame, end_frame):
|
|
|
594
601
|
return analog_data, reader.analog_rate, trimmed_events, plate_count, corners
|
|
595
602
|
|
|
596
603
|
|
|
604
|
+
# TODO: Georgio asked if we could maybe provide a way to approximate some of these values (from markers),
|
|
605
|
+
# if they haven't measured them.
|
|
606
|
+
# ...
|
|
607
|
+
# Thor suggests taking this value from the model.
|
|
608
|
+
# ...
|
|
609
|
+
# Discuss this with Elyse to see if she's Ok with this/
|
|
610
|
+
# If she can get the other labs to agree/
|
|
611
|
+
# See how each lab measures this.
|
|
597
612
|
def extract_static_data(file_path):
|
|
598
613
|
with open(file_path, 'rb') as handle:
|
|
599
614
|
reader = c3d.Reader(handle)
|
|
@@ -1407,6 +1422,11 @@ def add_medial_knee_markers(frame_data, left_knee_width, right_knee_width, marke
|
|
|
1407
1422
|
def create_osim_model(static_trc, dynamic_trc, static_marker_data, marker_diameter, static_data,
|
|
1408
1423
|
output_directory, optimise_knee_axis, progress_tracker):
|
|
1409
1424
|
|
|
1425
|
+
# TODO: Lazy import this here.
|
|
1426
|
+
# Reduce startup time.
|
|
1427
|
+
# We might be able to "warm up" this import in the background before we get here as well.
|
|
1428
|
+
# from opensim_model_creator.Create_Model import create_model
|
|
1429
|
+
|
|
1410
1430
|
static_marker_data = static_marker_data.drop("Time").to_dict()
|
|
1411
1431
|
rotation_matrix = np.array([[1, 0, 0], [0, 0, 1], [0, -1, 0]])
|
|
1412
1432
|
static_marker_data = {k: np.dot(rotation_matrix, v) for k, v in static_marker_data.items()}
|
|
@@ -18,6 +18,11 @@ def perform_ik(osim_file, trc_file, output_file):
|
|
|
18
18
|
ik_tool.setModel(model)
|
|
19
19
|
ik_tool.setMarkerDataFileName(trc_file)
|
|
20
20
|
ik_tool.set_IKTaskSet(osim.IKTaskSet(IK_TASK_SET))
|
|
21
|
+
|
|
22
|
+
# TODO: Fix Teresa's OpenSim issue.
|
|
23
|
+
# ik_tool.setResultsDir(r"C:\Users\MyUser\AppData\Local\MyApp\results")
|
|
24
|
+
# ik_tool.setResultsDir(output_directory)
|
|
25
|
+
|
|
21
26
|
ik_tool.setOutputMotionFileName(output_file)
|
|
22
27
|
ik_tool.set_report_errors(False)
|
|
23
28
|
ik_tool.run()
|
|
@@ -34,9 +39,13 @@ def perform_id(osim_file, ik_file, grf_file, output_file):
|
|
|
34
39
|
id_tool = osim.InverseDynamicsTool()
|
|
35
40
|
id_tool.setModel(model)
|
|
36
41
|
id_tool.setCoordinatesFileName(ik_file)
|
|
42
|
+
|
|
43
|
+
# TODO: Temporarily disable.
|
|
37
44
|
id_tool.setExternalLoadsFileName(external_loads_file)
|
|
45
|
+
|
|
38
46
|
id_tool.setResultsDir(output_directory)
|
|
39
47
|
id_tool.setOutputGenForceFileName(output_file_name)
|
|
48
|
+
# TODO: We shouldn't need this since the data (IK results) are already filtered.
|
|
40
49
|
id_tool.setLowpassCutoffFrequency(6)
|
|
41
50
|
id_tool.setStartTime(time_values[0])
|
|
42
51
|
id_tool.setEndTime(time_values[-1])
|