Skip to content

Commit f7c2709

Browse files
committed
event detection module rework
1 parent 4d6823a commit f7c2709

File tree

8 files changed

+2642
-147
lines changed

8 files changed

+2642
-147
lines changed

gaitalytics/api.py

+145-16
Original file line numberDiff line numberDiff line change
@@ -92,36 +92,165 @@ def load_c3d_trial(
9292
return trial
9393

9494

95+
def get_event_detector(
96+
method_hs: str,
97+
method_to: str,
98+
configs: mapping.MappingConfigs,
99+
offset: float = 0,
100+
trial_ref=None,
101+
) -> events.EventDetector:
102+
"""Builds an EventDetector object whose method is the same for all event types
103+
104+
Args:
105+
method_hs: event detection method for heel strike
106+
method_to: event detection method for toe off
107+
- "Zen" will test the Zeni method
108+
- "Des" will test the Desailly method
109+
- "AC1" to "AC6" will test the Autocorrelation 1 to 6 methods
110+
config: The mapping configurations
111+
offset: offset to be applied to all event timings, default is 0
112+
trial_ref: object of model.Trial, reference trial if the event detection requires a reference
113+
114+
Returns:
115+
EventDetector object
116+
"""
117+
if trial_ref is None:
118+
return events.EventDetectorBuilder.get_event_detector_no_ref(
119+
configs, method_hs, method_to, offset
120+
)
121+
else:
122+
return events.EventDetectorBuilder.get_event_detector_with_ref(
123+
configs, method_hs, method_to, trial_ref, offset
124+
)
125+
126+
127+
def get_GRF_event_detector(
128+
configs: mapping.MappingConfigs, offset: float = 0
129+
) -> events.EventDetector:
130+
"""Builds an EventDetector object whose method is the same for all event types
131+
132+
Args:
133+
config: The mapping configurations
134+
offset: offset to be applied to all event timings, default is 0
135+
136+
Returns:
137+
EventDetector object
138+
"""
139+
return events.EventDetectorBuilder.get_event_detector_no_ref(
140+
configs, "GRF", "GRF", offset
141+
)
142+
143+
144+
def get_mixed_event_detector(
145+
method_hs_l: str,
146+
method_hs_r: str,
147+
method_to_l: str,
148+
method_to_r: str,
149+
configs: mapping.MappingConfigs,
150+
offset: float = 0,
151+
trial=None,
152+
) -> events.EventDetector:
153+
"""Builds an EventDetector object whose method is the same for all event type
154+
155+
Args:
156+
method_to_l: event detection method for left toe off
157+
method_to_r: event detection method for right toe off
158+
method_hs_l: event detection method for left heel strike
159+
method_hs_r: event detection method for right heel strike
160+
- "Zen" will test the Zeni method
161+
- "Des" will test the Desailly method
162+
- "AC1" to "AC6" will test the Autocorrelation 1 to 6 methods
163+
config: The mapping configurations
164+
offset: offset to be applied to all event timings, default is 0
165+
trial_ref: object of model.Trial, reference trial if the event detection requires a reference
166+
167+
Returns:
168+
EventDetector object
169+
"""
170+
return events.EventDetectorBuilder.get_mixed_event_detector(
171+
configs, method_hs_l, method_hs_r, method_to_l, method_to_r, offset, trial
172+
)
173+
174+
95175
def detect_events(
96176
trial: model.Trial,
97-
config: mapping.MappingConfigs,
98-
method: type[events.BaseEventDetection] = events.MarkerEventDetection,
99-
**kwargs,
177+
event_detector: events.EventDetector,
178+
parameters: dict | None = None,
100179
) -> pd.DataFrame:
101180
"""Detects the events in the trial.
102181
103182
Args:
104183
trial: The trial to detect the events for.
105-
config: The mapping configurations
106-
method: The class to use for detecting the events.
107-
Currently, only "Marker" is supported, which implements
108-
the method from Zenis et al. 2008.
109-
Default is "Marker".
110-
**kwargs:
111-
- height: The height of peaks. Default = None
112-
- threshold: The threshold of peaks. Default = None
113-
- distance: The min distance in frames between events. Default = None
114-
- rel_height: The relative height of peak. Default = 0.5
184+
event_detector: object containing detection methods (optimized or not) for each event type
185+
parameters: dictionary of event detection parameters. Default None
115186
116187
Returns:
117188
A DataFrame containing the detected events.
189+
"""
190+
event_table = event_detector.detect_events(trial, parameters)
191+
return event_table
118192

193+
194+
def find_optimal_detectors(
195+
trial_ref: model.Trial,
196+
config: mapping.MappingConfigs,
197+
method_list: list[str] = ["Zen", "Des", "AC1", "AC2", "AC3", "AC4", "AC5", "AC6"],
198+
) -> tuple[events.EventDetector, dict]:
199+
"""Finds the set of best methods that best detect all Gait Event types on a short labeled reference trial
200+
Also returns feedback for user
201+
202+
Args:
203+
trial_ref: The reference trial with some labeled gait events
204+
config: The mapping configurations
205+
method_list: list of methods it tests for
206+
- "Zen" will test the Zeni method
207+
- "Des" will test the Desailly method
208+
- "AC1" to "AC6" will test the Autocorrelation 1 to 6 methods
209+
Returns:
210+
An EventDetector object with optimized detection methods for each gait event
211+
user_show : dict containing the performance of all selected methods, as well as the parameters used to find the events
119212
"""
213+
method_list_mapping = [
214+
events.EventDetectorBuilder.get_method(name) for name in method_list
215+
]
216+
auto_obj = events.AutoEventDetection(config, trial_ref, method_list_mapping)
217+
event_detector, user_show = auto_obj.get_optimised_event_detectors()
218+
return event_detector, user_show
120219

121-
method_obj = method(config, **kwargs)
122220

123-
event_table = method_obj.detect_events(trial)
124-
return event_table
221+
def get_ref_from_GRF(
222+
trial: model.Trial, config: mapping.MappingConfigs, gait_cycles_ref: int = 15
223+
) -> model.Trial:
224+
"""Creates a reference set of events detected with Ground Reaction Forces (if available) for the given trial.
225+
The detected events meet the following set of conditions:
226+
1. Events are regularly spaced (not too close and not too far apart)
227+
2. Detected events should have a GRF value close to 0
228+
3. Event should be followed/preceded by a large slope
229+
4. Order of events is correct
230+
If the required number of events is not found, an error is raised¨
231+
232+
Args:
233+
trial: The trial whose events to be detected and used as reference
234+
config: The mapping configurations
235+
gait_cycles_ref: number of gait cycles to use as reference. Default is 15
236+
237+
Returns:
238+
Trial: the same trial with detected events as attributes
239+
240+
Raises:
241+
ValueError if no events have been selected to use as reference
242+
"""
243+
event_detector = get_GRF_event_detector(config)
244+
events_table = detect_events(trial, event_detector)
245+
246+
obj = events.ReferenceFromGrf(events_table, trial, config, gait_cycles_ref)
247+
trial_ref = obj.get_reference()
248+
if trial_ref.events is not None and not trial_ref.events.empty:
249+
return trial_ref
250+
else:
251+
raise ValueError(
252+
"No valid events detected with GRF. Try manually labeling events to use as reference"
253+
)
125254

126255

127256
def check_events(event_table: pd.DataFrame, method: str = "sequence"):

0 commit comments

Comments
 (0)