sports2d 0.8.13__py3-none-any.whl → 0.8.14__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.
@@ -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
Sports2D/Sports2D.py CHANGED
@@ -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"],
Sports2D/process.py CHANGED
@@ -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
- cv2.namedWindow(f'{video_file} Sports2D', cv2.WINDOW_NORMAL + cv2.WINDOW_KEEPRATIO)
1515
- cv2.setWindowProperty(f'{video_file} Sports2D', cv2.WND_PROP_ASPECT_RATIO, cv2.WINDOW_FULLSCREEN)
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
- logging.warning(f"Tracking mode {tracking_mode} not recognized. Using sports2d method.")
1566
- tracking_mode = 'sports2d'
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,6 +1647,8 @@ 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
@@ -1829,7 +1837,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
1829
1837
 
1830
1838
  if fill_large_gaps_with.lower() == 'last_value':
1831
1839
  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])
1840
+ 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
1841
  for coord_df in [all_frames_X_person_interp, all_frames_Y_person_interp, all_frames_Z_homog]:
1834
1842
  coord_df.loc[:first_run_start, col] = np.nan
1835
1843
  coord_df.loc[last_run_end:, col] = np.nan
@@ -1957,7 +1965,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
1957
1965
  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
1966
  trc_data_m_i = pd.concat([all_frames_time.rename('time')]+px_to_m_i, axis=1)
1959
1967
  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])
1968
+ 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
1969
  trc_data_m_i.iloc[:first_run_start,c+2] = np.nan
1962
1970
  trc_data_m_i.iloc[last_run_end:,c+2] = np.nan
1963
1971
  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()
@@ -2032,8 +2040,11 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
2032
2040
  for i in range(all_frames_angles_homog.shape[1]): # for each person
2033
2041
  for j in range(all_frames_angles_homog.shape[2]): # for each angle
2034
2042
  valid_mask = ~np.isnan(all_frames_angles_homog[:, i, j])
2035
- all_frames_angles_homog[valid_mask, i, j] = np.unwrap(all_frames_angles_homog[valid_mask, i, j], period=180)
2036
-
2043
+ ang = np.unwrap(all_frames_angles_homog[valid_mask, i, j], period=180)
2044
+ ang = ang-360 if ang.mean()> 180 else ang
2045
+ ang = ang+360 if ang.mean()<-180 else ang
2046
+ all_frames_angles_homog[valid_mask, i, j] = ang
2047
+
2037
2048
  # Process angles for each person
2038
2049
  for i, idx_person in enumerate(selected_persons):
2039
2050
  angles_path_person = angles_output_path.parent / (angles_output_path.stem + f'_person{i:02d}.mot')
@@ -2055,7 +2066,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
2055
2066
  all_frames_angles_person_interp = all_frames_angles_person.apply(interpolate_zeros_nans, axis=0, args = [interp_gap_smaller_than, 'linear'])
2056
2067
  if fill_large_gaps_with == 'last_value':
2057
2068
  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])
2069
+ 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
2070
  all_frames_angles_person_interp.loc[:first_run_start, col] = np.nan
2060
2071
  all_frames_angles_person_interp.loc[last_run_end:, col] = np.nan
2061
2072
  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 +2163,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
2152
2163
  if save_vid:
2153
2164
  out_vid.write(img)
2154
2165
  if save_img:
2155
- cv2.imwrite(str((img_output_dir / f'{output_dir_name}_{(frame_count-1):06d}.png')), img)
2166
+ cv2.imwrite(str((img_output_dir / f'{output_dir_name}_{(frame_count):06d}.png')), img)
2156
2167
 
2157
2168
  if save_vid:
2158
2169
  out_vid.release()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sports2d
3
- Version: 0.8.13
3
+ Version: 0.8.14
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 participent switches their stance. true if not specified"],
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 instead. True if not specified"],
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"],
@@ -9,18 +9,18 @@ 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=tbQi_d6GXMqFkSd1_FRxyl6oA4CXEJH0g-lEskmR4mI,33521
12
+ Sports2D/Sports2D.py,sha256=Cq8UEKtwyNGWyaWjHAaVUTU8nwbmPuohAWmGbXb-hZM,33815
13
13
  Sports2D/__init__.py,sha256=BuUkPEdItxlkeqz4dmoiPwZLkgAfABJK3KWQ1ujTGwE,153
14
- Sports2D/process.py,sha256=VrjshVnplbaNZY5zcNWbuR30peqN28JlYvONwtWZFPQ,113626
15
- Sports2D/Demo/Config_demo.toml,sha256=jiPGr6JbePu1tItgRxdAu45mwFqr3RcIi_tfhO2ik1g,14027
14
+ Sports2D/process.py,sha256=6Qd6IHIMbuAa78NcJuknzUK0YthozMDodYi1QXDOSKI,114495
15
+ Sports2D/Demo/Config_demo.toml,sha256=skeMUqEyfWSrCF8Va8O0eiHsZRBLZooTZ1w-4-gm6Zc,14265
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
19
  Sports2D/Utilities/filter.py,sha256=rfZcqofjllKI_5ovZTKEAmyjOZpB_PzbAJ0P874T8Ak,4973
20
20
  Sports2D/Utilities/tests.py,sha256=3-slY2teKd78q0NzfJ2H84xRFSNXvGyDwew27oHJfrY,4740
21
- sports2d-0.8.13.dist-info/licenses/LICENSE,sha256=f4qe3nE0Y7ltJho5w-xAR0jI5PUox5Xl-MsYiY7ZRM8,1521
22
- sports2d-0.8.13.dist-info/METADATA,sha256=Kj5MWI6mJKi3HePcPThjFXkV1zMGug966uVX0D-r9tk,38443
23
- sports2d-0.8.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
- sports2d-0.8.13.dist-info/entry_points.txt,sha256=V8dFDIXatz9VvoGgoHzb2wE71C9-f85K6_OjnEQlxww,108
25
- sports2d-0.8.13.dist-info/top_level.txt,sha256=cWWBiDD2WbQXMoIoN6-9et9U2t2c_ZKo2JtBqO5uN-k,17
26
- sports2d-0.8.13.dist-info/RECORD,,
21
+ sports2d-0.8.14.dist-info/licenses/LICENSE,sha256=f4qe3nE0Y7ltJho5w-xAR0jI5PUox5Xl-MsYiY7ZRM8,1521
22
+ sports2d-0.8.14.dist-info/METADATA,sha256=6BMWYq533iLi5MGk7M7pcZrSogPcfrJ13X-BvzC5wYs,39189
23
+ sports2d-0.8.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
+ sports2d-0.8.14.dist-info/entry_points.txt,sha256=V8dFDIXatz9VvoGgoHzb2wE71C9-f85K6_OjnEQlxww,108
25
+ sports2d-0.8.14.dist-info/top_level.txt,sha256=cWWBiDD2WbQXMoIoN6-9et9U2t2c_ZKo2JtBqO5uN-k,17
26
+ sports2d-0.8.14.dist-info/RECORD,,