sports2d 0.5.4__py3-none-any.whl → 0.5.5__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/process.py CHANGED
@@ -1101,7 +1101,7 @@ def get_personID_with_highest_scores(all_frames_scores):
1101
1101
  return person_id
1102
1102
 
1103
1103
 
1104
- def compute_floor_line(trc_data, keypoint_names = ['LBigToe', 'RBigToe'], speed_threshold = 2.5):
1104
+ def compute_floor_line(trc_data, keypoint_names = ['LBigToe', 'RBigToe'], toe_speed_below = 1.0, tot_speed_above=2.0):
1105
1105
  '''
1106
1106
  Compute the floor line equation and angle
1107
1107
  from the feet keypoints when they have zero speed.
@@ -1111,19 +1111,24 @@ def compute_floor_line(trc_data, keypoint_names = ['LBigToe', 'RBigToe'], speed_
1111
1111
  INPUTS:
1112
1112
  - trc_data: pd.DataFrame. The trc data
1113
1113
  - keypoint_names: list of str. The names of the keypoints to use
1114
- - speed_threshold: float. The speed threshold below which the keypoints are considered as not moving
1114
+ - toe_speed_below: float. The speed threshold (px/frame) below which the keypoints are considered as not moving
1115
1115
 
1116
1116
  OUTPUT:
1117
1117
  - angle: float. The angle of the floor line in radians
1118
1118
  - xy_origin: list. The origin of the floor line
1119
1119
  '''
1120
1120
 
1121
+
1122
+ # Remove frames where the person is mostly not moving (outlier)
1123
+ av_speeds = np.nanmean([np.linalg.norm(trc_data[kpt].diff(), axis=1) for kpt in trc_data.columns.unique()[1:]], axis=0)
1124
+ trc_data = trc_data[av_speeds>tot_speed_above]
1125
+
1121
1126
  # Retrieve zero-speed coordinates for the foot
1122
1127
  low_speeds_X, low_speeds_Y = [], []
1123
1128
  for kpt in keypoint_names:
1124
1129
  speeds = np.linalg.norm(trc_data[kpt].diff(), axis=1)
1125
1130
 
1126
- low_speed_frames = trc_data[speeds<speed_threshold].index
1131
+ low_speed_frames = trc_data[speeds<toe_speed_below].index
1127
1132
  low_speeds_coords = trc_data[kpt].loc[low_speed_frames]
1128
1133
  low_speeds_coords = low_speeds_coords[low_speeds_coords!=0]
1129
1134
 
@@ -1501,7 +1506,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
1501
1506
  frame_count = 0
1502
1507
  while cap.isOpened():
1503
1508
  # Skip to the starting frame
1504
- if frame_count < frame_range[0]:
1509
+ if frame_count < frame_range[0] and not load_trc:
1505
1510
  cap.read()
1506
1511
  frame_count += 1
1507
1512
  continue
@@ -1528,6 +1533,8 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
1528
1533
 
1529
1534
  # Retrieve pose or Estimate pose and track people
1530
1535
  if load_trc:
1536
+ if frame_nb >= len(keypoints_all):
1537
+ break
1531
1538
  keypoints = keypoints_all[frame_nb]
1532
1539
  scores = scores_all[frame_nb]
1533
1540
  else:
@@ -1545,18 +1552,23 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
1545
1552
  valid_X, valid_Y, valid_scores = [], [], []
1546
1553
  valid_X_flipped, valid_angles = [], []
1547
1554
  for person_idx in range(len(keypoints)):
1548
- # Retrieve keypoints and scores for the person, remove low-confidence keypoints
1549
- person_X, person_Y = np.where(scores[person_idx][:, np.newaxis] < keypoint_likelihood_threshold, np.nan, keypoints[person_idx]).T
1550
- person_scores = np.where(scores[person_idx] < keypoint_likelihood_threshold, np.nan, scores[person_idx])
1551
-
1552
- # Skip person if the fraction of valid detected keypoints is too low
1553
- enough_good_keypoints = len(person_scores[~np.isnan(person_scores)]) >= len(person_scores) * keypoint_number_threshold
1554
- scores_of_good_keypoints = person_scores[~np.isnan(person_scores)]
1555
- average_score_of_remaining_keypoints_is_enough = (np.nanmean(scores_of_good_keypoints) if len(scores_of_good_keypoints)>0 else 0) >= average_likelihood_threshold
1556
- if not enough_good_keypoints or not average_score_of_remaining_keypoints_is_enough:
1557
- person_X = np.full_like(person_X, np.nan)
1558
- person_Y = np.full_like(person_Y, np.nan)
1559
- person_scores = np.full_like(person_scores, np.nan)
1555
+ if load_trc:
1556
+ person_X = keypoints[person_idx][:,0]
1557
+ person_Y = keypoints[person_idx][:,1]
1558
+ person_scores = scores[person_idx]
1559
+ else:
1560
+ # Retrieve keypoints and scores for the person, remove low-confidence keypoints
1561
+ person_X, person_Y = np.where(scores[person_idx][:, np.newaxis] < keypoint_likelihood_threshold, np.nan, keypoints[person_idx]).T
1562
+ person_scores = np.where(scores[person_idx] < keypoint_likelihood_threshold, np.nan, scores[person_idx])
1563
+
1564
+ # Skip person if the fraction of valid detected keypoints is too low
1565
+ enough_good_keypoints = len(person_scores[~np.isnan(person_scores)]) >= len(person_scores) * keypoint_number_threshold
1566
+ scores_of_good_keypoints = person_scores[~np.isnan(person_scores)]
1567
+ average_score_of_remaining_keypoints_is_enough = (np.nanmean(scores_of_good_keypoints) if len(scores_of_good_keypoints)>0 else 0) >= average_likelihood_threshold
1568
+ if not enough_good_keypoints or not average_score_of_remaining_keypoints_is_enough:
1569
+ person_X = np.full_like(person_X, np.nan)
1570
+ person_Y = np.full_like(person_Y, np.nan)
1571
+ person_scores = np.full_like(person_scores, np.nan)
1560
1572
  valid_X.append(person_X)
1561
1573
  valid_Y.append(person_Y)
1562
1574
  valid_scores.append(person_scores)
@@ -1634,7 +1646,8 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
1634
1646
  frame_range = [0,frame_count] if video_file == 'webcam' else frame_range
1635
1647
  all_frames_time = pd.Series(np.linspace(frame_range[0]/fps/slowmo_factor, frame_range[1]/fps/slowmo_factor, frame_count+1), name='time')
1636
1648
  if not multiperson:
1637
- detected_persons = [get_personID_with_highest_scores(all_frames_scores)]
1649
+ calib_on_person_id = get_personID_with_highest_scores(all_frames_scores)
1650
+ detected_persons = [calib_on_person_id]
1638
1651
  else:
1639
1652
  detected_persons = range(all_frames_X_homog.shape[1])
1640
1653
 
@@ -1735,24 +1748,26 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
1735
1748
 
1736
1749
  if floor_angle == 'auto' or xy_origin == 'auto':
1737
1750
  # estimated from the line formed by the toes when they are on the ground (where speed = 0)
1738
- floor_angle_estim, xy_origin_estim = compute_floor_line(trc_data[calib_on_person_id], keypoint_names=['LBigToe', 'RBigToe'])
1751
+ toe_speed_below = 1 # m/s (below which the foot is considered to be stationary)
1752
+ px_per_m = height_px/person_height_m
1753
+ toe_speed_below_px_frame = toe_speed_below * px_per_m / (fps*slowmo_factor)
1754
+ floor_angle_estim, xy_origin_estim = compute_floor_line(trc_data[calib_on_person_id], keypoint_names=['LBigToe', 'RBigToe'], toe_speed_below=toe_speed_below_px_frame)
1739
1755
  if not floor_angle == 'auto':
1740
1756
  floor_angle_estim = floor_angle
1741
- floor_angle_estim = -floor_angle_estim # Y points downwards
1742
1757
  if xy_origin == 'auto':
1743
1758
  cx, cy = xy_origin_estim
1744
1759
  else:
1745
1760
  cx, cy = xy_origin
1746
1761
  logging.info(f'Using height of person #{calib_on_person_id} ({person_height_m}m) to convert coordinates in meters. '
1747
- f'Floor angle: {np.degrees(-floor_angle_estim) if not floor_angle=="auto" else f"auto (estimation: {round(np.degrees(-floor_angle_estim),2)}°)"}, '
1748
- f'xy_origin: {xy_origin if not xy_origin=="auto" else f"auto (estimation: {[round(c,2) for c in xy_origin_estim]})"}.')
1762
+ f'Floor angle: {np.degrees(floor_angle_estim) if not floor_angle=="auto" else f"auto (estimation: {round(np.degrees(floor_angle_estim),2)}°)"}, '
1763
+ f'xy_origin: {xy_origin if not xy_origin=="auto" else f"auto (estimation: {[round(c) for c in xy_origin_estim]})"}.')
1749
1764
 
1750
1765
  # Coordinates in m
1751
1766
  for i in range(len(trc_data)):
1752
1767
  if not np.array(trc_data[i].iloc[:,1:] ==0).all():
1753
- trc_data_m_i = pd.concat([convert_px_to_meters(trc_data[i][kpt_name], person_height_m, height_px, cx, cy, floor_angle_estim) for kpt_name in keypoints_names], axis=1)
1768
+ trc_data_m_i = pd.concat([convert_px_to_meters(trc_data[i][kpt_name], person_height_m, height_px, cx, cy, -floor_angle_estim) for kpt_name in keypoints_names], axis=1)
1754
1769
  trc_data_m_i.insert(0, 't', all_frames_time)
1755
- trc_data_unfiltered_m_i = pd.concat([convert_px_to_meters(trc_data_unfiltered[i][kpt_name], person_height_m, height_px, cx, cy, floor_angle_estim) for kpt_name in keypoints_names], axis=1)
1770
+ trc_data_unfiltered_m_i = pd.concat([convert_px_to_meters(trc_data_unfiltered[i][kpt_name], person_height_m, height_px, cx, cy, -floor_angle_estim) for kpt_name in keypoints_names], axis=1)
1756
1771
  trc_data_unfiltered_m_i.insert(0, 't', all_frames_time)
1757
1772
 
1758
1773
  if to_meters and show_plots:
@@ -1815,6 +1830,9 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
1815
1830
  if save_angles and calculate_angles:
1816
1831
  logging.info('\nPost-processing angles:')
1817
1832
  all_frames_angles = make_homogeneous(all_frames_angles)
1833
+
1834
+ # unwrap angles
1835
+ all_frames_angles = np.unwrap(all_frames_angles, axis=0, period=180)
1818
1836
 
1819
1837
  # Process angles for each person
1820
1838
  for i in detected_persons:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sports2d
3
- Version: 0.5.4
3
+ Version: 0.5.5
4
4
  Summary: Detect pose and compute 2D joint angles from a video.
5
5
  Home-page: https://github.com/davidpagnon/Sports2D
6
6
  Author: David Pagnon
@@ -1,6 +1,6 @@
1
1
  Sports2D/Sports2D.py,sha256=dZv4xglguFJZDu9Zv0AZKAGu1TQIW9ynmY8pMsNHw14,26377
2
2
  Sports2D/__init__.py,sha256=TyCP7Uuuy6CNklhPf8W84MbYoO1_-1dxowSYAJyk_OI,102
3
- Sports2D/process.py,sha256=6rFjME0pSJXZpYD9t7Ai8P5Ly2MVpmKgZAo912j24kM,87663
3
+ Sports2D/process.py,sha256=g2kfmEaSs8LTir3IpuWSKDsNwYqnuWGLpsMj-vQ-xUE,88675
4
4
  Sports2D/Demo/Config_demo.toml,sha256=kp2iqohOLlN3vzFBDgz69BB8kpaYqcGXDQpchlxeO9w,6769
5
5
  Sports2D/Demo/demo.mp4,sha256=2aZkFxhWR7ESMEtXCT8MGA83p2jmoU2sp1ylQfO3gDk,3968304
6
6
  Sports2D/Utilities/__init__.py,sha256=TyCP7Uuuy6CNklhPf8W84MbYoO1_-1dxowSYAJyk_OI,102
@@ -8,9 +8,9 @@ Sports2D/Utilities/common.py,sha256=FEWmlq9HNlHzA2ioV5MPPOeC-5Py4JaDbIIxQgq9hGE,
8
8
  Sports2D/Utilities/filter.py,sha256=8mVefMjDzxmh9a30eNtIrUuK_mUKoOJ2Nr-OzcQKkKM,4922
9
9
  Sports2D/Utilities/skeletons.py,sha256=44IWpz47zjh_6YDqkwaJnSysaGi7ovgYE25ji-hC-Kw,15660
10
10
  Sports2D/Utilities/tests.py,sha256=g06HBExGkvZrhZpNXN19G9Shisfgp1cqjAp0kFxiKEc,2574
11
- sports2d-0.5.4.dist-info/LICENSE,sha256=f4qe3nE0Y7ltJho5w-xAR0jI5PUox5Xl-MsYiY7ZRM8,1521
12
- sports2d-0.5.4.dist-info/METADATA,sha256=0EOJSFWnNfbndViXV-gYWglHkzkTeYvg5CIpBEK4pVs,23067
13
- sports2d-0.5.4.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
14
- sports2d-0.5.4.dist-info/entry_points.txt,sha256=h2CJTuydtNf8JyaLoWxWl5HTSIxx5Ra_FSiSGQsf7Sk,52
15
- sports2d-0.5.4.dist-info/top_level.txt,sha256=DoURf9UDB8lQ_9lMUPQMQqhXCvWPFFjJco9NzPlHJ6I,9
16
- sports2d-0.5.4.dist-info/RECORD,,
11
+ sports2d-0.5.5.dist-info/LICENSE,sha256=f4qe3nE0Y7ltJho5w-xAR0jI5PUox5Xl-MsYiY7ZRM8,1521
12
+ sports2d-0.5.5.dist-info/METADATA,sha256=vIFM97emgo_ZgljSbeI_lBRI2QObHeAwB3uf5TCJ5x8,23067
13
+ sports2d-0.5.5.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
14
+ sports2d-0.5.5.dist-info/entry_points.txt,sha256=h2CJTuydtNf8JyaLoWxWl5HTSIxx5Ra_FSiSGQsf7Sk,52
15
+ sports2d-0.5.5.dist-info/top_level.txt,sha256=DoURf9UDB8lQ_9lMUPQMQqhXCvWPFFjJco9NzPlHJ6I,9
16
+ sports2d-0.5.5.dist-info/RECORD,,