sports2d 0.8.13__tar.gz → 0.8.15__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.13 → sports2d-0.8.15}/PKG-INFO +14 -3
- {sports2d-0.8.13 → sports2d-0.8.15}/README.md +13 -2
- {sports2d-0.8.13 → sports2d-0.8.15}/Sports2D/Demo/Config_demo.toml +2 -1
- {sports2d-0.8.13 → sports2d-0.8.15}/Sports2D/Sports2D.py +2 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Sports2D/process.py +45 -16
- {sports2d-0.8.13 → sports2d-0.8.15}/sports2d.egg-info/PKG-INFO +14 -3
- {sports2d-0.8.13 → sports2d-0.8.15}/.github/workflows/continuous-integration.yml +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/.github/workflows/joss_pdf.yml +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/.github/workflows/publish-on-release.yml +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/.gitignore +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/CITATION.cff +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Content/Demo_plots.png +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Content/Demo_results.png +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Content/Demo_terminal.png +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Content/Person_selection.png +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Content/Video_tuto_Sports2D_Colab.png +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Content/joint_convention.png +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Content/paper.bib +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Content/paper.md +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Content/sports2d_blender.gif +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Content/sports2d_opensim.gif +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/LICENSE +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Sports2D/Demo/demo.mp4 +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Sports2D/Sports2D.ipynb +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Sports2D/Utilities/__init__.py +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Sports2D/Utilities/common.py +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Sports2D/Utilities/filter.py +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Sports2D/Utilities/tests.py +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/Sports2D/__init__.py +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/pyproject.toml +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/setup.cfg +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/sports2d.egg-info/SOURCES.txt +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/sports2d.egg-info/dependency_links.txt +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/sports2d.egg-info/entry_points.txt +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/sports2d.egg-info/requires.txt +0 -0
- {sports2d-0.8.13 → sports2d-0.8.15}/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.15
|
|
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>
|
|
@@ -402,6 +402,17 @@ sports2d --video_input demo.mp4 other_video.mp4 --time_range 1.2 2.7 0 3.5
|
|
|
402
402
|
```cmd
|
|
403
403
|
sports2d --calculate_angles false
|
|
404
404
|
```
|
|
405
|
+
- Flip angles when the person faces the other side.\
|
|
406
|
+
**N.B.:** *We consider that the person looks to the right if their toe keypoint is to the right of their heel. This is not always true when the person is sprinting, especially in the swing phase. Set it to false if you want timeseries to be continuous even when the participant switches their stance.*
|
|
407
|
+
```cmd
|
|
408
|
+
sports2d --flip_left_right true # Default
|
|
409
|
+
```
|
|
410
|
+
- Correct segment angles according to the estimated camera tild angle.\
|
|
411
|
+
**N.B.:** *The camera tilt angle is automatically estimated. Set to false if it is actually the floor which is tilted rather than the camera.*
|
|
412
|
+
```cmd
|
|
413
|
+
sports2d --correct_segment_angles_with_floor_angle true # Default
|
|
414
|
+
```
|
|
415
|
+
|
|
405
416
|
- To run **inverse kinematics with OpenSim**, check [this section](#run-inverse-kinematics)
|
|
406
417
|
|
|
407
418
|
<br>
|
|
@@ -505,8 +516,8 @@ sports2d --help
|
|
|
505
516
|
'large_hip_knee_angles': ["", "Hip and knee angles below this value are considered as imprecise. Defaults to 45"],
|
|
506
517
|
'trimmed_extrema_percent': ["", "Proportion of the most extreme segment values to remove before calculating their mean. Defaults to 50"],
|
|
507
518
|
'fontSize': ["", "font size for angle values. 0.3 if not specified"],
|
|
508
|
-
'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
|
|
509
|
-
'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
|
|
519
|
+
'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"],
|
|
520
|
+
'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"],
|
|
510
521
|
'interpolate': ["", "interpolate missing data. true if not specified"],
|
|
511
522
|
'interp_gap_smaller_than': ["", "interpolate sequences of missing data if they are less than N frames long. 10 if not specified"],
|
|
512
523
|
'fill_large_gaps_with': ["", "last_value, nan, or zeros. last_value if not specified"],
|
|
@@ -359,6 +359,17 @@ sports2d --video_input demo.mp4 other_video.mp4 --time_range 1.2 2.7 0 3.5
|
|
|
359
359
|
```cmd
|
|
360
360
|
sports2d --calculate_angles false
|
|
361
361
|
```
|
|
362
|
+
- Flip angles when the person faces the other side.\
|
|
363
|
+
**N.B.:** *We consider that the person looks to the right if their toe keypoint is to the right of their heel. This is not always true when the person is sprinting, especially in the swing phase. Set it to false if you want timeseries to be continuous even when the participant switches their stance.*
|
|
364
|
+
```cmd
|
|
365
|
+
sports2d --flip_left_right true # Default
|
|
366
|
+
```
|
|
367
|
+
- Correct segment angles according to the estimated camera tild angle.\
|
|
368
|
+
**N.B.:** *The camera tilt angle is automatically estimated. Set to false if it is actually the floor which is tilted rather than the camera.*
|
|
369
|
+
```cmd
|
|
370
|
+
sports2d --correct_segment_angles_with_floor_angle true # Default
|
|
371
|
+
```
|
|
372
|
+
|
|
362
373
|
- To run **inverse kinematics with OpenSim**, check [this section](#run-inverse-kinematics)
|
|
363
374
|
|
|
364
375
|
<br>
|
|
@@ -462,8 +473,8 @@ sports2d --help
|
|
|
462
473
|
'large_hip_knee_angles': ["", "Hip and knee angles below this value are considered as imprecise. Defaults to 45"],
|
|
463
474
|
'trimmed_extrema_percent': ["", "Proportion of the most extreme segment values to remove before calculating their mean. Defaults to 50"],
|
|
464
475
|
'fontSize': ["", "font size for angle values. 0.3 if not specified"],
|
|
465
|
-
'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
|
|
466
|
-
'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
|
|
476
|
+
'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"],
|
|
477
|
+
'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"],
|
|
467
478
|
'interpolate': ["", "interpolate missing data. true if not specified"],
|
|
468
479
|
'interp_gap_smaller_than': ["", "interpolate sequences of missing data if they are less than N frames long. 10 if not specified"],
|
|
469
480
|
'fill_large_gaps_with': ["", "last_value, nan, or zeros. last_value if not specified"],
|
|
@@ -133,7 +133,8 @@ correct_segment_angles_with_floor_angle = true # If the camera is tilted, correc
|
|
|
133
133
|
interpolate = true
|
|
134
134
|
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
|
+
# 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
|
|
137
138
|
filter = true
|
|
138
139
|
show_graphs = true # Show plots of raw and processed results
|
|
139
140
|
filter_type = 'butterworth' # butterworth, gaussian, LOESS, median
|
|
@@ -232,6 +232,7 @@ DEFAULT_CONFIG = {'base': {'video_input': ['demo.mp4'],
|
|
|
232
232
|
'post-processing': {'interpolate': True,
|
|
233
233
|
'interp_gap_smaller_than': 10,
|
|
234
234
|
'fill_large_gaps_with': 'last_value',
|
|
235
|
+
'sections_to_keep':'all',
|
|
235
236
|
'filter': True,
|
|
236
237
|
'show_graphs': True,
|
|
237
238
|
'filter_type': 'butterworth',
|
|
@@ -318,6 +319,7 @@ CONFIG_HELP = {'config': ["C", "path to a toml configuration file"],
|
|
|
318
319
|
'interpolate': ["", "interpolate missing data. true if not specified"],
|
|
319
320
|
'interp_gap_smaller_than': ["", "interpolate sequences of missing data if they are less than N frames long. 10 if not specified"],
|
|
320
321
|
'fill_large_gaps_with': ["", "last_value, nan, or zeros. last_value if not specified"],
|
|
322
|
+
'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"],
|
|
321
323
|
'filter': ["", "filter results. true if not specified"],
|
|
322
324
|
'filter_type': ["", "butterworth, gaussian, median, or loess. butterworth if not specified"],
|
|
323
325
|
'order': ["", "order of the Butterworth filter. 4 if not specified"],
|
|
@@ -1074,6 +1074,7 @@ def select_persons_on_vid(frames, all_pose_coords):
|
|
|
1074
1074
|
bbox=dict(facecolor=UNSELECTED_COLOR, edgecolor=LINE_UNSELECTED_COLOR, boxstyle='square,pad=0.3', path_effects=[patheffects.withSimplePatchShadow()]), zorder=3
|
|
1075
1075
|
)
|
|
1076
1076
|
rects.append(rect)
|
|
1077
|
+
annotations.append(annotation)
|
|
1077
1078
|
img_plot = ax_video.imshow(frame_rgb)
|
|
1078
1079
|
|
|
1079
1080
|
# Slider
|
|
@@ -1432,6 +1433,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1432
1433
|
interpolate = config_dict.get('post-processing').get('interpolate')
|
|
1433
1434
|
interp_gap_smaller_than = config_dict.get('post-processing').get('interp_gap_smaller_than')
|
|
1434
1435
|
fill_large_gaps_with = config_dict.get('post-processing').get('fill_large_gaps_with')
|
|
1436
|
+
sections_to_keep = config_dict.get('post-processing').get('sections_to_keep')
|
|
1435
1437
|
|
|
1436
1438
|
do_filter = config_dict.get('post-processing').get('filter')
|
|
1437
1439
|
show_plots = config_dict.get('post-processing').get('show_graphs')
|
|
@@ -1511,9 +1513,13 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1511
1513
|
frame_range = [int((time_range[0]-start_time) * frame_rate), int((time_range[1]-start_time) * frame_rate)] if time_range else [0, int(cap.get(cv2.CAP_PROP_FRAME_COUNT))]
|
|
1512
1514
|
frame_iterator = tqdm(range(*frame_range)) # use a progress bar
|
|
1513
1515
|
if show_realtime_results:
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1516
|
+
try:
|
|
1517
|
+
screen_width, screen_height = get_screen_size()
|
|
1518
|
+
display_width, display_height = calculate_display_size(cam_width, cam_height, screen_width, screen_height, margin=50)
|
|
1519
|
+
cv2.namedWindow(f'{video_file} Sports2D', cv2.WINDOW_NORMAL)
|
|
1520
|
+
cv2.resizeWindow(f'{video_file} Sports2D', display_width, display_height)
|
|
1521
|
+
except: # if Pose2Sim < v0.10.29
|
|
1522
|
+
cv2.namedWindow(f'{video_file} Sports2D', cv2.WINDOW_NORMAL + cv2.WINDOW_KEEPRATIO)
|
|
1517
1523
|
|
|
1518
1524
|
# Select the appropriate model based on the model_type
|
|
1519
1525
|
logging.info('\nEstimating pose...')
|
|
@@ -1561,9 +1567,9 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1561
1567
|
logging.error('Error: Pose estimation failed. Check in Config.toml that pose_model and mode are valid.')
|
|
1562
1568
|
raise ValueError('Error: Pose estimation failed. Check in Config.toml that pose_model and mode are valid.')
|
|
1563
1569
|
|
|
1564
|
-
if tracking_mode not in ['deepsort', 'sports2d']:
|
|
1565
|
-
|
|
1566
|
-
|
|
1570
|
+
# if tracking_mode not in ['deepsort', 'sports2d']:
|
|
1571
|
+
# logging.warning(f"Tracking mode {tracking_mode} not recognized. Using sports2d method.")
|
|
1572
|
+
# tracking_mode = 'sports2d'
|
|
1567
1573
|
logging.info(f'Pose tracking set up for "{pose_model_name}" model.')
|
|
1568
1574
|
logging.info(f'Mode: {mode}.\n')
|
|
1569
1575
|
logging.info(f'Persons are detected every {det_frequency} frames and tracked inbetween. Tracking is done with {tracking_mode}.')
|
|
@@ -1641,11 +1647,13 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1641
1647
|
if tracking_mode == 'sports2d':
|
|
1642
1648
|
if 'prev_keypoints' not in locals(): prev_keypoints = keypoints
|
|
1643
1649
|
prev_keypoints, keypoints, scores = sort_people_sports2d(prev_keypoints, keypoints, scores=scores)
|
|
1650
|
+
else:
|
|
1651
|
+
pass
|
|
1644
1652
|
|
|
1645
1653
|
|
|
1646
1654
|
# Process coordinates and compute angles
|
|
1647
1655
|
valid_X, valid_Y, valid_scores = [], [], []
|
|
1648
|
-
valid_X_flipped, valid_angles = [], []
|
|
1656
|
+
valid_X_flipped, valid_angles_flipped, valid_angles = [], [], []
|
|
1649
1657
|
for person_idx in range(len(keypoints)):
|
|
1650
1658
|
if load_trc_px:
|
|
1651
1659
|
person_X = keypoints[person_idx][:,0]
|
|
@@ -1691,7 +1699,17 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1691
1699
|
else:
|
|
1692
1700
|
ang = np.nan
|
|
1693
1701
|
person_angles.append(ang)
|
|
1702
|
+
|
|
1703
|
+
# flip angles on the left side if flip_left_right false
|
|
1704
|
+
if len(visible_side) <= person_idx:
|
|
1705
|
+
visible_side += ['auto'] # set to 'auto' if list too short
|
|
1706
|
+
if visible_side[person_idx] == 'left' and not flip_left_right:
|
|
1707
|
+
person_angles_flipped = list(-np.array(person_angles))
|
|
1708
|
+
else:
|
|
1709
|
+
person_angles_flipped = person_angles.copy()
|
|
1710
|
+
|
|
1694
1711
|
valid_angles.append(person_angles)
|
|
1712
|
+
valid_angles_flipped.append(person_angles_flipped)
|
|
1695
1713
|
valid_X_flipped.append(person_X_flipped)
|
|
1696
1714
|
valid_X.append(person_X)
|
|
1697
1715
|
valid_Y.append(person_Y)
|
|
@@ -1706,7 +1724,8 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1706
1724
|
img = draw_keypts(img, valid_X, valid_Y, valid_scores, cmap_str='RdYlGn')
|
|
1707
1725
|
img = draw_skel(img, valid_X, valid_Y, pose_model)
|
|
1708
1726
|
if calculate_angles:
|
|
1709
|
-
|
|
1727
|
+
|
|
1728
|
+
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)
|
|
1710
1729
|
cv2.imshow(f'{video_file} Sports2D', img)
|
|
1711
1730
|
if (cv2.waitKey(1) & 0xFF) == ord('q') or (cv2.waitKey(1) & 0xFF) == 27:
|
|
1712
1731
|
break
|
|
@@ -1829,7 +1848,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1829
1848
|
|
|
1830
1849
|
if fill_large_gaps_with.lower() == 'last_value':
|
|
1831
1850
|
for col in all_frames_X_person_interp.columns:
|
|
1832
|
-
first_run_start, last_run_end = indices_of_first_last_non_nan_chunks(all_frames_Y_person_interp[col])
|
|
1851
|
+
first_run_start, last_run_end = indices_of_first_last_non_nan_chunks(all_frames_Y_person_interp[col], min_chunk_size=interp_gap_smaller_than, chunk_choice_method=sections_to_keep)
|
|
1833
1852
|
for coord_df in [all_frames_X_person_interp, all_frames_Y_person_interp, all_frames_Z_homog]:
|
|
1834
1853
|
coord_df.loc[:first_run_start, col] = np.nan
|
|
1835
1854
|
coord_df.loc[last_run_end:, col] = np.nan
|
|
@@ -1928,6 +1947,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1928
1947
|
f'xy_origin: {xy_origin if not xy_origin=="auto" else f"auto (estimation: {[round(c) for c in xy_origin_estim]})"} px.')
|
|
1929
1948
|
|
|
1930
1949
|
# Coordinates in m
|
|
1950
|
+
new_visible_side = []
|
|
1931
1951
|
for i in range(len(trc_data)):
|
|
1932
1952
|
if not np.array(trc_data[i].iloc[:,1:] ==0).all():
|
|
1933
1953
|
# Automatically determine visible side
|
|
@@ -1957,7 +1977,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1957
1977
|
px_to_m_i = [convert_px_to_meters(trc_data[i][kpt_name], first_person_height, height_px, cx, cy, -floor_angle_estim, visible_side=visible_side_i) for kpt_name in new_keypoints_names]
|
|
1958
1978
|
trc_data_m_i = pd.concat([all_frames_time.rename('time')]+px_to_m_i, axis=1)
|
|
1959
1979
|
for c in 3*np.arange(len(trc_data_m_i.columns[3::3]))+1: # only X coordinates
|
|
1960
|
-
first_run_start, last_run_end = indices_of_first_last_non_nan_chunks(trc_data_m_i.iloc[:,c])
|
|
1980
|
+
first_run_start, last_run_end = indices_of_first_last_non_nan_chunks(trc_data_m_i.iloc[:,c], min_chunk_size=interp_gap_smaller_than, chunk_choice_method=sections_to_keep)
|
|
1961
1981
|
trc_data_m_i.iloc[:first_run_start,c+2] = np.nan
|
|
1962
1982
|
trc_data_m_i.iloc[last_run_end:,c+2] = np.nan
|
|
1963
1983
|
trc_data_m_i.iloc[first_run_start:last_run_end,c+2] = trc_data_m_i.iloc[first_run_start:last_run_end,c+2].ffill().bfill()
|
|
@@ -1976,7 +1996,9 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1976
1996
|
if make_c3d:
|
|
1977
1997
|
c3d_path = convert_to_c3d(str(pose_path_person_m_i))
|
|
1978
1998
|
logging.info(f'Pose in meters saved to {pose_path_person_m_i.resolve()}. {"Also saved in c3d format." if make_c3d else ""}')
|
|
1979
|
-
|
|
1999
|
+
else:
|
|
2000
|
+
visible_side_i = 'none'
|
|
2001
|
+
new_visible_side += [visible_side_i]
|
|
1980
2002
|
|
|
1981
2003
|
|
|
1982
2004
|
|
|
@@ -2032,13 +2054,20 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
2032
2054
|
for i in range(all_frames_angles_homog.shape[1]): # for each person
|
|
2033
2055
|
for j in range(all_frames_angles_homog.shape[2]): # for each angle
|
|
2034
2056
|
valid_mask = ~np.isnan(all_frames_angles_homog[:, i, j])
|
|
2035
|
-
|
|
2036
|
-
|
|
2057
|
+
ang = np.unwrap(all_frames_angles_homog[valid_mask, i, j], period=180)
|
|
2058
|
+
ang = ang-360 if ang.mean()> 180 else ang
|
|
2059
|
+
ang = ang+360 if ang.mean()<-180 else ang
|
|
2060
|
+
all_frames_angles_homog[valid_mask, i, j] = ang
|
|
2061
|
+
|
|
2037
2062
|
# Process angles for each person
|
|
2038
2063
|
for i, idx_person in enumerate(selected_persons):
|
|
2039
2064
|
angles_path_person = angles_output_path.parent / (angles_output_path.stem + f'_person{i:02d}.mot')
|
|
2040
2065
|
all_frames_angles_person = pd.DataFrame(all_frames_angles_homog[:,idx_person,:], columns=angle_names)
|
|
2041
2066
|
|
|
2067
|
+
# Flip angles for left side when flip_left_right false
|
|
2068
|
+
if new_visible_side[i] == 'left' and not flip_left_right:
|
|
2069
|
+
all_frames_angles_homog[:, idx_person, :] = -all_frames_angles_homog[:, idx_person, :]
|
|
2070
|
+
|
|
2042
2071
|
# Delete person if less than 4 valid frames
|
|
2043
2072
|
angle_nan_count = len(np.where(all_frames_angles_person.sum(axis=1)==0)[0])
|
|
2044
2073
|
if frame_count - frame_range[0] - angle_nan_count <= 4:
|
|
@@ -2055,7 +2084,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
2055
2084
|
all_frames_angles_person_interp = all_frames_angles_person.apply(interpolate_zeros_nans, axis=0, args = [interp_gap_smaller_than, 'linear'])
|
|
2056
2085
|
if fill_large_gaps_with == 'last_value':
|
|
2057
2086
|
for col in all_frames_angles_person_interp.columns:
|
|
2058
|
-
first_run_start, last_run_end = indices_of_first_last_non_nan_chunks(all_frames_angles_person_interp[col])
|
|
2087
|
+
first_run_start, last_run_end = indices_of_first_last_non_nan_chunks(all_frames_angles_person_interp[col], min_chunk_size=interp_gap_smaller_than, chunk_choice_method=sections_to_keep)
|
|
2059
2088
|
all_frames_angles_person_interp.loc[:first_run_start, col] = np.nan
|
|
2060
2089
|
all_frames_angles_person_interp.loc[last_run_end:, col] = np.nan
|
|
2061
2090
|
all_frames_angles_person_interp.loc[first_run_start:last_run_end, col] = all_frames_angles_person_interp.loc[first_run_start:last_run_end, col].ffill().bfill()
|
|
@@ -2152,7 +2181,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
2152
2181
|
if save_vid:
|
|
2153
2182
|
out_vid.write(img)
|
|
2154
2183
|
if save_img:
|
|
2155
|
-
cv2.imwrite(str((img_output_dir / f'{output_dir_name}_{(frame_count
|
|
2184
|
+
cv2.imwrite(str((img_output_dir / f'{output_dir_name}_{(frame_count):06d}.png')), img)
|
|
2156
2185
|
|
|
2157
2186
|
if save_vid:
|
|
2158
2187
|
out_vid.release()
|
|
@@ -2204,7 +2233,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
2204
2233
|
# masses.append(DEFAULT_MASS)
|
|
2205
2234
|
logging.info(f'Less than 4 valid frames. Deleting person.')
|
|
2206
2235
|
else:
|
|
2207
|
-
if
|
|
2236
|
+
if new_visible_side[i] == 'none':
|
|
2208
2237
|
logging.info(f'Skipping marker augmentation and inverse kinematics because visible_side is "none".')
|
|
2209
2238
|
# heights_m.append(DEFAULT_HEIGHT)
|
|
2210
2239
|
# masses.append(DEFAULT_MASS)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sports2d
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.15
|
|
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>
|
|
@@ -402,6 +402,17 @@ sports2d --video_input demo.mp4 other_video.mp4 --time_range 1.2 2.7 0 3.5
|
|
|
402
402
|
```cmd
|
|
403
403
|
sports2d --calculate_angles false
|
|
404
404
|
```
|
|
405
|
+
- Flip angles when the person faces the other side.\
|
|
406
|
+
**N.B.:** *We consider that the person looks to the right if their toe keypoint is to the right of their heel. This is not always true when the person is sprinting, especially in the swing phase. Set it to false if you want timeseries to be continuous even when the participant switches their stance.*
|
|
407
|
+
```cmd
|
|
408
|
+
sports2d --flip_left_right true # Default
|
|
409
|
+
```
|
|
410
|
+
- Correct segment angles according to the estimated camera tild angle.\
|
|
411
|
+
**N.B.:** *The camera tilt angle is automatically estimated. Set to false if it is actually the floor which is tilted rather than the camera.*
|
|
412
|
+
```cmd
|
|
413
|
+
sports2d --correct_segment_angles_with_floor_angle true # Default
|
|
414
|
+
```
|
|
415
|
+
|
|
405
416
|
- To run **inverse kinematics with OpenSim**, check [this section](#run-inverse-kinematics)
|
|
406
417
|
|
|
407
418
|
<br>
|
|
@@ -505,8 +516,8 @@ sports2d --help
|
|
|
505
516
|
'large_hip_knee_angles': ["", "Hip and knee angles below this value are considered as imprecise. Defaults to 45"],
|
|
506
517
|
'trimmed_extrema_percent': ["", "Proportion of the most extreme segment values to remove before calculating their mean. Defaults to 50"],
|
|
507
518
|
'fontSize': ["", "font size for angle values. 0.3 if not specified"],
|
|
508
|
-
'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
|
|
509
|
-
'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
|
|
519
|
+
'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"],
|
|
520
|
+
'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"],
|
|
510
521
|
'interpolate': ["", "interpolate missing data. true if not specified"],
|
|
511
522
|
'interp_gap_smaller_than': ["", "interpolate sequences of missing data if they are less than N frames long. 10 if not specified"],
|
|
512
523
|
'fill_large_gaps_with': ["", "last_value, nan, or zeros. last_value if not specified"],
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|