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.
Files changed (45) hide show
  1. {c3d_parser-0.3.0/src/c3d_parser.egg-info → c3d_parser-0.4.0}/PKG-INFO +4 -2
  2. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/requirements.txt +3 -1
  3. c3d_parser-0.4.0/src/c3d_parser/__init__.py +2 -0
  4. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/application.py +15 -3
  5. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/core/c3d_parser.py +27 -7
  6. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/core/osim.py +9 -0
  7. c3d_parser-0.4.0/src/c3d_parser/splash_rc.py +1454 -0
  8. c3d_parser-0.4.0/src/c3d_parser/splashscreen.py +50 -0
  9. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/main_window.py +104 -32
  10. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/ui/resources_rc.py +6 -6
  11. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/ui/ui_main_window.py +63 -35
  12. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/widgets.py +29 -0
  13. {c3d_parser-0.3.0 → c3d_parser-0.4.0/src/c3d_parser.egg-info}/PKG-INFO +4 -2
  14. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser.egg-info/SOURCES.txt +2 -0
  15. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser.egg-info/requires.txt +3 -1
  16. c3d_parser-0.3.0/src/c3d_parser/__init__.py +0 -2
  17. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/LICENSE +0 -0
  18. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/README.md +0 -0
  19. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/pyproject.toml +0 -0
  20. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/setup.cfg +0 -0
  21. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/core/c3d_patch.py +0 -0
  22. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/core/osim_resources/external_loads_template.xml +0 -0
  23. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/core/osim_resources/ik_task_set.xml +0 -0
  24. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/core/utils.py +0 -0
  25. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/general.py +0 -0
  26. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/logging.py +0 -0
  27. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/marker_maps/AUC.json +0 -0
  28. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/marker_maps/FMC.json +0 -0
  29. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/marker_maps/MH.json +0 -0
  30. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/marker_maps/QCMAS.json +0 -0
  31. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/marker_maps/RBWH.json +0 -0
  32. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/marker_maps/RCH.json +0 -0
  33. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/settings/marker_maps/Sydney.json +0 -0
  34. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/dialogs/about_dialog.py +0 -0
  35. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/dialogs/marker_set_dialog.py +0 -0
  36. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/dialogs/marker_set_import_dialog.py +0 -0
  37. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/dialogs/options_dialog.py +0 -0
  38. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/ui/ui_marker_set_dialog.py +0 -0
  39. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/ui/ui_marker_set_import_dialog.py +0 -0
  40. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/ui/ui_options_dialog.py +0 -0
  41. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser/view/utils.py +0 -0
  42. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser.egg-info/dependency_links.txt +0 -0
  43. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser.egg-info/entry_points.txt +0 -0
  44. {c3d_parser-0.3.0 → c3d_parser-0.4.0}/src/c3d_parser.egg-info/top_level.txt +0 -0
  45. {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.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.1.18
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
@@ -3,7 +3,9 @@ numpy==1.26.4
3
3
  pandas
4
4
  c3d==0.5.2
5
5
  trc-data-reader
6
- opensim-model-creator>=0.1.18
6
+ opensim-model-creator>=0.2.0
7
7
  PySide6
8
8
  matplotlib
9
9
  mplcursors
10
+ pyvistaqt
11
+ ll-visualiser
@@ -0,0 +1,2 @@
1
+
2
+ __version__ = "0.4.0"
@@ -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 dynamic_trials else ""
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 dynamic_trials:
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 = int((end_time - start_time) * frequency)
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=1, stop=len(resampled_frame_data) + 1, step=1)
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
- label = labels[i].strip()
556
- event_time = times[i][1]
557
- events[foot][event_time] = label
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])