sports2d 0.8.19__py3-none-any.whl → 0.8.20__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 +3 -2
- Sports2D/Sports2D.py +5 -3
- Sports2D/process.py +58 -17
- {sports2d-0.8.19.dist-info → sports2d-0.8.20.dist-info}/METADATA +2 -2
- {sports2d-0.8.19.dist-info → sports2d-0.8.20.dist-info}/RECORD +9 -9
- {sports2d-0.8.19.dist-info → sports2d-0.8.20.dist-info}/WHEEL +0 -0
- {sports2d-0.8.19.dist-info → sports2d-0.8.20.dist-info}/entry_points.txt +0 -0
- {sports2d-0.8.19.dist-info → sports2d-0.8.20.dist-info}/licenses/LICENSE +0 -0
- {sports2d-0.8.19.dist-info → sports2d-0.8.20.dist-info}/top_level.txt +0 -0
Sports2D/Demo/Config_demo.toml
CHANGED
|
@@ -60,8 +60,8 @@ pose_model = 'Body_with_feet' #With RTMLib:
|
|
|
60
60
|
# - Hand (HAND_21, only lightweight mode. Potentially better results with Whole_body),
|
|
61
61
|
# - Face (FACE_106),
|
|
62
62
|
# - Animal (ANIMAL2D_17)
|
|
63
|
-
#
|
|
64
|
-
#
|
|
63
|
+
# ⚠ Only RTMPose is natively embeded in Pose2Sim. For all other pose estimation methods, you will have to run them yourself, and then refer to the documentation to convert the output files if needed
|
|
64
|
+
# ⚠ For Face and Animal, use mode="""{dictionary}""", and find the corresponding .onnx model there https://github.com/open-mmlab/mmpose/tree/main/projects/rtmpose
|
|
65
65
|
mode = 'balanced' # 'lightweight', 'balanced', 'performance', or """{dictionary}""" (see below)
|
|
66
66
|
|
|
67
67
|
# A dictionary (WITHIN THREE DOUBLE QUOTES) allows you to manually select the person detection (if top_down approach) and/or pose estimation models (see https://github.com/Tau-J/rtmlib).
|
|
@@ -139,6 +139,7 @@ reject_outliers = true # Hampel filter for outlier rejection before other f
|
|
|
139
139
|
|
|
140
140
|
filter = true
|
|
141
141
|
show_graphs = true # Show plots of raw and processed results
|
|
142
|
+
save_graphs = false # Save position and angle plots of raw and processed results
|
|
142
143
|
filter_type = 'butterworth' # butterworth, kalman, gcv_spline, gaussian, loess, median, butterworth_on_speed
|
|
143
144
|
|
|
144
145
|
# Most intuitive and standard filter in biomechanics
|
Sports2D/Sports2D.py
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
- Run on webcam with default parameters:
|
|
29
29
|
sports2d --video_input webcam
|
|
30
30
|
- Run with custom parameters (all non specified are set to default):
|
|
31
|
-
sports2d --
|
|
31
|
+
sports2d --show_graphs False --time_range 0 2.1 --result_dir path_to_result_dir
|
|
32
32
|
sports2d --person_detection_method highest_likelihood --mode lightweight --det_frequency 50
|
|
33
33
|
- Run with a toml configuration file:
|
|
34
34
|
sports2d --config path_to_config.toml
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
pip install .
|
|
45
45
|
|
|
46
46
|
-----
|
|
47
|
-
|
|
47
|
+
⚠ Warning ⚠
|
|
48
48
|
-----
|
|
49
49
|
- The angle estimation is only as good as the pose estimation algorithm, i.e., it is not perfect.
|
|
50
50
|
- It will only lead to acceptable results if the persons move in the 2D plane (sagittal plane).
|
|
@@ -236,6 +236,7 @@ DEFAULT_CONFIG = {'base': {'video_input': ['demo.mp4'],
|
|
|
236
236
|
'reject_outliers': True,
|
|
237
237
|
'filter': True,
|
|
238
238
|
'show_graphs': True,
|
|
239
|
+
'save_graphs': False,
|
|
239
240
|
'filter_type': 'butterworth',
|
|
240
241
|
'butterworth': {'order': 4, 'cut_off_frequency': 6.0},
|
|
241
242
|
'kalman': {'trust_ratio': 500.0, 'smooth':True},
|
|
@@ -279,6 +280,7 @@ CONFIG_HELP = {'config': ["C", "path to a toml configuration file"],
|
|
|
279
280
|
'show_realtime_results': ["R", "show results in real-time. true if not specified"],
|
|
280
281
|
'display_angle_values_on': ["a", '"body", "list", "body" "list", or "none". body list if not specified'],
|
|
281
282
|
'show_graphs': ["G", "show plots of raw and processed results. true if not specified"],
|
|
283
|
+
'save_graphs': ["", "save position and angle plots of raw and processed results. false if not specified"],
|
|
282
284
|
'joint_angles': ["j", '"Right ankle" "Left ankle" "Right knee" "Left knee" "Right hip" "Left hip" "Right shoulder" "Left shoulder" "Right elbow" "Left elbow" if not specified'],
|
|
283
285
|
'segment_angles': ["s", '"Right foot" "Left foot" "Right shank" "Left shank" "Right thigh" "Left thigh" "Pelvis" "Trunk" "Shoulders" "Head" "Right arm" "Left arm" "Right forearm" "Left forearm" if not specified'],
|
|
284
286
|
'save_vid': ["V", "save processed video. true if not specified"],
|
|
@@ -546,7 +548,7 @@ def main():
|
|
|
546
548
|
- Run on webcam with default parameters:
|
|
547
549
|
sports2d --video_input webcam
|
|
548
550
|
- Run with custom parameters (all non specified are set to default):
|
|
549
|
-
sports2d --
|
|
551
|
+
sports2d --show_graphs False --time_range 0 2.1 --result_dir path_to_result_dir
|
|
550
552
|
sports2d --mode lightweight --det_frequency 50
|
|
551
553
|
- Run with a toml configuration file:
|
|
552
554
|
sports2d --config path_to_config.toml
|
Sports2D/process.py
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
- optionally plots pose and angle data before and after processing for comparison
|
|
30
30
|
- optionally saves poses for each person as a trc file, and angles as a mot file
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
⚠ Warning ⚠
|
|
33
33
|
- The pose detection is only as good as the pose estimation algorithm, i.e., it is not perfect.
|
|
34
34
|
- It will lead to reliable results only if the persons move in the 2D plane (sagittal or frontal plane).
|
|
35
35
|
- The persons need to be filmed as perpendicularly as possible from their side.
|
|
@@ -77,6 +77,7 @@ from matplotlib.widgets import Slider, Button
|
|
|
77
77
|
from matplotlib import patheffects
|
|
78
78
|
|
|
79
79
|
from rtmlib import PoseTracker, BodyWithFeet, Wholebody, Body, Hand, Custom
|
|
80
|
+
from rtmlib.tools.object_detection.post_processings import nms
|
|
80
81
|
from deep_sort_realtime.deepsort_tracker import DeepSort
|
|
81
82
|
|
|
82
83
|
from Sports2D.Utilities.common import *
|
|
@@ -789,10 +790,10 @@ def make_mot_with_angles(angles, time, mot_path):
|
|
|
789
790
|
return angles
|
|
790
791
|
|
|
791
792
|
|
|
792
|
-
def pose_plots(trc_data_unfiltered, trc_data, person_id):
|
|
793
|
+
def pose_plots(trc_data_unfiltered, trc_data, person_id, show=True):
|
|
793
794
|
'''
|
|
794
795
|
Displays trc filtered and unfiltered data for comparison
|
|
795
|
-
|
|
796
|
+
⚠ Often crashes on the third window...
|
|
796
797
|
|
|
797
798
|
INPUTS:
|
|
798
799
|
- trc_data_unfiltered: pd.DataFrame. The unfiltered trc data
|
|
@@ -835,13 +836,16 @@ def pose_plots(trc_data_unfiltered, trc_data, person_id):
|
|
|
835
836
|
|
|
836
837
|
pw.addPlot(keypoint, f)
|
|
837
838
|
|
|
838
|
-
|
|
839
|
+
if show:
|
|
840
|
+
pw.show()
|
|
839
841
|
|
|
842
|
+
return pw
|
|
840
843
|
|
|
841
|
-
|
|
844
|
+
|
|
845
|
+
def angle_plots(angle_data_unfiltered, angle_data, person_id, show=True):
|
|
842
846
|
'''
|
|
843
847
|
Displays angle filtered and unfiltered data for comparison
|
|
844
|
-
|
|
848
|
+
⚠ Often crashes on the third window...
|
|
845
849
|
|
|
846
850
|
INPUTS:
|
|
847
851
|
- angle_data_unfiltered: pd.DataFrame. The unfiltered angle data
|
|
@@ -878,7 +882,10 @@ def angle_plots(angle_data_unfiltered, angle_data, person_id):
|
|
|
878
882
|
|
|
879
883
|
pw.addPlot(angle, f)
|
|
880
884
|
|
|
881
|
-
|
|
885
|
+
if show:
|
|
886
|
+
pw.show()
|
|
887
|
+
|
|
888
|
+
return pw
|
|
882
889
|
|
|
883
890
|
|
|
884
891
|
def get_personIDs_with_highest_scores(all_frames_scores, nb_persons_to_detect):
|
|
@@ -1374,7 +1381,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1374
1381
|
- optionally plots pose and angle data before and after processing for comparison
|
|
1375
1382
|
- optionally saves poses for each person as a trc file, and angles as a mot file
|
|
1376
1383
|
|
|
1377
|
-
|
|
1384
|
+
⚠ Warning ⚠
|
|
1378
1385
|
- The pose detection is only as good as the pose estimation algorithm, i.e., it is not perfect.
|
|
1379
1386
|
- It will lead to reliable results only if the persons move in the 2D plane (sagittal or frontal plane).
|
|
1380
1387
|
- The persons need to be filmed as perpendicularly as possible from their side.
|
|
@@ -1490,6 +1497,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1490
1497
|
handle_LR_swap = config_dict.get('post-processing').get('handle_LR_swap', False)
|
|
1491
1498
|
reject_outliers = config_dict.get('post-processing').get('reject_outliers', False)
|
|
1492
1499
|
show_plots = config_dict.get('post-processing').get('show_graphs')
|
|
1500
|
+
save_plots = config_dict.get('post-processing').get('save_graphs')
|
|
1493
1501
|
filter_type = config_dict.get('post-processing').get('filter_type')
|
|
1494
1502
|
butterworth_filter_order = config_dict.get('post-processing').get('butterworth', {}).get('order')
|
|
1495
1503
|
butterworth_filter_cutoff = config_dict.get('post-processing').get('butterworth', {}).get('cut_off_frequency')
|
|
@@ -1513,6 +1521,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1513
1521
|
output_dir_name = f'{video_file_stem}_Sports2D'
|
|
1514
1522
|
video_file_path = video_dir / video_file
|
|
1515
1523
|
output_dir = result_dir / output_dir_name
|
|
1524
|
+
plots_output_dir = output_dir / f'{output_dir_name}_graphs'
|
|
1516
1525
|
img_output_dir = output_dir / f'{output_dir_name}_img'
|
|
1517
1526
|
vid_output_path = output_dir / f'{output_dir_name}.mp4'
|
|
1518
1527
|
pose_output_path = output_dir / f'{output_dir_name}_px.trc'
|
|
@@ -1521,6 +1530,8 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1521
1530
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
1522
1531
|
if save_img:
|
|
1523
1532
|
img_output_dir.mkdir(parents=True, exist_ok=True)
|
|
1533
|
+
if save_plots:
|
|
1534
|
+
plots_output_dir.mkdir(parents=True, exist_ok=True)
|
|
1524
1535
|
|
|
1525
1536
|
# Inverse kinematics settings
|
|
1526
1537
|
do_ik = config_dict.get('kinematics').get('do_ik')
|
|
@@ -1721,6 +1732,13 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1721
1732
|
# Detect poses
|
|
1722
1733
|
keypoints, scores = pose_tracker(frame)
|
|
1723
1734
|
|
|
1735
|
+
# Non maximum suppression (at pose level, not detection)
|
|
1736
|
+
frame_shape = frame.shape
|
|
1737
|
+
bboxes = bbox_xyxy_compute(frame_shape, keypoints, padding=0)
|
|
1738
|
+
score_bboxes = np.array([np.mean(s) for s in scores])
|
|
1739
|
+
keep = nms(bboxes, score_bboxes, nms_thr=0.45)
|
|
1740
|
+
keypoints, scores = keypoints[keep], scores[keep]
|
|
1741
|
+
|
|
1724
1742
|
# Track poses across frames
|
|
1725
1743
|
if tracking_mode == 'deepsort':
|
|
1726
1744
|
keypoints, scores = sort_people_deepsort(keypoints, scores, deepsort_tracker, frame, frame_count)
|
|
@@ -1999,9 +2017,17 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1999
2017
|
columns_to_concat.extend([all_frames_X_person.iloc[:,kpt], all_frames_Y_person.iloc[:,kpt], all_frames_Z_homog.iloc[:,kpt]])
|
|
2000
2018
|
trc_data_unfiltered_i = pd.concat([all_frames_time] + columns_to_concat, axis=1)
|
|
2001
2019
|
trc_data_unfiltered.append(trc_data_unfiltered_i)
|
|
2002
|
-
if
|
|
2003
|
-
pose_plots(trc_data_unfiltered_i, trc_data_i, i)
|
|
2004
|
-
|
|
2020
|
+
if not to_meters and (show_plots or save_plots):
|
|
2021
|
+
pw = pose_plots(trc_data_unfiltered_i, trc_data_i, i, show=show_plots)
|
|
2022
|
+
if save_plots:
|
|
2023
|
+
for n, f in enumerate(pw.figure_handles):
|
|
2024
|
+
dpi = pw.canvases[i].figure.dpi
|
|
2025
|
+
f.set_size_inches(1280/dpi, 720/dpi)
|
|
2026
|
+
title = pw.tabs.tabText(n)
|
|
2027
|
+
plot_path = plots_output_dir / (pose_output_path.stem + f'_person{i:02d}_px_{title.replace(" ","_").replace("/","_")}.png')
|
|
2028
|
+
f.savefig(plot_path, dpi=dpi, bbox_inches='tight')
|
|
2029
|
+
logging.info(f'Pose plots (px) saved in {plots_output_dir}.')
|
|
2030
|
+
|
|
2005
2031
|
all_frames_X_processed[:,idx_person,:], all_frames_Y_processed[:,idx_person,:] = all_frames_X_person_filt, all_frames_Y_person_filt
|
|
2006
2032
|
if calculate_angles or save_angles:
|
|
2007
2033
|
all_frames_X_flipped_processed[:,idx_person,:] = all_frames_X_flipped_person
|
|
@@ -2087,9 +2113,17 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
2087
2113
|
px_to_m_unfiltered_i = [convert_px_to_meters(trc_data_unfiltered[i][kpt_name], first_person_height, height_px, cx, cy, -floor_angle_estim) for kpt_name in new_keypoints_names]
|
|
2088
2114
|
trc_data_unfiltered_m_i = pd.concat([all_frames_time.rename('time')]+px_to_m_unfiltered_i, axis=1)
|
|
2089
2115
|
|
|
2090
|
-
if to_meters and show_plots:
|
|
2091
|
-
pose_plots(trc_data_unfiltered_m_i, trc_data_m_i, i)
|
|
2092
|
-
|
|
2116
|
+
if to_meters and (show_plots or save_plots):
|
|
2117
|
+
pw = pose_plots(trc_data_unfiltered_m_i, trc_data_m_i, i, show=show_plots)
|
|
2118
|
+
if save_plots:
|
|
2119
|
+
for n, f in enumerate(pw.figure_handles):
|
|
2120
|
+
dpi = pw.canvases[i].figure.dpi
|
|
2121
|
+
f.set_size_inches(1280/dpi, 720/dpi)
|
|
2122
|
+
title = pw.tabs.tabText(n)
|
|
2123
|
+
plot_path = plots_output_dir / (pose_output_path_m.stem + f'_person{i:02d}_m_{title.replace(" ","_").replace("/","_")}.png')
|
|
2124
|
+
f.savefig(plot_path, dpi=dpi, bbox_inches='tight')
|
|
2125
|
+
logging.info(f'Pose plots (m) saved in {plots_output_dir}.')
|
|
2126
|
+
|
|
2093
2127
|
# Write to trc file
|
|
2094
2128
|
trc_data_m.append(trc_data_m_i)
|
|
2095
2129
|
pose_path_person_m_i = (pose_output_path.parent / (pose_output_path_m.stem + f'_person{i:02d}.trc'))
|
|
@@ -2248,9 +2282,16 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
2248
2282
|
logging.info(f'Angles saved to {angles_path_person.resolve()}.')
|
|
2249
2283
|
|
|
2250
2284
|
# Plotting angles before and after interpolation and filtering
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
angle_plots(all_frames_angles_person, angle_data, i) # i = current person
|
|
2285
|
+
all_frames_angles_person.insert(0, 'time', all_frames_time)
|
|
2286
|
+
if save_plots and (show_plots or save_plots):
|
|
2287
|
+
pw = angle_plots(all_frames_angles_person, angle_data, i, show=show_plots) # i = current person
|
|
2288
|
+
for n, f in enumerate(pw.figure_handles):
|
|
2289
|
+
dpi = pw.canvases[i].figure.dpi
|
|
2290
|
+
f.set_size_inches(1280/dpi, 720/dpi)
|
|
2291
|
+
title = pw.tabs.tabText(n)
|
|
2292
|
+
plot_path = plots_output_dir / (pose_output_path_m.stem + f'_person{i:02d}_ang_{title.replace(" ","_").replace("/","_")}.png')
|
|
2293
|
+
f.savefig(plot_path, dpi=dpi, bbox_inches='tight')
|
|
2294
|
+
logging.info(f'Pose plots (m) saved in {plots_output_dir}.')
|
|
2254
2295
|
|
|
2255
2296
|
|
|
2256
2297
|
#%% ==================================================
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sports2d
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.20
|
|
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<4.12
|
|
39
39
|
Requires-Dist: imageio_ffmpeg
|
|
40
40
|
Requires-Dist: deep-sort-realtime
|
|
41
|
-
Requires-Dist: Pose2Sim>=0.10.
|
|
41
|
+
Requires-Dist: Pose2Sim>=0.10.36
|
|
42
42
|
Dynamic: license-file
|
|
43
43
|
|
|
44
44
|
|
|
@@ -9,17 +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=aCx3Hy1XltgBS7kuQ52b653hQtL_9sjvpQ0xGoPuoOw,35572
|
|
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=VrN8ZQimznjPAk_-1r9NpZnBCzDz9z0lV-E4wNJMCq0,125549
|
|
15
|
+
Sports2D/Demo/Config_demo.toml,sha256=GXlj4tuGvnEpzQm0FFOS2BWVHcGSHGxuudAAHhzsTQc,15665
|
|
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/tests.py,sha256=bUcPoaIwa6ur13Njup5MjGY3N060Ropl_MCdAbCAbTc,4792
|
|
20
|
-
sports2d-0.8.
|
|
21
|
-
sports2d-0.8.
|
|
22
|
-
sports2d-0.8.
|
|
23
|
-
sports2d-0.8.
|
|
24
|
-
sports2d-0.8.
|
|
25
|
-
sports2d-0.8.
|
|
20
|
+
sports2d-0.8.20.dist-info/licenses/LICENSE,sha256=f4qe3nE0Y7ltJho5w-xAR0jI5PUox5Xl-MsYiY7ZRM8,1521
|
|
21
|
+
sports2d-0.8.20.dist-info/METADATA,sha256=PX4DMmISr1ZnSSKNHFqGF6F97_SuZBAPV5OOe8tgTPU,41277
|
|
22
|
+
sports2d-0.8.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
23
|
+
sports2d-0.8.20.dist-info/entry_points.txt,sha256=V8dFDIXatz9VvoGgoHzb2wE71C9-f85K6_OjnEQlxww,108
|
|
24
|
+
sports2d-0.8.20.dist-info/top_level.txt,sha256=cWWBiDD2WbQXMoIoN6-9et9U2t2c_ZKo2JtBqO5uN-k,17
|
|
25
|
+
sports2d-0.8.20.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|