@@ -523,32 +523,41 @@ def _calculate(self, trial: model.Trial) -> xr.DataArray:
523
523
marker_dict = self .select_markers_for_spatial_features (trial )
524
524
525
525
results_dict = self ._calculate_step_length (
526
- trial , marker_dict ["ipsi_heel" ], marker_dict ["contra_heel" ]
526
+ trial ,
527
+ marker_dict ["ipsi_heel" ], # type: ignore
528
+ marker_dict ["contra_heel" ], # type: ignore
527
529
)
528
530
results_dict .update (
529
531
self ._calculate_step_width (
530
- trial , marker_dict ["ipsi_heel" ], marker_dict ["contra_heel" ]
532
+ trial ,
533
+ marker_dict ["ipsi_heel" ], # type: ignore
534
+ marker_dict ["contra_heel" ], # type: ignore
531
535
)
532
536
)
533
537
results_dict .update (
534
538
self ._calculate_stride_length (
535
- trial , marker_dict ["ipsi_heel" ], marker_dict ["contra_heel" ]
539
+ trial ,
540
+ marker_dict ["ipsi_heel" ], # type: ignore
541
+ marker_dict ["contra_heel" ], # type: ignore
536
542
)
537
543
)
544
+
545
+ toe_markers = [marker_dict ["ipsi_toe_2" ]]
546
+ if marker_dict ["ipsi_toe_5" ] is not None :
547
+ toe_markers .append (marker_dict ["ipsi_toe_5" ])
538
548
results_dict .update (
539
549
self ._calculate_minimal_toe_clearance (
540
550
trial ,
541
- marker_dict ["ipsi_toe_2" ],
542
- marker_dict ["ipsi_heel" ],
543
- marker_dict ["ipsi_toe_5" ],
551
+ toe_markers , # type: ignore
552
+ marker_dict ["ipsi_heel" ], # type: ignore
544
553
)
545
554
)
546
555
if marker_dict ["xcom" ] is not None :
547
556
results_dict .update (
548
557
self ._calculate_ap_margin_of_stability (
549
558
trial ,
550
- marker_dict ["ipsi_toe_2" ],
551
- marker_dict ["contra_toe_2" ],
559
+ marker_dict ["ipsi_toe_2" ], # type: ignore
560
+ marker_dict ["contra_toe_2" ], # type: ignore
552
561
marker_dict ["xcom" ],
553
562
)
554
563
)
@@ -568,7 +577,7 @@ def _calculate(self, trial: model.Trial) -> xr.DataArray:
568
577
569
578
def select_markers_for_spatial_features (
570
579
self , trial : model .Trial
571
- ) -> dict [str , mapping .MappedMarkers ]:
580
+ ) -> dict [str , mapping .MappedMarkers | None ]:
572
581
"""Select markers based on the trial's context (Right or Left). If some markers are missing, return them as None
573
582
574
583
Args:
@@ -736,63 +745,63 @@ def _calculate_stride_length(
736
745
def _calculate_minimal_toe_clearance (
737
746
self ,
738
747
trial : model .Trial ,
739
- ipsi_toe_marker : mapping .MappedMarkers ,
748
+ ipsi_toe_markers : list [ mapping .MappedMarkers ] ,
740
749
ipsi_heel_marker : mapping .MappedMarkers ,
741
- * opt_ipsi_toe_markers : mapping .MappedMarkers ,
742
750
) -> dict [str , np .ndarray ]:
743
- """Calculate the minimal toe clearance for a trial. Toe clearance is computed for all toe markers passed, only the minimal is returned
751
+ """Calculate the minimal toe clearance for a trial. <br />
752
+ Toe clearance is computed for all toe markers passed, only the minimal is returned. <br />
753
+ Addition toe markers can be passed to improve the accuracy of the calculation. <br />
754
+ If the minimal toe clearance cannot be calculated, an empty array is returned. <br />
744
755
745
756
Args:
746
- trial (model.Trial) : The trial to compute the minimal toe clearance for
747
- ipsi_toe_marker (mapping.MappedMarkers) : The ipsilateral toe marker
748
- ipsi_heel_marker (mapping.MappedMarkers): The ipsilateral heel marker
757
+ trial: The trial to compute the minimal toe clearance for
758
+ ipsi_toe_markers : The ipsi-lateral toe markers
759
+ ipsi_heel_marker (mapping.MappedMarkers): The ipsi-lateral heel marker
749
760
750
761
Returns:
751
762
dict[str, np.ndarray]: The calculated minimal toe clearance in a dict
763
+
764
+ Raises:
765
+ ValueError: If no toe markers are found for minimal toe clearance calculation
752
766
"""
753
767
event_times = self .get_event_times (trial .events )
754
768
755
769
ipsi_heel = self ._get_marker_data (trial , ipsi_heel_marker ).sel (
756
770
time = slice (event_times [3 ], event_times [4 ])
757
771
)
758
- ipsi_toe = self ._get_marker_data (trial , ipsi_toe_marker ).sel (
759
- time = slice (event_times [3 ], event_times [4 ])
760
- )
772
+ ipsi_toes = []
773
+ ipsi_toe_velocities = None
761
774
762
- toes_vel = linalg .calculate_speed_norm (ipsi_toe )
763
-
764
- additional_meta_data = []
765
-
766
- for meta_marker in opt_ipsi_toe_markers :
767
- if meta_marker is not None :
768
- meta_data = self ._get_marker_data (trial , meta_marker ).sel (
769
- time = slice (event_times [3 ], event_times [4 ])
770
- )
771
- toes_vel += linalg .calculate_speed_norm (meta_data )
772
- additional_meta_data .append (meta_data )
773
-
774
- toes_vel /= 1 + len (additional_meta_data )
775
+ # Get all ipsi toe marker positions and calculate the mean velocity of them
776
+ for ipsi_toe_marker in ipsi_toe_markers :
777
+ ipsi_toe = self ._get_marker_data (trial , ipsi_toe_marker ).sel (
778
+ time = slice (event_times [3 ], event_times [4 ])
779
+ )
780
+ ipsi_toes .append (ipsi_toe )
781
+ if ipsi_toe_velocities is None :
782
+ ipsi_toe_velocities = linalg .calculate_speed_norm (ipsi_toe )
783
+ else :
784
+ ipsi_toe_velocities += linalg .calculate_speed_norm (ipsi_toe )
775
785
776
- mtc_i = self ._find_mtc_index (ipsi_toe , ipsi_heel , toes_vel )
777
- mtc_additional_indices = [
778
- self ._find_mtc_index (meta_data , ipsi_heel , toes_vel )
779
- for meta_data in additional_meta_data
780
- ]
786
+ if ipsi_toe_velocities is None :
787
+ raise ValueError (
788
+ "No toe markers found for minimal toe clearance calculation"
789
+ )
781
790
782
- # Handle NaN cases and find minimal clearance
783
- mtc_values = [] if np .isnan (mtc_i ) else [ipsi_toe .sel (axis = "z" )[mtc_i ]]
784
- for i , meta_data in zip (mtc_additional_indices , additional_meta_data ):
785
- if not np .isnan (i ):
786
- mtc_values .append (meta_data .sel (axis = "z" )[i ])
791
+ ipsi_toe_velocities /= len (ipsi_toe_markers )
787
792
788
- if not mtc_values :
789
- return {"minimal_toe_clearance" : np .empty (1 )}
793
+ minimal_toe_clearance = np .full (1 , np .inf )
794
+ for ipsi_toe in ipsi_toes :
795
+ i = self ._find_mtc_index (ipsi_toe , ipsi_heel , ipsi_toe_velocities )
796
+ clearance = ipsi_toe .sel (axis = "z" )[i ].values
797
+ if clearance < minimal_toe_clearance :
798
+ minimal_toe_clearance = clearance
790
799
791
- return {"minimal_toe_clearance" : min ( mtc_values ) }
800
+ return {"minimal_toe_clearance" : minimal_toe_clearance }
792
801
793
802
@staticmethod
794
803
def _find_mtc_index (
795
- toe_position : xr .DataArray , heel_position : xr .DataArray , toes_vel : xr . DataArray
804
+ toe_position : xr .DataArray , heel_position : xr .DataArray , toes_vel : np . ndarray
796
805
):
797
806
"""Find the time corresponding to minimal toe clearance of a specific toe.
798
807
Valid minimal toe clearance point must pass conditions
@@ -813,7 +822,7 @@ def _find_mtc_index(
813
822
mtc_i = [i for i in mtc_i if toes_vel [i ] >= toes_vel_up_quant ]
814
823
mtc_i = [i for i in mtc_i if toe_z [i ] <= heel_z [i ]]
815
824
816
- return None if not mtc_i else min (mtc_i , key = lambda i : toe_z [i ])
825
+ return None if not mtc_i else min (mtc_i , key = lambda i : toe_z [i ]) # type: ignore
817
826
818
827
def _calculate_ap_margin_of_stability (
819
828
self ,
0 commit comments