sports2d 0.8.16__py3-none-any.whl → 0.8.18__py3-none-any.whl
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/Demo/Config_demo.toml +27 -8
- Sports2D/Sports2D.py +21 -10
- Sports2D/process.py +102 -45
- {sports2d-0.8.16.dist-info → sports2d-0.8.18.dist-info}/METADATA +12 -4
- {sports2d-0.8.16.dist-info → sports2d-0.8.18.dist-info}/RECORD +9 -10
- Sports2D/Utilities/filter.py +0 -176
- {sports2d-0.8.16.dist-info → sports2d-0.8.18.dist-info}/WHEEL +0 -0
- {sports2d-0.8.16.dist-info → sports2d-0.8.18.dist-info}/entry_points.txt +0 -0
- {sports2d-0.8.16.dist-info → sports2d-0.8.18.dist-info}/licenses/LICENSE +0 -0
- {sports2d-0.8.16.dist-info → sports2d-0.8.18.dist-info}/top_level.txt +0 -0
Sports2D/Demo/Config_demo.toml
CHANGED
|
@@ -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,44 @@ 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, kalman, gcv_spline, gaussian, loess, median, butterworth_on_speed
|
|
143
|
+
|
|
144
|
+
# Most intuitive and standard filter in biomechanics
|
|
141
145
|
[post-processing.butterworth]
|
|
142
|
-
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
|
+
order = 4
|
|
148
|
+
|
|
149
|
+
# Used in countless applications, this one is a simplified Kalman filter
|
|
150
|
+
[post-processing.kalman]
|
|
151
|
+
# How much more do you trust triangulation results (measurements), than the assumption of constant acceleration(process)?
|
|
152
|
+
trust_ratio = 500 # = measurement_trust/process_trust ~= process_noise/measurement_noise
|
|
153
|
+
smooth = true # should be true, unless you need real-time filtering
|
|
154
|
+
|
|
155
|
+
# Automatically determines optimal parameters for each point, which is good when some move faster than others (eg fingers vs hips).
|
|
156
|
+
[post-processing.gcv_spline]
|
|
157
|
+
gcv_cut_off_frequency = 'auto' # 'auto' or int # If int, behaves like a Butterworth filter. 'auto' is usually better, unless the signal is too short (noise can then be considered as signal -> trajectories not filtered)
|
|
158
|
+
gcv_smoothing_factor = 0.1 # >=0, ignored if cut_off_frequency != 'auto'. Biases results towards more smoothing (>1) or more fidelity to data (<1)
|
|
159
|
+
|
|
146
160
|
[post-processing.loess]
|
|
147
161
|
nb_values_used = 5 # = fraction of data used * nb frames
|
|
162
|
+
[post-processing.gaussian]
|
|
163
|
+
sigma_kernel = 1 #px
|
|
148
164
|
[post-processing.median]
|
|
149
165
|
kernel_size = 3
|
|
166
|
+
[post-processing.butterworth_on_speed]
|
|
167
|
+
order = 4
|
|
168
|
+
cut_off_frequency = 10 # Hz
|
|
150
169
|
|
|
151
170
|
|
|
152
171
|
[kinematics]
|
|
153
172
|
do_ik = false # Do scaling and inverse kinematics?
|
|
154
173
|
use_augmentation = false # true or false (lowercase) # Set to true if you want to use the model with augmented markers
|
|
155
174
|
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 #
|
|
175
|
+
use_simple_model = false # true or false # IK 10+ times faster, but no muscles or flexible spine, no patella
|
|
157
176
|
participant_mass = [55.0, 67.0] # kg # defaults to 70 if not provided. No influence on kinematics (motion), only on kinetics (forces)
|
|
158
177
|
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
178
|
|
Sports2D/Sports2D.py
CHANGED
|
@@ -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', 'gcv_smoothing_factor': 1.0},
|
|
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"],
|
|
330
|
+
'cut_off_frequency': ["", "cut-off frequency of the Butterworth filter. 6 if not specified"],
|
|
325
331
|
'order': ["", "order of the Butterworth filter. 4 if not specified"],
|
|
326
|
-
'
|
|
332
|
+
'gcv_cut_off_frequency': ["", "cut-off frequency of the GCV spline filter. 'auto' is usually better, unless the signal is too short (noise can then be considered as signal -> trajectories not filtered). 'auto' if not specified"],
|
|
333
|
+
'gcv_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). 1.0 if not specified"],
|
|
334
|
+
'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"],
|
|
335
|
+
'smooth': ["", "dual Kalman smoothing. true 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"],
|
Sports2D/process.py
CHANGED
|
@@ -79,11 +79,17 @@ 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
|
+
|
|
88
|
+
# Not safe, but to be used until OpenMMLab/RTMlib's SSL certificates are updated
|
|
89
|
+
import ssl
|
|
90
|
+
ssl._create_default_https_context = ssl._create_unverified_context
|
|
91
|
+
|
|
92
|
+
|
|
87
93
|
|
|
88
94
|
DEFAULT_MASS = 70
|
|
89
95
|
DEFAULT_HEIGHT = 1.7
|
|
@@ -196,7 +202,7 @@ def setup_video(video_file_path, save_vid, vid_output_path):
|
|
|
196
202
|
|
|
197
203
|
def setup_model_class_mode(pose_model, mode, config_dict={}):
|
|
198
204
|
'''
|
|
199
|
-
|
|
205
|
+
Set up the pose model class and mode for the pose tracker.
|
|
200
206
|
'''
|
|
201
207
|
|
|
202
208
|
if pose_model.upper() in ('HALPE_26', 'BODY_WITH_FEET'):
|
|
@@ -258,6 +264,10 @@ def setup_model_class_mode(pose_model, mode, config_dict={}):
|
|
|
258
264
|
det_class=det_class, det=det, det_input_size=det_input_size,
|
|
259
265
|
pose_class=pose_class, pose=pose, pose_input_size=pose_input_size)
|
|
260
266
|
logging.info(f"Using model {model_name} with the following custom parameters: {mode}.")
|
|
267
|
+
|
|
268
|
+
if pose_class == 'RTMO' and model_name != 'COCO_17':
|
|
269
|
+
logging.warning("RTMO currently only supports 'Body' pose_model. Switching to 'Body'.")
|
|
270
|
+
pose_model = eval('COCO_17')
|
|
261
271
|
|
|
262
272
|
except (json.JSONDecodeError, TypeError):
|
|
263
273
|
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 +1446,21 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1436
1446
|
sections_to_keep = config_dict.get('post-processing').get('sections_to_keep')
|
|
1437
1447
|
|
|
1438
1448
|
do_filter = config_dict.get('post-processing').get('filter')
|
|
1449
|
+
handle_LR_swap = config_dict.get('post-processing').get('handle_LR_swap', False)
|
|
1450
|
+
reject_outliers = config_dict.get('post-processing').get('reject_outliers', False)
|
|
1439
1451
|
show_plots = config_dict.get('post-processing').get('show_graphs')
|
|
1440
1452
|
filter_type = config_dict.get('post-processing').get('filter_type')
|
|
1441
|
-
butterworth_filter_order = config_dict.get('post-processing').get('butterworth').get('order')
|
|
1442
|
-
butterworth_filter_cutoff = config_dict.get('post-processing').get('butterworth').get('cut_off_frequency')
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1453
|
+
butterworth_filter_order = config_dict.get('post-processing').get('butterworth', {}).get('order')
|
|
1454
|
+
butterworth_filter_cutoff = config_dict.get('post-processing').get('butterworth', {}).get('cut_off_frequency')
|
|
1455
|
+
gcv_filter_cutoff = config_dict.get('post-processing').get('gcv_spline', {}).get('gcv_cut_off_frequency')
|
|
1456
|
+
gcv_smoothing_factor = config_dict.get('post-processing').get('gcv_spline', {}).get('gcv_smoothing_factor')
|
|
1457
|
+
kalman_filter_trust_ratio = config_dict.get('post-processing').get('kalman', {}).get('trust_ratio')
|
|
1458
|
+
kalman_filter_smooth = config_dict.get('post-processing').get('kalman', {}).get('smooth')
|
|
1459
|
+
gaussian_filter_kernel = config_dict.get('post-processing').get('gaussian', {}).get('sigma_kernel')
|
|
1460
|
+
loess_filter_kernel = config_dict.get('post-processing').get('loess', {}).get('nb_values_used')
|
|
1461
|
+
median_filter_kernel = config_dict.get('post-processing').get('median', {}).get('kernel_size')
|
|
1462
|
+
butterworthspeed_filter_order = config_dict.get('post-processing').get('butterworth_on_speed', {}).get('order')
|
|
1463
|
+
butterworthspeed_filter_cutoff = config_dict.get('post-processing').get('butterworth_on_speed', {}).get('cut_off_frequency')
|
|
1449
1464
|
|
|
1450
1465
|
# Create output directories
|
|
1451
1466
|
if video_file == "webcam":
|
|
@@ -1476,7 +1491,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1476
1491
|
trimmed_extrema_percent = config_dict.get('kinematics').get('trimmed_extrema_percent')
|
|
1477
1492
|
close_to_zero_speed_px = config_dict.get('kinematics').get('close_to_zero_speed_px')
|
|
1478
1493
|
close_to_zero_speed_m = config_dict.get('kinematics').get('close_to_zero_speed_m')
|
|
1479
|
-
if do_ik or use_augmentation:
|
|
1494
|
+
if do_ik or use_augmentation or do_filter:
|
|
1480
1495
|
try:
|
|
1481
1496
|
if use_augmentation:
|
|
1482
1497
|
from Pose2Sim.markerAugmentation import augment_markers_all
|
|
@@ -1498,6 +1513,23 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1498
1513
|
pose3d_dir.mkdir(parents=True, exist_ok=True)
|
|
1499
1514
|
kinematics_dir = Path(output_dir) / 'kinematics'
|
|
1500
1515
|
kinematics_dir.mkdir(parents=True, exist_ok=True)
|
|
1516
|
+
|
|
1517
|
+
if do_filter:
|
|
1518
|
+
Pose2Sim_config_dict['personAssociation']['handle_LR_swap'] = handle_LR_swap
|
|
1519
|
+
Pose2Sim_config_dict['filtering']['reject_outliers'] = reject_outliers
|
|
1520
|
+
Pose2Sim_config_dict['filtering']['filter'] = do_filter
|
|
1521
|
+
Pose2Sim_config_dict['filtering']['type'] = filter_type
|
|
1522
|
+
Pose2Sim_config_dict['filtering']['gcv_spline']['cut_off_frequency'] = gcv_filter_cutoff
|
|
1523
|
+
Pose2Sim_config_dict['filtering']['gcv_spline']['smoothing_factor'] = gcv_smoothing_factor
|
|
1524
|
+
Pose2Sim_config_dict['filtering']['butterworth']['cut_off_frequency'] = butterworth_filter_cutoff
|
|
1525
|
+
Pose2Sim_config_dict['filtering']['butterworth']['order'] = butterworth_filter_order
|
|
1526
|
+
Pose2Sim_config_dict['filtering']['kalman']['trust_ratio'] = kalman_filter_trust_ratio
|
|
1527
|
+
Pose2Sim_config_dict['filtering']['kalman']['smooth'] = kalman_filter_smooth
|
|
1528
|
+
Pose2Sim_config_dict['filtering']['gaussian']['sigma_kernel'] = gaussian_filter_kernel
|
|
1529
|
+
Pose2Sim_config_dict['filtering']['loess']['nb_values_used'] = loess_filter_kernel
|
|
1530
|
+
Pose2Sim_config_dict['filtering']['median']['kernel_size'] = median_filter_kernel
|
|
1531
|
+
Pose2Sim_config_dict['filtering']['butterworth_on_speed']['order'] = butterworthspeed_filter_order
|
|
1532
|
+
Pose2Sim_config_dict['filtering']['butterworth_on_speed']['cut_off_frequency'] = butterworthspeed_filter_cutoff
|
|
1501
1533
|
|
|
1502
1534
|
|
|
1503
1535
|
# Set up video capture
|
|
@@ -1570,8 +1602,8 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1570
1602
|
# if tracking_mode not in ['deepsort', 'sports2d']:
|
|
1571
1603
|
# logging.warning(f"Tracking mode {tracking_mode} not recognized. Using sports2d method.")
|
|
1572
1604
|
# tracking_mode = 'sports2d'
|
|
1573
|
-
logging.info(f'Pose tracking set up for "{pose_model_name}" model.')
|
|
1574
|
-
logging.info(f'Mode: {mode}.\n')
|
|
1605
|
+
# logging.info(f'Pose tracking set up for "{pose_model_name}" model.')
|
|
1606
|
+
# logging.info(f'Mode: {mode}.\n')
|
|
1575
1607
|
logging.info(f'Persons are detected every {det_frequency} frames and tracked inbetween. Tracking is done with {tracking_mode}.')
|
|
1576
1608
|
if tracking_mode == 'deepsort': logging.info(f'Deepsort parameters: {deepsort_params}.')
|
|
1577
1609
|
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 +1625,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1593
1625
|
ang_params = angle_dict.get(ang_name)
|
|
1594
1626
|
kpts = ang_params[0]
|
|
1595
1627
|
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]}.")
|
|
1628
|
+
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
1629
|
|
|
1598
1630
|
|
|
1599
1631
|
#%% ==================================================
|
|
@@ -1724,7 +1756,6 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1724
1756
|
img = draw_keypts(img, valid_X, valid_Y, valid_scores, cmap_str='RdYlGn')
|
|
1725
1757
|
img = draw_skel(img, valid_X, valid_Y, pose_model)
|
|
1726
1758
|
if calculate_angles:
|
|
1727
|
-
|
|
1728
1759
|
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
1760
|
cv2.imshow(f'{video_file} Sports2D', img)
|
|
1730
1761
|
if (cv2.waitKey(1) & 0xFF) == ord('q') or (cv2.waitKey(1) & 0xFF) == 27:
|
|
@@ -1859,31 +1890,45 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1859
1890
|
all_frames_Y_person_interp.replace(np.nan, 0, inplace=True)
|
|
1860
1891
|
|
|
1861
1892
|
# Filter
|
|
1862
|
-
if
|
|
1893
|
+
if reject_outliers:
|
|
1894
|
+
logging.info('Rejecting outliers with Hampel filter.')
|
|
1895
|
+
all_frames_X_person_interp = all_frames_X_person_interp.apply(hampel_filter, axis=0, args = [round(7*frame_rate/30), 2])
|
|
1896
|
+
all_frames_Y_person_interp = all_frames_Y_person_interp.apply(hampel_filter, axis=0, args = [round(7*frame_rate/30), 2])
|
|
1897
|
+
|
|
1898
|
+
if not do_filter:
|
|
1863
1899
|
logging.info(f'No filtering.')
|
|
1864
1900
|
all_frames_X_person_filt = all_frames_X_person_interp
|
|
1865
1901
|
all_frames_Y_person_filt = all_frames_Y_person_interp
|
|
1866
1902
|
else:
|
|
1867
|
-
filter_type
|
|
1868
|
-
|
|
1869
|
-
cutoff = filter_options[3]
|
|
1903
|
+
if filter_type == ('butterworth' or 'butterworth_on_speed'):
|
|
1904
|
+
cutoff = butterworth_filter_cutoff
|
|
1870
1905
|
if video_file == 'webcam':
|
|
1871
1906
|
if cutoff / (fps / 2) >= 1:
|
|
1872
1907
|
cutoff_old = cutoff
|
|
1873
1908
|
cutoff = fps/(2+0.001)
|
|
1874
1909
|
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
|
-
|
|
1910
|
+
butterworth_filter_cutoff = cutoff
|
|
1911
|
+
filt_type = 'Butterworth' if filter_type == 'butterworth' else 'Butterworth on speed'
|
|
1912
|
+
args = f'{filt_type} filter, {butterworth_filter_order}th order, {butterworth_filter_cutoff} Hz.'
|
|
1913
|
+
frame_rate = fps
|
|
1914
|
+
elif filter_type == 'gcv_spline':
|
|
1915
|
+
args = f'GVC Spline filter, which automatically evaluates the best trade-off between smoothness and fidelity to data.'
|
|
1916
|
+
elif filter_type == 'kalman':
|
|
1917
|
+
args = f'Kalman filter, trusting measurement {kalman_filter_trust_ratio} times more than the process matrix.'
|
|
1918
|
+
elif filter_type == 'gaussian':
|
|
1919
|
+
args = f'Gaussian filter, Sigma kernel {gaussian_filter_kernel}.'
|
|
1920
|
+
elif filter_type == 'loess':
|
|
1921
|
+
args = f'LOESS filter, window size of {loess_filter_kernel} frames.'
|
|
1922
|
+
elif filter_type == 'median':
|
|
1923
|
+
args = f'Median filter, kernel of {median_filter_kernel}.'
|
|
1924
|
+
else:
|
|
1925
|
+
logging.error(f"Invalid filter_type: {filter_type}. Must be 'butterworth', 'gcv_spline', 'kalman', 'gaussian', 'loess', or 'median'.")
|
|
1926
|
+
raise ValueError(f"Invalid filter_type: {filter_type}. Must be 'butterworth', 'gcv_spline', 'kalman', 'gaussian', 'loess', or 'median'.")
|
|
1927
|
+
|
|
1884
1928
|
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(
|
|
1929
|
+
all_frames_X_person_filt = all_frames_X_person_interp.apply(filter1d, axis=0, args = [Pose2Sim_config_dict, filter_type, frame_rate])
|
|
1930
|
+
all_frames_Y_person_filt = all_frames_Y_person_interp.apply(filter1d, axis=0, args = [Pose2Sim_config_dict, filter_type, frame_rate])
|
|
1931
|
+
|
|
1887
1932
|
|
|
1888
1933
|
# Build TRC file
|
|
1889
1934
|
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 +2139,41 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
2094
2139
|
all_frames_angles_person_interp.replace(np.nan, 0, inplace=True)
|
|
2095
2140
|
|
|
2096
2141
|
# Filter
|
|
2097
|
-
if
|
|
2142
|
+
if reject_outliers:
|
|
2143
|
+
logging.info(f'Rejecting outliers with Hampel filter.')
|
|
2144
|
+
all_frames_angles_person_interp = all_frames_angles_person_interp.apply(hampel_filter, axis=0)
|
|
2145
|
+
|
|
2146
|
+
if not do_filter:
|
|
2098
2147
|
logging.info(f'No filtering.')
|
|
2099
2148
|
all_frames_angles_person_filt = all_frames_angles_person_interp
|
|
2100
2149
|
else:
|
|
2101
|
-
filter_type
|
|
2102
|
-
|
|
2103
|
-
cutoff = filter_options[3]
|
|
2150
|
+
if filter_type == ('butterworth' or 'butterworth_on_speed'):
|
|
2151
|
+
cutoff = butterworth_filter_cutoff
|
|
2104
2152
|
if video_file == 'webcam':
|
|
2105
2153
|
if cutoff / (fps / 2) >= 1:
|
|
2106
2154
|
cutoff_old = cutoff
|
|
2107
2155
|
cutoff = fps/(2+0.001)
|
|
2108
2156
|
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
|
-
|
|
2157
|
+
butterworth_filter_cutoff = cutoff
|
|
2158
|
+
filt_type = 'Butterworth' if filter_type == 'butterworth' else 'Butterworth on speed'
|
|
2159
|
+
args = f'{filt_type} filter, {butterworth_filter_order}th order, {butterworth_filter_cutoff} Hz.'
|
|
2160
|
+
frame_rate = fps
|
|
2161
|
+
elif filter_type == 'gcv_spline':
|
|
2162
|
+
args = f'GVC Spline filter, which automatically evaluates the best trade-off between smoothness and fidelity to data.'
|
|
2163
|
+
elif filter_type == 'kalman':
|
|
2164
|
+
args = f'Kalman filter, trusting measurement {kalman_filter_trust_ratio} times more than the process matrix.'
|
|
2165
|
+
elif filter_type == 'gaussian':
|
|
2166
|
+
args = f'Gaussian filter, Sigma kernel {gaussian_filter_kernel}.'
|
|
2167
|
+
elif filter_type == 'loess':
|
|
2168
|
+
args = f'LOESS filter, window size of {loess_filter_kernel} frames.'
|
|
2169
|
+
elif filter_type == 'median':
|
|
2170
|
+
args = f'Median filter, kernel of {median_filter_kernel}.'
|
|
2171
|
+
else:
|
|
2172
|
+
logging.error(f"Invalid filter_type: {filter_type}. Must be 'butterworth', 'gcv_spline', 'kalman', 'gaussian', 'loess', or 'median'.")
|
|
2173
|
+
raise ValueError(f"Invalid filter_type: {filter_type}. Must be 'butterworth', 'gcv_spline', 'kalman', 'gaussian', 'loess', or 'median'.")
|
|
2174
|
+
|
|
2175
|
+
logging.info(f'Filtering with {args}.')
|
|
2176
|
+
all_frames_angles_person_filt = all_frames_angles_person_interp.apply(filter1d, axis=0, args = [Pose2Sim_config_dict, filter_type, frame_rate])
|
|
2120
2177
|
|
|
2121
2178
|
# Add floor_angle_estim to segment angles
|
|
2122
2179
|
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.18
|
|
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
|
|
655
|
+
6. **Interpolates and filters results:** Missing pose and angle sequences are interpolated unless gaps are too large. Outliers are rejected with a Hampel filter. Results are filtered with a 6 Hz Butterworth filter. Many other filters are available, and all of the above can be configured or deactivated (see [Config_Demo.toml](https://github.com/davidpagnon/Sports2D/blob/main/Sports2D/Demo/Config_demo.toml))
|
|
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\
|
|
@@ -9,18 +9,17 @@ Content/paper.md,sha256=8rWSOLrKTysloZv0Fz2lr3nayxtHi7GlFMqwdgDVggY,11333
|
|
|
9
9
|
Content/sports2d_blender.gif,sha256=wgMuPRxhja3XtQn76_okGXsNnUT9Thp0pnD36GdW5_E,448786
|
|
10
10
|
Content/sports2d_opensim.gif,sha256=XP1AcjqhbGcJknXUoNJjPWAwaM9ahZafbDgLWvzKJs4,376656
|
|
11
11
|
Sports2D/Sports2D.ipynb,sha256=VnOVjIl6ndnCJTT13L4W5qTw4T-TQDF3jt3-wxnXDqM,2427047
|
|
12
|
-
Sports2D/Sports2D.py,sha256=
|
|
12
|
+
Sports2D/Sports2D.py,sha256=3Mcc_jFaD5Zv4ArB-jKYhgpMlFT0XBifTlSe70volzk,35385
|
|
13
13
|
Sports2D/__init__.py,sha256=BuUkPEdItxlkeqz4dmoiPwZLkgAfABJK3KWQ1ujTGwE,153
|
|
14
|
-
Sports2D/process.py,sha256=
|
|
15
|
-
Sports2D/Demo/Config_demo.toml,sha256=
|
|
14
|
+
Sports2D/process.py,sha256=bDKhKftfDQucuwnVnoXrtHYrMe8qrOP33B6P1wy2wLE,120632
|
|
15
|
+
Sports2D/Demo/Config_demo.toml,sha256=YescEgeQq3ojGqEAFWgXN142HL-YaVcRty9LbJgScoM,15577
|
|
16
16
|
Sports2D/Demo/demo.mp4,sha256=2aZkFxhWR7ESMEtXCT8MGA83p2jmoU2sp1ylQfO3gDk,3968304
|
|
17
17
|
Sports2D/Utilities/__init__.py,sha256=BuUkPEdItxlkeqz4dmoiPwZLkgAfABJK3KWQ1ujTGwE,153
|
|
18
18
|
Sports2D/Utilities/common.py,sha256=idMRmesFv5BPX-5g3z5dOVa7SpS_8tNgijvGrOZlR-k,11185
|
|
19
|
-
Sports2D/Utilities/filter.py,sha256=rfZcqofjllKI_5ovZTKEAmyjOZpB_PzbAJ0P874T8Ak,4973
|
|
20
19
|
Sports2D/Utilities/tests.py,sha256=Ec504-4iuAvw5TDNT7upyoPRcs09EIe4Dteph3ybFJA,4702
|
|
21
|
-
sports2d-0.8.
|
|
22
|
-
sports2d-0.8.
|
|
23
|
-
sports2d-0.8.
|
|
24
|
-
sports2d-0.8.
|
|
25
|
-
sports2d-0.8.
|
|
26
|
-
sports2d-0.8.
|
|
20
|
+
sports2d-0.8.18.dist-info/licenses/LICENSE,sha256=f4qe3nE0Y7ltJho5w-xAR0jI5PUox5Xl-MsYiY7ZRM8,1521
|
|
21
|
+
sports2d-0.8.18.dist-info/METADATA,sha256=1X_RdFwHijFHWjOpFrmXkGTAnqCzVXnlAl-ZHnosNNQ,40492
|
|
22
|
+
sports2d-0.8.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
23
|
+
sports2d-0.8.18.dist-info/entry_points.txt,sha256=V8dFDIXatz9VvoGgoHzb2wE71C9-f85K6_OjnEQlxww,108
|
|
24
|
+
sports2d-0.8.18.dist-info/top_level.txt,sha256=cWWBiDD2WbQXMoIoN6-9et9U2t2c_ZKo2JtBqO5uN-k,17
|
|
25
|
+
sports2d-0.8.18.dist-info/RECORD,,
|
Sports2D/Utilities/filter.py
DELETED
|
@@ -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
|