sports2d 0.8.16__tar.gz → 0.8.17__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.
- {sports2d-0.8.16 → sports2d-0.8.17}/PKG-INFO +12 -4
- {sports2d-0.8.16 → sports2d-0.8.17}/README.md +10 -2
- {sports2d-0.8.16 → sports2d-0.8.17}/Sports2D/Demo/Config_demo.toml +25 -7
- {sports2d-0.8.16 → sports2d-0.8.17}/Sports2D/Sports2D.py +20 -9
- {sports2d-0.8.16 → sports2d-0.8.17}/Sports2D/process.py +93 -40
- {sports2d-0.8.16 → sports2d-0.8.17}/pyproject.toml +1 -1
- {sports2d-0.8.16 → sports2d-0.8.17}/sports2d.egg-info/PKG-INFO +12 -4
- {sports2d-0.8.16 → sports2d-0.8.17}/sports2d.egg-info/SOURCES.txt +0 -1
- {sports2d-0.8.16 → sports2d-0.8.17}/sports2d.egg-info/requires.txt +1 -1
- sports2d-0.8.16/Sports2D/Utilities/filter.py +0 -176
- {sports2d-0.8.16 → sports2d-0.8.17}/.github/workflows/continuous-integration.yml +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/.github/workflows/joss_pdf.yml +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/.github/workflows/publish-on-release.yml +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/.gitignore +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/CITATION.cff +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Content/Demo_plots.png +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Content/Demo_results.png +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Content/Demo_terminal.png +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Content/Person_selection.png +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Content/Video_tuto_Sports2D_Colab.png +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Content/joint_convention.png +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Content/paper.bib +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Content/paper.md +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Content/sports2d_blender.gif +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Content/sports2d_opensim.gif +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/LICENSE +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Sports2D/Demo/demo.mp4 +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Sports2D/Sports2D.ipynb +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Sports2D/Utilities/__init__.py +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Sports2D/Utilities/common.py +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Sports2D/Utilities/tests.py +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/Sports2D/__init__.py +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/setup.cfg +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/sports2d.egg-info/dependency_links.txt +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/sports2d.egg-info/entry_points.txt +0 -0
- {sports2d-0.8.16 → sports2d-0.8.17}/sports2d.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sports2d
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.17
|
|
4
4
|
Summary: Compute 2D human pose and angles from a video or a webcam.
|
|
5
5
|
Author-email: David Pagnon <contact@david-pagnon.com>
|
|
6
6
|
Maintainer-email: David Pagnon <contact@david-pagnon.com>
|
|
@@ -38,7 +38,7 @@ Requires-Dist: openvino
|
|
|
38
38
|
Requires-Dist: opencv-python
|
|
39
39
|
Requires-Dist: imageio_ffmpeg
|
|
40
40
|
Requires-Dist: deep-sort-realtime
|
|
41
|
-
Requires-Dist: Pose2Sim
|
|
41
|
+
Requires-Dist: Pose2Sim>=0.10.33
|
|
42
42
|
Dynamic: license-file
|
|
43
43
|
|
|
44
44
|
|
|
@@ -521,13 +521,21 @@ sports2d --help
|
|
|
521
521
|
'interpolate': ["", "interpolate missing data. true if not specified"],
|
|
522
522
|
'interp_gap_smaller_than': ["", "interpolate sequences of missing data if they are less than N frames long. 10 if not specified"],
|
|
523
523
|
'fill_large_gaps_with': ["", "last_value, nan, or zeros. last_value if not specified"],
|
|
524
|
+
'sections_to_keep': ["", "all, largest, first, or last. Keep 'all' valid sections even when they are interspersed with undetected chunks, or the 'largest' valid section, or the 'first' one, or the 'last' one"],
|
|
525
|
+
'reject_outliers': ["", "reject outliers with Hampel filter before other filtering methods. true if not specified"],
|
|
524
526
|
'filter': ["", "filter results. true if not specified"],
|
|
525
|
-
'filter_type': ["", "butterworth, gaussian, median, or loess. butterworth if not specified"],
|
|
527
|
+
'filter_type': ["", "butterworth, kalman, gcv_spline, gaussian, median, or loess. butterworth if not specified"],
|
|
526
528
|
'order': ["", "order of the Butterworth filter. 4 if not specified"],
|
|
527
529
|
'cut_off_frequency': ["", "cut-off frequency of the Butterworth filter. 3 if not specified"],
|
|
530
|
+
'trust_ratio': ["", "trust ratio of the Kalman filter: How much more do you trust triangulation results (measurements), than the assumption of constant acceleration(process)? 500 if not specified"],
|
|
531
|
+
'smooth': ["", "dual Kalman smoothing. true if not specified"],
|
|
532
|
+
'gcv_cut_off_frequency': ["", "cut-off frequency of the GCV spline filter. 'auto' if not specified"],
|
|
533
|
+
'smoothing_factor': ["", "smoothing factor of the GCV spline filter (>=0). Ignored if cut_off_frequency != 'auto'. Biases results towards more smoothing (>1) or more fidelity to data (<1). 0.1 if not specified"],
|
|
528
534
|
'sigma_kernel': ["", "sigma of the gaussian filter. 1 if not specified"],
|
|
529
535
|
'nb_values_used': ["", "number of values used for the loess filter. 5 if not specified"],
|
|
530
536
|
'kernel_size': ["", "kernel size of the median filter. 3 if not specified"],
|
|
537
|
+
'butterspeed_order': ["", "order of the Butterworth filter on speed. 4 if not specified"],
|
|
538
|
+
'butterspeed_cut_off_frequency': ["", "cut-off frequency of the Butterworth filter on speed. 6 if not specified"],
|
|
531
539
|
'osim_setup_path': ["", "path to OpenSim setup. '../OpenSim_setup' if not specified"],
|
|
532
540
|
'right_left_symmetry': ["", "right left symmetry. true if not specified"],
|
|
533
541
|
'default_height': ["", "default height for scaling. 1.70 if not specified"],
|
|
@@ -644,7 +652,7 @@ Sports2D:
|
|
|
644
652
|
Draws the skeleton and the keypoints, with a green to red color scale to account for their confidence\
|
|
645
653
|
Draws joint and segment angles on the body, and writes the values either near the joint/segment, or on the upper-left of the image with a progress bar
|
|
646
654
|
|
|
647
|
-
6. **Interpolates and filters results:** Missing pose and angle sequences are interpolated unless gaps are too large. Results are filtered according to the selected filter (among `Butterworth`, `Gaussian`, `LOESS`, or `
|
|
655
|
+
6. **Interpolates and filters results:** Missing pose and angle sequences are interpolated unless gaps are too large. You can reject outliers with a Hampel filter): `--reject_outliers True`. Results are filtered according to the selected filter (among `Butterworth`, `Kalman`, `GCV_spline`, `Gaussian`, `LOESS`, `Median`, or `Butterworth_on_speed`) and their parameters, or not filtered at all if `--filter False`.\
|
|
648
656
|
|
|
649
657
|
7. **Optionally show** processed images, saves them, or saves them as a video\
|
|
650
658
|
**Optionally plots** pose and angle data before and after processing for comparison\
|
|
@@ -478,13 +478,21 @@ sports2d --help
|
|
|
478
478
|
'interpolate': ["", "interpolate missing data. true if not specified"],
|
|
479
479
|
'interp_gap_smaller_than': ["", "interpolate sequences of missing data if they are less than N frames long. 10 if not specified"],
|
|
480
480
|
'fill_large_gaps_with': ["", "last_value, nan, or zeros. last_value if not specified"],
|
|
481
|
+
'sections_to_keep': ["", "all, largest, first, or last. Keep 'all' valid sections even when they are interspersed with undetected chunks, or the 'largest' valid section, or the 'first' one, or the 'last' one"],
|
|
482
|
+
'reject_outliers': ["", "reject outliers with Hampel filter before other filtering methods. true if not specified"],
|
|
481
483
|
'filter': ["", "filter results. true if not specified"],
|
|
482
|
-
'filter_type': ["", "butterworth, gaussian, median, or loess. butterworth if not specified"],
|
|
484
|
+
'filter_type': ["", "butterworth, kalman, gcv_spline, gaussian, median, or loess. butterworth if not specified"],
|
|
483
485
|
'order': ["", "order of the Butterworth filter. 4 if not specified"],
|
|
484
486
|
'cut_off_frequency': ["", "cut-off frequency of the Butterworth filter. 3 if not specified"],
|
|
487
|
+
'trust_ratio': ["", "trust ratio of the Kalman filter: How much more do you trust triangulation results (measurements), than the assumption of constant acceleration(process)? 500 if not specified"],
|
|
488
|
+
'smooth': ["", "dual Kalman smoothing. true if not specified"],
|
|
489
|
+
'gcv_cut_off_frequency': ["", "cut-off frequency of the GCV spline filter. 'auto' if not specified"],
|
|
490
|
+
'smoothing_factor': ["", "smoothing factor of the GCV spline filter (>=0). Ignored if cut_off_frequency != 'auto'. Biases results towards more smoothing (>1) or more fidelity to data (<1). 0.1 if not specified"],
|
|
485
491
|
'sigma_kernel': ["", "sigma of the gaussian filter. 1 if not specified"],
|
|
486
492
|
'nb_values_used': ["", "number of values used for the loess filter. 5 if not specified"],
|
|
487
493
|
'kernel_size': ["", "kernel size of the median filter. 3 if not specified"],
|
|
494
|
+
'butterspeed_order': ["", "order of the Butterworth filter on speed. 4 if not specified"],
|
|
495
|
+
'butterspeed_cut_off_frequency': ["", "cut-off frequency of the Butterworth filter on speed. 6 if not specified"],
|
|
488
496
|
'osim_setup_path': ["", "path to OpenSim setup. '../OpenSim_setup' if not specified"],
|
|
489
497
|
'right_left_symmetry': ["", "right left symmetry. true if not specified"],
|
|
490
498
|
'default_height': ["", "default height for scaling. 1.70 if not specified"],
|
|
@@ -601,7 +609,7 @@ Sports2D:
|
|
|
601
609
|
Draws the skeleton and the keypoints, with a green to red color scale to account for their confidence\
|
|
602
610
|
Draws joint and segment angles on the body, and writes the values either near the joint/segment, or on the upper-left of the image with a progress bar
|
|
603
611
|
|
|
604
|
-
6. **Interpolates and filters results:** Missing pose and angle sequences are interpolated unless gaps are too large. Results are filtered according to the selected filter (among `Butterworth`, `Gaussian`, `LOESS`, or `
|
|
612
|
+
6. **Interpolates and filters results:** Missing pose and angle sequences are interpolated unless gaps are too large. You can reject outliers with a Hampel filter): `--reject_outliers True`. Results are filtered according to the selected filter (among `Butterworth`, `Kalman`, `GCV_spline`, `Gaussian`, `LOESS`, `Median`, or `Butterworth_on_speed`) and their parameters, or not filtered at all if `--filter False`.\
|
|
605
613
|
|
|
606
614
|
7. **Optionally show** processed images, saves them, or saves them as a video\
|
|
607
615
|
**Optionally plots** pose and angle data before and after processing for comparison\
|
|
@@ -125,8 +125,8 @@ joint_angles = ['Right ankle', 'Left ankle', 'Right knee', 'Left knee', 'Right h
|
|
|
125
125
|
segment_angles = ['Right foot', 'Left foot', 'Right shank', 'Left shank', 'Right thigh', 'Left thigh', 'Pelvis', 'Trunk', 'Shoulders', 'Head', 'Right arm', 'Left arm', 'Right forearm', 'Left forearm']
|
|
126
126
|
|
|
127
127
|
# Processing parameters
|
|
128
|
-
flip_left_right = true # Same angles whether the participant faces left/right. Set it to false if you want timeseries to be continuous even when the
|
|
129
|
-
correct_segment_angles_with_floor_angle = true # If the camera is tilted, corrects segment angles as regards to the floor angle. Set to false is the floor is tilted
|
|
128
|
+
flip_left_right = true # Same angles whether the participant faces left/right. Set it to false if you want timeseries to be continuous even when the participant switches their stance.
|
|
129
|
+
correct_segment_angles_with_floor_angle = true # If the camera is tilted, corrects segment angles as regards to the floor angle. Set to false if it is the floor which is actually tilted
|
|
130
130
|
|
|
131
131
|
|
|
132
132
|
[post-processing]
|
|
@@ -135,25 +135,43 @@ interp_gap_smaller_than = 10 # do not interpolate bigger gaps
|
|
|
135
135
|
fill_large_gaps_with = 'last_value' # 'last_value', 'nan', or 'zeros'
|
|
136
136
|
sections_to_keep = 'all' # 'all', 'largest', 'first', 'last'
|
|
137
137
|
# keep 'all' valid sections even when they are interspersed with undetected chunks, or the 'largest' valid section, or the 'first' one, or the 'last' one
|
|
138
|
+
reject_outliers = true # Hampel filter for outlier rejection before other filtering methods. Rejects outliers that are outside of a 95% confidence interal from the median in a sliding window of size 7.
|
|
139
|
+
|
|
138
140
|
filter = true
|
|
139
|
-
show_graphs = true
|
|
140
|
-
filter_type = 'butterworth'
|
|
141
|
+
show_graphs = true # Show plots of raw and processed results
|
|
142
|
+
filter_type = 'butterworth' # butterworth, gcv_spline, kalman, gaussian, loess, median, butterworth_on_speed
|
|
143
|
+
# Most intuitive and standard filter in biomechanics
|
|
141
144
|
[post-processing.butterworth]
|
|
142
145
|
order = 4
|
|
143
146
|
cut_off_frequency = 6 # Hz # Will be divided by slowmo_factor to be equivalent to non slowed-down video
|
|
144
|
-
|
|
145
|
-
|
|
147
|
+
|
|
148
|
+
# Used in countless applications, this one is a simplified Kalman filter
|
|
149
|
+
[post-processing.kalman]
|
|
150
|
+
# How much more do you trust triangulation results (measurements), than the assumption of constant acceleration(process)?
|
|
151
|
+
trust_ratio = 500 # = measurement_trust/process_trust ~= process_noise/measurement_noise
|
|
152
|
+
smooth = true # should be true, unless you need real-time filtering
|
|
153
|
+
|
|
154
|
+
# Automatically determines optimal parameters for each point, which is good when some move faster than others (eg fingers vs hips).
|
|
155
|
+
[post-processing.gcv_spline]
|
|
156
|
+
cut_off_frequency = 'auto' # 'auto' or int # If int, behaves like a Butterworth filter. 'auto' is sometimes unstable
|
|
157
|
+
smoothing_factor = 0.1 # >=0, ignored if cut_off_frequency != 'auto'. Biases results towards more smoothing (>1) or more fidelity to data (<1)
|
|
158
|
+
|
|
146
159
|
[post-processing.loess]
|
|
147
160
|
nb_values_used = 5 # = fraction of data used * nb frames
|
|
161
|
+
[post-processing.gaussian]
|
|
162
|
+
sigma_kernel = 1 #px
|
|
148
163
|
[post-processing.median]
|
|
149
164
|
kernel_size = 3
|
|
165
|
+
[post-processing.butterworth_on_speed]
|
|
166
|
+
order = 4
|
|
167
|
+
cut_off_frequency = 10 # Hz
|
|
150
168
|
|
|
151
169
|
|
|
152
170
|
[kinematics]
|
|
153
171
|
do_ik = false # Do scaling and inverse kinematics?
|
|
154
172
|
use_augmentation = false # true or false (lowercase) # Set to true if you want to use the model with augmented markers
|
|
155
173
|
feet_on_floor = false # true or false (lowercase) # Set to false if you want to use the model with feet not on the floor (e.g. running, jumping, etc.)
|
|
156
|
-
use_simple_model = false # true or false #
|
|
174
|
+
use_simple_model = false # true or false # IK 10+ times faster, but no muscles or flexible spine, no patella
|
|
157
175
|
participant_mass = [55.0, 67.0] # kg # defaults to 70 if not provided. No influence on kinematics (motion), only on kinetics (forces)
|
|
158
176
|
right_left_symmetry = true # true or false (lowercase) # Set to false only if you have good reasons to think the participant is not symmetrical (e.g. prosthetic limb)
|
|
159
177
|
|
|
@@ -141,7 +141,7 @@ DEFAULT_CONFIG = {'base': {'video_input': ['demo.mp4'],
|
|
|
141
141
|
'save_angles': True,
|
|
142
142
|
'result_dir': ''
|
|
143
143
|
},
|
|
144
|
-
'pose': {'slowmo_factor': 1,
|
|
144
|
+
'pose': {'slowmo_factor': 1.0,
|
|
145
145
|
'pose_model': 'body_with_feet',
|
|
146
146
|
'mode': 'balanced',
|
|
147
147
|
'det_frequency': 4,
|
|
@@ -233,13 +233,17 @@ DEFAULT_CONFIG = {'base': {'video_input': ['demo.mp4'],
|
|
|
233
233
|
'interp_gap_smaller_than': 10,
|
|
234
234
|
'fill_large_gaps_with': 'last_value',
|
|
235
235
|
'sections_to_keep':'all',
|
|
236
|
+
'reject_outliers': True,
|
|
236
237
|
'filter': True,
|
|
237
238
|
'show_graphs': True,
|
|
238
239
|
'filter_type': 'butterworth',
|
|
239
|
-
'butterworth': {'order': 4, 'cut_off_frequency': 6},
|
|
240
|
+
'butterworth': {'order': 4, 'cut_off_frequency': 6.0},
|
|
241
|
+
'kalman': {'trust_ratio': 500.0, 'smooth':True},
|
|
242
|
+
'gcv_spline': {'gcv_cut_off_frequency': 'auto', 'smoothing_factor': 0.1},
|
|
240
243
|
'gaussian': {'sigma_kernel': 1},
|
|
241
244
|
'loess': {'nb_values_used': 5},
|
|
242
|
-
'median': {'kernel_size': 3}
|
|
245
|
+
'median': {'kernel_size': 3},
|
|
246
|
+
'butterworth_on_speed': {'butterspeed_order': 4, 'butterspeed_cut_off_frequency': 6.0},
|
|
243
247
|
},
|
|
244
248
|
'kinematics':{'do_ik': False,
|
|
245
249
|
'use_augmentation': False,
|
|
@@ -251,9 +255,9 @@ DEFAULT_CONFIG = {'base': {'video_input': ['demo.mp4'],
|
|
|
251
255
|
'remove_individual_scaling_setup': True,
|
|
252
256
|
'remove_individual_ik_setup': True,
|
|
253
257
|
'fastest_frames_to_remove_percent': 0.1,
|
|
254
|
-
'close_to_zero_speed_px': 50,
|
|
258
|
+
'close_to_zero_speed_px': 50.0,
|
|
255
259
|
'close_to_zero_speed_m': 0.2,
|
|
256
|
-
'large_hip_knee_angles': 45,
|
|
260
|
+
'large_hip_knee_angles': 45.0,
|
|
257
261
|
'trimmed_extrema_percent': 0.5,
|
|
258
262
|
'osim_setup_path': '../OpenSim_setup'
|
|
259
263
|
},
|
|
@@ -298,7 +302,7 @@ CONFIG_HELP = {'config': ["C", "path to a toml configuration file"],
|
|
|
298
302
|
'do_ik': ["", "do inverse kinematics. false if not specified"],
|
|
299
303
|
'use_augmentation': ["", "Use LSTM marker augmentation. false if not specified"],
|
|
300
304
|
'feet_on_floor': ["", "offset marker augmentation results so that feet are at floor level. true if not specified"],
|
|
301
|
-
'use_simple_model': ["", "
|
|
305
|
+
'use_simple_model': ["", "IK 10+ times faster, but no muscles or flexible spine, no patella. false if not specified"],
|
|
302
306
|
'participant_mass': ["", "mass of the participant in kg or none. Defaults to 70 if not provided. No influence on kinematics (motion), only on kinetics (forces)"],
|
|
303
307
|
'close_to_zero_speed_m': ["","Sum for all keypoints: about 50 px/frame or 0.2 m/frame"],
|
|
304
308
|
'tracking_mode': ["", "'sports2d' or 'deepsort'. 'deepsort' is slower, harder to parametrize but can be more robust if correctly tuned"],
|
|
@@ -314,19 +318,26 @@ CONFIG_HELP = {'config': ["C", "path to a toml configuration file"],
|
|
|
314
318
|
'large_hip_knee_angles': ["", "Hip and knee angles below this value are considered as imprecise. Defaults to 45"],
|
|
315
319
|
'trimmed_extrema_percent': ["", "Proportion of the most extreme segment values to remove before calculating their mean. Defaults to 50"],
|
|
316
320
|
'fontSize': ["", "font size for angle values. 0.3 if not specified"],
|
|
317
|
-
'flip_left_right': ["", "true or false. Flips angles when the person faces the other side. The person looks to the right if their toe keypoint is to the right of their heel. Set it to false if the person is sprinting or if you want timeseries to be continuous even when the
|
|
318
|
-
'correct_segment_angles_with_floor_angle': ["", "true or false. If the camera is tilted, corrects segment angles as regards to the floor angle. Set to false is the floor is tilted
|
|
321
|
+
'flip_left_right': ["", "true or false. Flips angles when the person faces the other side. The person looks to the right if their toe keypoint is to the right of their heel. Set it to false if the person is sprinting or if you want timeseries to be continuous even when the participant switches their stance. true if not specified"],
|
|
322
|
+
'correct_segment_angles_with_floor_angle': ["", "true or false. If the camera is tilted, corrects segment angles as regards to the floor angle. Set to false if it is actually the floor which is tilted, not the camera. True if not specified"],
|
|
319
323
|
'interpolate': ["", "interpolate missing data. true if not specified"],
|
|
320
324
|
'interp_gap_smaller_than': ["", "interpolate sequences of missing data if they are less than N frames long. 10 if not specified"],
|
|
321
325
|
'fill_large_gaps_with': ["", "last_value, nan, or zeros. last_value if not specified"],
|
|
322
326
|
'sections_to_keep': ["", "all, largest, first, or last. Keep 'all' valid sections even when they are interspersed with undetected chunks, or the 'largest' valid section, or the 'first' one, or the 'last' one"],
|
|
327
|
+
'reject_outliers': ["", "reject outliers with Hampel filter before other filtering methods. true if not specified"],
|
|
323
328
|
'filter': ["", "filter results. true if not specified"],
|
|
324
|
-
'filter_type': ["", "butterworth, gaussian, median, or loess. butterworth if not specified"],
|
|
329
|
+
'filter_type': ["", "butterworth, kalman, gcv_spline, gaussian, median, or loess. butterworth if not specified"],
|
|
325
330
|
'order': ["", "order of the Butterworth filter. 4 if not specified"],
|
|
326
331
|
'cut_off_frequency': ["", "cut-off frequency of the Butterworth filter. 3 if not specified"],
|
|
332
|
+
'trust_ratio': ["", "trust ratio of the Kalman filter: How much more do you trust triangulation results (measurements), than the assumption of constant acceleration(process)? 500 if not specified"],
|
|
333
|
+
'smooth': ["", "dual Kalman smoothing. true if not specified"],
|
|
334
|
+
'gcv_cut_off_frequency': ["", "cut-off frequency of the GCV spline filter. 'auto' if not specified"],
|
|
335
|
+
'smoothing_factor': ["", "smoothing factor of the GCV spline filter (>=0). Ignored if cut_off_frequency != 'auto'. Biases results towards more smoothing (>1) or more fidelity to data (<1). 0.1 if not specified"],
|
|
327
336
|
'sigma_kernel': ["", "sigma of the gaussian filter. 1 if not specified"],
|
|
328
337
|
'nb_values_used': ["", "number of values used for the loess filter. 5 if not specified"],
|
|
329
338
|
'kernel_size': ["", "kernel size of the median filter. 3 if not specified"],
|
|
339
|
+
'butterspeed_order': ["", "order of the Butterworth filter on speed. 4 if not specified"],
|
|
340
|
+
'butterspeed_cut_off_frequency': ["", "cut-off frequency of the Butterworth filter on speed. 6 if not specified"],
|
|
330
341
|
'osim_setup_path': ["", "path to OpenSim setup. '../OpenSim_setup' if not specified"],
|
|
331
342
|
'right_left_symmetry': ["", "right left symmetry. true if not specified"],
|
|
332
343
|
'default_height': ["", "default height for scaling. 1.70 if not specified"],
|
|
@@ -79,11 +79,12 @@ from matplotlib import patheffects
|
|
|
79
79
|
from rtmlib import PoseTracker, BodyWithFeet, Wholebody, Body, Hand, Custom
|
|
80
80
|
from deep_sort_realtime.deepsort_tracker import DeepSort
|
|
81
81
|
|
|
82
|
-
from Sports2D.Utilities import filter
|
|
83
82
|
from Sports2D.Utilities.common import *
|
|
84
83
|
from Pose2Sim.common import *
|
|
85
84
|
from Pose2Sim.skeletons import *
|
|
86
85
|
from Pose2Sim.triangulation import indices_of_first_last_non_nan_chunks
|
|
86
|
+
from Pose2Sim.filtering import *
|
|
87
|
+
|
|
87
88
|
|
|
88
89
|
DEFAULT_MASS = 70
|
|
89
90
|
DEFAULT_HEIGHT = 1.7
|
|
@@ -196,7 +197,7 @@ def setup_video(video_file_path, save_vid, vid_output_path):
|
|
|
196
197
|
|
|
197
198
|
def setup_model_class_mode(pose_model, mode, config_dict={}):
|
|
198
199
|
'''
|
|
199
|
-
|
|
200
|
+
Set up the pose model class and mode for the pose tracker.
|
|
200
201
|
'''
|
|
201
202
|
|
|
202
203
|
if pose_model.upper() in ('HALPE_26', 'BODY_WITH_FEET'):
|
|
@@ -258,6 +259,10 @@ def setup_model_class_mode(pose_model, mode, config_dict={}):
|
|
|
258
259
|
det_class=det_class, det=det, det_input_size=det_input_size,
|
|
259
260
|
pose_class=pose_class, pose=pose, pose_input_size=pose_input_size)
|
|
260
261
|
logging.info(f"Using model {model_name} with the following custom parameters: {mode}.")
|
|
262
|
+
|
|
263
|
+
if pose_class == 'RTMO' and model_name != 'COCO_17':
|
|
264
|
+
logging.warning("RTMO currently only supports 'Body' pose_model. Switching to 'Body'.")
|
|
265
|
+
pose_model = eval('COCO_17')
|
|
261
266
|
|
|
262
267
|
except (json.JSONDecodeError, TypeError):
|
|
263
268
|
logging.warning("Invalid mode. Must be 'lightweight', 'balanced', 'performance', or '''{dictionary}''' of parameters within triple quotes. Make sure input_sizes are within square brackets.")
|
|
@@ -1436,16 +1441,20 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1436
1441
|
sections_to_keep = config_dict.get('post-processing').get('sections_to_keep')
|
|
1437
1442
|
|
|
1438
1443
|
do_filter = config_dict.get('post-processing').get('filter')
|
|
1444
|
+
reject_outliers = config_dict.get('post-processing').get('reject_outliers', False)
|
|
1439
1445
|
show_plots = config_dict.get('post-processing').get('show_graphs')
|
|
1440
1446
|
filter_type = config_dict.get('post-processing').get('filter_type')
|
|
1441
1447
|
butterworth_filter_order = config_dict.get('post-processing').get('butterworth').get('order')
|
|
1442
1448
|
butterworth_filter_cutoff = config_dict.get('post-processing').get('butterworth').get('cut_off_frequency')
|
|
1449
|
+
gcv_filter_cutoff = config_dict.get('post-processing').get('gcv_spline').get('cut_off_frequency')
|
|
1450
|
+
gcv_filter_smoothingfactor = config_dict.get('post-processing').get('gcv_spline').get('smoothing_factor')
|
|
1451
|
+
kalman_filter_trust_ratio = config_dict.get('post-processing').get('kalman').get('trust_ratio')
|
|
1452
|
+
kalman_filter_smooth = config_dict.get('post-processing').get('kalman').get('smooth')
|
|
1443
1453
|
gaussian_filter_kernel = config_dict.get('post-processing').get('gaussian').get('sigma_kernel')
|
|
1444
1454
|
loess_filter_kernel = config_dict.get('post-processing').get('loess').get('nb_values_used')
|
|
1445
1455
|
median_filter_kernel = config_dict.get('post-processing').get('median').get('kernel_size')
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
gaussian_filter_kernel, loess_filter_kernel, median_filter_kernel]
|
|
1456
|
+
butterworthspeed_filter_order = config_dict.get('post-processing').get('butterworth_on_speed').get('order')
|
|
1457
|
+
butterworthspeed_filter_cutoff = config_dict.get('post-processing').get('butterworth_on_speed').get('cut_off_frequency')
|
|
1449
1458
|
|
|
1450
1459
|
# Create output directories
|
|
1451
1460
|
if video_file == "webcam":
|
|
@@ -1476,12 +1485,14 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1476
1485
|
trimmed_extrema_percent = config_dict.get('kinematics').get('trimmed_extrema_percent')
|
|
1477
1486
|
close_to_zero_speed_px = config_dict.get('kinematics').get('close_to_zero_speed_px')
|
|
1478
1487
|
close_to_zero_speed_m = config_dict.get('kinematics').get('close_to_zero_speed_m')
|
|
1479
|
-
if do_ik or use_augmentation:
|
|
1488
|
+
if do_ik or use_augmentation or do_filter:
|
|
1480
1489
|
try:
|
|
1481
1490
|
if use_augmentation:
|
|
1482
1491
|
from Pose2Sim.markerAugmentation import augment_markers_all
|
|
1483
1492
|
if do_ik:
|
|
1484
1493
|
from Pose2Sim.kinematics import kinematics_all
|
|
1494
|
+
if do_filter:
|
|
1495
|
+
from Pose2Sim.filtering import filter_all
|
|
1485
1496
|
except ImportError:
|
|
1486
1497
|
logging.error("OpenSim package is not installed. Please install it to use inverse kinematics or marker augmentation features (see 'Full install' section of the documentation).")
|
|
1487
1498
|
raise ImportError("OpenSim package is not installed. Please install it to use inverse kinematics or marker augmentation features (see 'Full install' section of the documentation).")
|
|
@@ -1498,6 +1509,23 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1498
1509
|
pose3d_dir.mkdir(parents=True, exist_ok=True)
|
|
1499
1510
|
kinematics_dir = Path(output_dir) / 'kinematics'
|
|
1500
1511
|
kinematics_dir.mkdir(parents=True, exist_ok=True)
|
|
1512
|
+
|
|
1513
|
+
if do_filter:
|
|
1514
|
+
print(filter_type)
|
|
1515
|
+
Pose2Sim_config_dict['filtering']['reject_outliers'] = reject_outliers
|
|
1516
|
+
Pose2Sim_config_dict['filtering']['filter'] = do_filter
|
|
1517
|
+
Pose2Sim_config_dict['filtering']['type'] = filter_type
|
|
1518
|
+
Pose2Sim_config_dict['filtering']['butterworth']['order'] = butterworth_filter_order
|
|
1519
|
+
Pose2Sim_config_dict['filtering']['butterworth']['cut_off_frequency'] = butterworth_filter_cutoff
|
|
1520
|
+
Pose2Sim_config_dict['filtering']['gcv_spline']['cut_off_frequency'] = gcv_filter_cutoff
|
|
1521
|
+
Pose2Sim_config_dict['filtering']['gcv_spline']['smoothing_factor'] = gcv_filter_smoothingfactor
|
|
1522
|
+
Pose2Sim_config_dict['filtering']['kalman']['trust_ratio'] = kalman_filter_trust_ratio
|
|
1523
|
+
Pose2Sim_config_dict['filtering']['kalman']['smooth'] = kalman_filter_smooth
|
|
1524
|
+
Pose2Sim_config_dict['filtering']['gaussian']['sigma_kernel'] = gaussian_filter_kernel
|
|
1525
|
+
Pose2Sim_config_dict['filtering']['loess']['nb_values_used'] = loess_filter_kernel
|
|
1526
|
+
Pose2Sim_config_dict['filtering']['median']['kernel_size'] = median_filter_kernel
|
|
1527
|
+
Pose2Sim_config_dict['filtering']['butterworth_on_speed']['order'] = butterworthspeed_filter_order
|
|
1528
|
+
Pose2Sim_config_dict['filtering']['butterworth_on_speed']['cut_off_frequency'] = butterworthspeed_filter_cutoff
|
|
1501
1529
|
|
|
1502
1530
|
|
|
1503
1531
|
# Set up video capture
|
|
@@ -1570,8 +1598,8 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1570
1598
|
# if tracking_mode not in ['deepsort', 'sports2d']:
|
|
1571
1599
|
# logging.warning(f"Tracking mode {tracking_mode} not recognized. Using sports2d method.")
|
|
1572
1600
|
# tracking_mode = 'sports2d'
|
|
1573
|
-
logging.info(f'Pose tracking set up for "{pose_model_name}" model.')
|
|
1574
|
-
logging.info(f'Mode: {mode}.\n')
|
|
1601
|
+
# logging.info(f'Pose tracking set up for "{pose_model_name}" model.')
|
|
1602
|
+
# logging.info(f'Mode: {mode}.\n')
|
|
1575
1603
|
logging.info(f'Persons are detected every {det_frequency} frames and tracked inbetween. Tracking is done with {tracking_mode}.')
|
|
1576
1604
|
if tracking_mode == 'deepsort': logging.info(f'Deepsort parameters: {deepsort_params}.')
|
|
1577
1605
|
logging.info(f'{"All persons are" if nb_persons_to_detect=="all" else f"{nb_persons_to_detect} persons are" if nb_persons_to_detect>1 else "1 person is"} analyzed. Person ordering method is {person_ordering_method}.')
|
|
@@ -1593,7 +1621,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1593
1621
|
ang_params = angle_dict.get(ang_name)
|
|
1594
1622
|
kpts = ang_params[0]
|
|
1595
1623
|
if any(item not in keypoints_names+['Neck', 'Hip'] for item in kpts):
|
|
1596
|
-
logging.warning(f"Skipping {ang_name} angle computation because at least one of the following keypoints is not provided by the model: {ang_params[0]}.")
|
|
1624
|
+
logging.warning(f"Skipping {ang_name} angle computation because at least one of the following keypoints is not provided by the pose estimation model: {ang_params[0]}.")
|
|
1597
1625
|
|
|
1598
1626
|
|
|
1599
1627
|
#%% ==================================================
|
|
@@ -1724,7 +1752,6 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1724
1752
|
img = draw_keypts(img, valid_X, valid_Y, valid_scores, cmap_str='RdYlGn')
|
|
1725
1753
|
img = draw_skel(img, valid_X, valid_Y, pose_model)
|
|
1726
1754
|
if calculate_angles:
|
|
1727
|
-
|
|
1728
1755
|
img = draw_angles(img, valid_X, valid_Y, valid_angles_flipped, valid_X_flipped, new_keypoints_ids, new_keypoints_names, angle_names, display_angle_values_on=display_angle_values_on, colors=colors, fontSize=fontSize, thickness=thickness)
|
|
1729
1756
|
cv2.imshow(f'{video_file} Sports2D', img)
|
|
1730
1757
|
if (cv2.waitKey(1) & 0xFF) == ord('q') or (cv2.waitKey(1) & 0xFF) == 27:
|
|
@@ -1859,31 +1886,45 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1859
1886
|
all_frames_Y_person_interp.replace(np.nan, 0, inplace=True)
|
|
1860
1887
|
|
|
1861
1888
|
# Filter
|
|
1862
|
-
if
|
|
1889
|
+
if reject_outliers:
|
|
1890
|
+
logging.info('Rejecting outliers with Hampel filter.')
|
|
1891
|
+
all_frames_X_person_interp = all_frames_X_person_interp.apply(hampel_filter, axis=0, args = [round(7*frame_rate/30), 2])
|
|
1892
|
+
all_frames_Y_person_interp = all_frames_Y_person_interp.apply(hampel_filter, axis=0, args = [round(7*frame_rate/30), 2])
|
|
1893
|
+
|
|
1894
|
+
if not do_filter:
|
|
1863
1895
|
logging.info(f'No filtering.')
|
|
1864
1896
|
all_frames_X_person_filt = all_frames_X_person_interp
|
|
1865
1897
|
all_frames_Y_person_filt = all_frames_Y_person_interp
|
|
1866
1898
|
else:
|
|
1867
|
-
filter_type
|
|
1868
|
-
|
|
1869
|
-
cutoff = filter_options[3]
|
|
1899
|
+
if filter_type == ('butterworth' or 'butterworth_on_speed'):
|
|
1900
|
+
cutoff = butterworth_filter_cutoff
|
|
1870
1901
|
if video_file == 'webcam':
|
|
1871
1902
|
if cutoff / (fps / 2) >= 1:
|
|
1872
1903
|
cutoff_old = cutoff
|
|
1873
1904
|
cutoff = fps/(2+0.001)
|
|
1874
1905
|
args = f'\n{cutoff_old:.1f} Hz cut-off framerate too large for a real-time framerate of {fps:.1f} Hz. Using a cut-off framerate of {cutoff:.1f} Hz instead.'
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1906
|
+
butterworth_filter_cutoff = cutoff
|
|
1907
|
+
filt_type = 'Butterworth' if filter_type == 'butterworth' else 'Butterworth on speed'
|
|
1908
|
+
args = f'{filt_type} filter, {butterworth_filter_order}th order, {butterworth_filter_cutoff} Hz.'
|
|
1909
|
+
frame_rate = fps
|
|
1910
|
+
elif filter_type == 'gcv_spline':
|
|
1911
|
+
args = f'GVC Spline filter, which automatically evaluates the best trade-off between smoothness and fidelity to data.'
|
|
1912
|
+
elif filter_type == 'kalman':
|
|
1913
|
+
args = f'Kalman filter, trusting measurement {kalman_filter_trust_ratio} times more than the process matrix.'
|
|
1914
|
+
elif filter_type == 'gaussian':
|
|
1915
|
+
args = f'Gaussian filter, Sigma kernel {gaussian_filter_kernel}.'
|
|
1916
|
+
elif filter_type == 'loess':
|
|
1917
|
+
args = f'LOESS filter, window size of {loess_filter_kernel} frames.'
|
|
1918
|
+
elif filter_type == 'median':
|
|
1919
|
+
args = f'Median filter, kernel of {median_filter_kernel}.'
|
|
1920
|
+
else:
|
|
1921
|
+
logging.error(f"Invalid filter_type: {filter_type}. Must be 'butterworth', 'gcv_spline', 'kalman', 'gaussian', 'loess', or 'median'.")
|
|
1922
|
+
raise ValueError(f"Invalid filter_type: {filter_type}. Must be 'butterworth', 'gcv_spline', 'kalman', 'gaussian', 'loess', or 'median'.")
|
|
1923
|
+
|
|
1884
1924
|
logging.info(f'Filtering with {args}')
|
|
1885
|
-
all_frames_X_person_filt = all_frames_X_person_interp.apply(
|
|
1886
|
-
all_frames_Y_person_filt = all_frames_Y_person_interp.apply(
|
|
1925
|
+
all_frames_X_person_filt = all_frames_X_person_interp.apply(filter1d, axis=0, args = [Pose2Sim_config_dict, filter_type, frame_rate])
|
|
1926
|
+
all_frames_Y_person_filt = all_frames_Y_person_interp.apply(filter1d, axis=0, args = [Pose2Sim_config_dict, filter_type, frame_rate])
|
|
1927
|
+
|
|
1887
1928
|
|
|
1888
1929
|
# Build TRC file
|
|
1889
1930
|
trc_data_i = trc_data_from_XYZtime(all_frames_X_person_filt, all_frames_Y_person_filt, all_frames_Z_homog, all_frames_time)
|
|
@@ -2094,29 +2135,41 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
2094
2135
|
all_frames_angles_person_interp.replace(np.nan, 0, inplace=True)
|
|
2095
2136
|
|
|
2096
2137
|
# Filter
|
|
2097
|
-
if
|
|
2138
|
+
if reject_outliers:
|
|
2139
|
+
logging.info(f'Rejecting outliers with Hampel filter.')
|
|
2140
|
+
all_frames_angles_person_interp = all_frames_angles_person_interp.apply(hampel_filter, axis=0)
|
|
2141
|
+
|
|
2142
|
+
if not do_filter:
|
|
2098
2143
|
logging.info(f'No filtering.')
|
|
2099
2144
|
all_frames_angles_person_filt = all_frames_angles_person_interp
|
|
2100
2145
|
else:
|
|
2101
|
-
filter_type
|
|
2102
|
-
|
|
2103
|
-
cutoff = filter_options[3]
|
|
2146
|
+
if filter_type == ('butterworth' or 'butterworth_on_speed'):
|
|
2147
|
+
cutoff = butterworth_filter_cutoff
|
|
2104
2148
|
if video_file == 'webcam':
|
|
2105
2149
|
if cutoff / (fps / 2) >= 1:
|
|
2106
2150
|
cutoff_old = cutoff
|
|
2107
2151
|
cutoff = fps/(2+0.001)
|
|
2108
2152
|
args = f'\n{cutoff_old:.1f} Hz cut-off framerate too large for a real-time framerate of {fps:.1f} Hz. Using a cut-off framerate of {cutoff:.1f} Hz instead.'
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2153
|
+
butterworth_filter_cutoff = cutoff
|
|
2154
|
+
filt_type = 'Butterworth' if filter_type == 'butterworth' else 'Butterworth on speed'
|
|
2155
|
+
args = f'{filt_type} filter, {butterworth_filter_order}th order, {butterworth_filter_cutoff} Hz.'
|
|
2156
|
+
frame_rate = fps
|
|
2157
|
+
elif filter_type == 'gcv_spline':
|
|
2158
|
+
args = f'GVC Spline filter, which automatically evaluates the best trade-off between smoothness and fidelity to data.'
|
|
2159
|
+
elif filter_type == 'kalman':
|
|
2160
|
+
args = f'Kalman filter, trusting measurement {kalman_filter_trust_ratio} times more than the process matrix.'
|
|
2161
|
+
elif filter_type == 'gaussian':
|
|
2162
|
+
args = f'Gaussian filter, Sigma kernel {gaussian_filter_kernel}.'
|
|
2163
|
+
elif filter_type == 'loess':
|
|
2164
|
+
args = f'LOESS filter, window size of {loess_filter_kernel} frames.'
|
|
2165
|
+
elif filter_type == 'median':
|
|
2166
|
+
args = f'Median filter, kernel of {median_filter_kernel}.'
|
|
2167
|
+
else:
|
|
2168
|
+
logging.error(f"Invalid filter_type: {filter_type}. Must be 'butterworth', 'gcv_spline', 'kalman', 'gaussian', 'loess', or 'median'.")
|
|
2169
|
+
raise ValueError(f"Invalid filter_type: {filter_type}. Must be 'butterworth', 'gcv_spline', 'kalman', 'gaussian', 'loess', or 'median'.")
|
|
2170
|
+
|
|
2171
|
+
logging.info(f'Filtering with {args}.')
|
|
2172
|
+
all_frames_angles_person_filt = all_frames_angles_person_interp.apply(filter1d, axis=0, args = [Pose2Sim_config_dict, filter_type, frame_rate])
|
|
2120
2173
|
|
|
2121
2174
|
# Add floor_angle_estim to segment angles
|
|
2122
2175
|
if correct_segment_angles_with_floor_angle and to_meters:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sports2d
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.17
|
|
4
4
|
Summary: Compute 2D human pose and angles from a video or a webcam.
|
|
5
5
|
Author-email: David Pagnon <contact@david-pagnon.com>
|
|
6
6
|
Maintainer-email: David Pagnon <contact@david-pagnon.com>
|
|
@@ -38,7 +38,7 @@ Requires-Dist: openvino
|
|
|
38
38
|
Requires-Dist: opencv-python
|
|
39
39
|
Requires-Dist: imageio_ffmpeg
|
|
40
40
|
Requires-Dist: deep-sort-realtime
|
|
41
|
-
Requires-Dist: Pose2Sim
|
|
41
|
+
Requires-Dist: Pose2Sim>=0.10.33
|
|
42
42
|
Dynamic: license-file
|
|
43
43
|
|
|
44
44
|
|
|
@@ -521,13 +521,21 @@ sports2d --help
|
|
|
521
521
|
'interpolate': ["", "interpolate missing data. true if not specified"],
|
|
522
522
|
'interp_gap_smaller_than': ["", "interpolate sequences of missing data if they are less than N frames long. 10 if not specified"],
|
|
523
523
|
'fill_large_gaps_with': ["", "last_value, nan, or zeros. last_value if not specified"],
|
|
524
|
+
'sections_to_keep': ["", "all, largest, first, or last. Keep 'all' valid sections even when they are interspersed with undetected chunks, or the 'largest' valid section, or the 'first' one, or the 'last' one"],
|
|
525
|
+
'reject_outliers': ["", "reject outliers with Hampel filter before other filtering methods. true if not specified"],
|
|
524
526
|
'filter': ["", "filter results. true if not specified"],
|
|
525
|
-
'filter_type': ["", "butterworth, gaussian, median, or loess. butterworth if not specified"],
|
|
527
|
+
'filter_type': ["", "butterworth, kalman, gcv_spline, gaussian, median, or loess. butterworth if not specified"],
|
|
526
528
|
'order': ["", "order of the Butterworth filter. 4 if not specified"],
|
|
527
529
|
'cut_off_frequency': ["", "cut-off frequency of the Butterworth filter. 3 if not specified"],
|
|
530
|
+
'trust_ratio': ["", "trust ratio of the Kalman filter: How much more do you trust triangulation results (measurements), than the assumption of constant acceleration(process)? 500 if not specified"],
|
|
531
|
+
'smooth': ["", "dual Kalman smoothing. true if not specified"],
|
|
532
|
+
'gcv_cut_off_frequency': ["", "cut-off frequency of the GCV spline filter. 'auto' if not specified"],
|
|
533
|
+
'smoothing_factor': ["", "smoothing factor of the GCV spline filter (>=0). Ignored if cut_off_frequency != 'auto'. Biases results towards more smoothing (>1) or more fidelity to data (<1). 0.1 if not specified"],
|
|
528
534
|
'sigma_kernel': ["", "sigma of the gaussian filter. 1 if not specified"],
|
|
529
535
|
'nb_values_used': ["", "number of values used for the loess filter. 5 if not specified"],
|
|
530
536
|
'kernel_size': ["", "kernel size of the median filter. 3 if not specified"],
|
|
537
|
+
'butterspeed_order': ["", "order of the Butterworth filter on speed. 4 if not specified"],
|
|
538
|
+
'butterspeed_cut_off_frequency': ["", "cut-off frequency of the Butterworth filter on speed. 6 if not specified"],
|
|
531
539
|
'osim_setup_path': ["", "path to OpenSim setup. '../OpenSim_setup' if not specified"],
|
|
532
540
|
'right_left_symmetry': ["", "right left symmetry. true if not specified"],
|
|
533
541
|
'default_height': ["", "default height for scaling. 1.70 if not specified"],
|
|
@@ -644,7 +652,7 @@ Sports2D:
|
|
|
644
652
|
Draws the skeleton and the keypoints, with a green to red color scale to account for their confidence\
|
|
645
653
|
Draws joint and segment angles on the body, and writes the values either near the joint/segment, or on the upper-left of the image with a progress bar
|
|
646
654
|
|
|
647
|
-
6. **Interpolates and filters results:** Missing pose and angle sequences are interpolated unless gaps are too large. Results are filtered according to the selected filter (among `Butterworth`, `Gaussian`, `LOESS`, or `
|
|
655
|
+
6. **Interpolates and filters results:** Missing pose and angle sequences are interpolated unless gaps are too large. You can reject outliers with a Hampel filter): `--reject_outliers True`. Results are filtered according to the selected filter (among `Butterworth`, `Kalman`, `GCV_spline`, `Gaussian`, `LOESS`, `Median`, or `Butterworth_on_speed`) and their parameters, or not filtered at all if `--filter False`.\
|
|
648
656
|
|
|
649
657
|
7. **Optionally show** processed images, saves them, or saves them as a video\
|
|
650
658
|
**Optionally plots** pose and angle data before and after processing for comparison\
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
'''
|
|
6
|
-
##################################################
|
|
7
|
-
## Filter TRC files ##
|
|
8
|
-
##################################################
|
|
9
|
-
|
|
10
|
-
Filters pandans columns or numpy arrays.
|
|
11
|
-
Available filters: Butterworth, Gaussian, LOESS, Median.
|
|
12
|
-
|
|
13
|
-
Usage:
|
|
14
|
-
col_filtered = filter1d(col, *filter_options)
|
|
15
|
-
filter_options = (do_filter, filter_type, butterworth_filter_order, butterworth_filter_cutoff, frame_rate, gaussian_filter_kernel, loess_filter_kernel, median_filter_kernel)
|
|
16
|
-
bool str int int int int int int
|
|
17
|
-
|
|
18
|
-
'''
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
## INIT
|
|
22
|
-
from importlib.metadata import version
|
|
23
|
-
import numpy as np
|
|
24
|
-
from scipy import signal
|
|
25
|
-
from scipy.ndimage import gaussian_filter1d
|
|
26
|
-
from statsmodels.nonparametric.smoothers_lowess import lowess
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
## AUTHORSHIP INFORMATION
|
|
30
|
-
__author__ = "David Pagnon"
|
|
31
|
-
__copyright__ = "Copyright 2021, Pose2Sim"
|
|
32
|
-
__credits__ = ["David Pagnon"]
|
|
33
|
-
__license__ = "BSD 3-Clause License"
|
|
34
|
-
__version__ = version("sports2d")
|
|
35
|
-
__maintainer__ = "David Pagnon"
|
|
36
|
-
__email__ = "contact@david-pagnon.com"
|
|
37
|
-
__status__ = "Development"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
## FUNCTIONS
|
|
42
|
-
def butterworth_filter_1d(col, args):
|
|
43
|
-
'''
|
|
44
|
-
1D Zero-phase Butterworth filter (dual pass)
|
|
45
|
-
Deals with nans
|
|
46
|
-
|
|
47
|
-
INPUT:
|
|
48
|
-
- col: numpy array
|
|
49
|
-
- order: int
|
|
50
|
-
- cutoff: int
|
|
51
|
-
- framerate: int
|
|
52
|
-
|
|
53
|
-
OUTPUT
|
|
54
|
-
- col_filtered: Filtered pandas dataframe column
|
|
55
|
-
'''
|
|
56
|
-
|
|
57
|
-
order, cutoff, framerate = args
|
|
58
|
-
|
|
59
|
-
# Filter
|
|
60
|
-
b, a = signal.butter(order/2, cutoff/(framerate/2), 'low', analog = False)
|
|
61
|
-
padlen = 3 * max(len(a), len(b))
|
|
62
|
-
|
|
63
|
-
# split into sequences of not nans
|
|
64
|
-
col_filtered = col.copy()
|
|
65
|
-
mask = np.isnan(col_filtered) | col_filtered.eq(0)
|
|
66
|
-
falsemask_indices = np.where(~mask)[0]
|
|
67
|
-
gaps = np.where(np.diff(falsemask_indices) > 1)[0] + 1
|
|
68
|
-
idx_sequences = np.split(falsemask_indices, gaps)
|
|
69
|
-
if idx_sequences[0].size > 0:
|
|
70
|
-
idx_sequences_to_filter = [seq for seq in idx_sequences if len(seq) > padlen]
|
|
71
|
-
|
|
72
|
-
# Filter each of the selected sequences
|
|
73
|
-
for seq_f in idx_sequences_to_filter:
|
|
74
|
-
col_filtered[seq_f] = signal.filtfilt(b, a, col_filtered[seq_f])
|
|
75
|
-
|
|
76
|
-
return col_filtered
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def gaussian_filter_1d(col, kernel):
|
|
80
|
-
'''
|
|
81
|
-
1D Gaussian filter
|
|
82
|
-
|
|
83
|
-
INPUT:
|
|
84
|
-
- col: numpy array
|
|
85
|
-
- kernel: Sigma kernel value (int)
|
|
86
|
-
|
|
87
|
-
OUTPUT
|
|
88
|
-
- col_filtered: Filtered pandas dataframe column
|
|
89
|
-
'''
|
|
90
|
-
|
|
91
|
-
col_filtered = gaussian_filter1d(col, kernel)
|
|
92
|
-
|
|
93
|
-
return col_filtered
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def loess_filter_1d(col, kernel):
|
|
97
|
-
'''
|
|
98
|
-
1D LOWESS filter (Locally Weighted Scatterplot Smoothing)
|
|
99
|
-
|
|
100
|
-
INPUT:
|
|
101
|
-
- col: numpy array
|
|
102
|
-
- kernel: Kernel value: window length used for smoothing (int)
|
|
103
|
-
NB: frac = kernel / frames_number
|
|
104
|
-
|
|
105
|
-
OUTPUT
|
|
106
|
-
- col_filtered: Filtered pandas dataframe column
|
|
107
|
-
'''
|
|
108
|
-
|
|
109
|
-
# split into sequences of not nans
|
|
110
|
-
col_filtered = col.copy()
|
|
111
|
-
mask = np.isnan(col_filtered)
|
|
112
|
-
falsemask_indices = np.where(~mask)[0]
|
|
113
|
-
gaps = np.where(np.diff(falsemask_indices) > 1)[0] + 1
|
|
114
|
-
idx_sequences = np.split(falsemask_indices, gaps)
|
|
115
|
-
if idx_sequences[0].size > 0:
|
|
116
|
-
idx_sequences_to_filter = [seq for seq in idx_sequences if len(seq) > kernel]
|
|
117
|
-
|
|
118
|
-
# Filter each of the selected sequences
|
|
119
|
-
for seq_f in idx_sequences_to_filter:
|
|
120
|
-
col_filtered[seq_f] = lowess(col_filtered[seq_f], seq_f, is_sorted=True, frac=kernel/len(seq_f), it=0)[:,1]
|
|
121
|
-
|
|
122
|
-
return col_filtered
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
def median_filter_1d(col, kernel):
|
|
126
|
-
'''
|
|
127
|
-
1D median filter
|
|
128
|
-
|
|
129
|
-
INPUT:
|
|
130
|
-
- col: numpy array
|
|
131
|
-
- kernel: window size (int)
|
|
132
|
-
|
|
133
|
-
OUTPUT
|
|
134
|
-
- col_filtered: Filtered pandas dataframe column
|
|
135
|
-
'''
|
|
136
|
-
|
|
137
|
-
col_filtered = signal.medfilt(col, kernel_size=kernel)
|
|
138
|
-
|
|
139
|
-
return col_filtered
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def filter1d(col, *filter_options):
|
|
143
|
-
'''
|
|
144
|
-
Choose filter type and filter column
|
|
145
|
-
|
|
146
|
-
INPUT:
|
|
147
|
-
- col: Pandas dataframe column
|
|
148
|
-
- filter_options = (do_filter, filter_type, butterworth_filter_order, butterworth_filter_cutoff, frame_rate, gaussian_filter_kernel, loess_filter_kernel, median_filter_kernel)
|
|
149
|
-
|
|
150
|
-
OUTPUT
|
|
151
|
-
- col_filtered: Filtered pandas dataframe column
|
|
152
|
-
'''
|
|
153
|
-
|
|
154
|
-
filter_type = filter_options[1]
|
|
155
|
-
if filter_type == 'butterworth':
|
|
156
|
-
args = (filter_options[2], filter_options[3], filter_options[4])
|
|
157
|
-
if filter_type == 'gaussian':
|
|
158
|
-
args = (filter_options[5])
|
|
159
|
-
if filter_type == 'loess':
|
|
160
|
-
args = (filter_options[6])
|
|
161
|
-
if filter_type == 'median':
|
|
162
|
-
args = (filter_options[7])
|
|
163
|
-
|
|
164
|
-
# Choose filter
|
|
165
|
-
filter_mapping = {
|
|
166
|
-
'butterworth': butterworth_filter_1d,
|
|
167
|
-
'gaussian': gaussian_filter_1d,
|
|
168
|
-
'loess': loess_filter_1d,
|
|
169
|
-
'median': median_filter_1d
|
|
170
|
-
}
|
|
171
|
-
filter_fun = filter_mapping[filter_type]
|
|
172
|
-
|
|
173
|
-
# Filter column
|
|
174
|
-
col_filtered = filter_fun(col, args)
|
|
175
|
-
|
|
176
|
-
return col_filtered
|
|
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
|
|
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
|