sports2d 0.5.2__py3-none-any.whl → 0.5.4__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 +5 -2
- Sports2D/Sports2D.py +10 -6
- Sports2D/Utilities/common.py +37 -4
- Sports2D/process.py +19 -4
- {sports2d-0.5.2.dist-info → sports2d-0.5.4.dist-info}/METADATA +1 -1
- sports2d-0.5.4.dist-info/RECORD +16 -0
- sports2d-0.5.2.dist-info/RECORD +0 -16
- {sports2d-0.5.2.dist-info → sports2d-0.5.4.dist-info}/LICENSE +0 -0
- {sports2d-0.5.2.dist-info → sports2d-0.5.4.dist-info}/WHEEL +0 -0
- {sports2d-0.5.2.dist-info → sports2d-0.5.4.dist-info}/entry_points.txt +0 -0
- {sports2d-0.5.2.dist-info → sports2d-0.5.4.dist-info}/top_level.txt +0 -0
Sports2D/Demo/Config_demo.toml
CHANGED
|
@@ -102,7 +102,7 @@ show_graphs = true # Show plots of raw and processed results
|
|
|
102
102
|
filter_type = 'butterworth' # butterworth, gaussian, LOESS, median
|
|
103
103
|
[post-processing.butterworth]
|
|
104
104
|
order = 4
|
|
105
|
-
cut_off_frequency =
|
|
105
|
+
cut_off_frequency = 6 # Hz # Will be divided by slowmo_factor to be equivalent to non slowed-down video
|
|
106
106
|
[post-processing.gaussian]
|
|
107
107
|
sigma_kernel = 1 #px
|
|
108
108
|
[post-processing.loess]
|
|
@@ -119,4 +119,7 @@ person_orientation = ['front', 'none', 'left'] # Choose among 'auto', 'none', 'f
|
|
|
119
119
|
# Example with one person on one video: ['front']
|
|
120
120
|
# Or ['front', 'none', 'left'] with 3 persons on one video
|
|
121
121
|
osim_setup_path = '../OpenSim_setup' # Path to the OpenSim setup folder
|
|
122
|
-
close_to_zero_speed_m = 0.2 # Sum for all keypoints: about 50 px/frame or 0.2 m/frame
|
|
122
|
+
close_to_zero_speed_m = 0.2 # Sum for all keypoints: about 50 px/frame or 0.2 m/frame
|
|
123
|
+
|
|
124
|
+
[logging]
|
|
125
|
+
use_custom_logging = false # if integrated in an API that already has logging
|
Sports2D/Sports2D.py
CHANGED
|
@@ -194,7 +194,7 @@ DEFAULT_CONFIG = {'project': {'video_input': ['demo.mp4'],
|
|
|
194
194
|
'filter': True,
|
|
195
195
|
'show_graphs': True,
|
|
196
196
|
'filter_type': 'butterworth',
|
|
197
|
-
'butterworth': {'order': 4, 'cut_off_frequency':
|
|
197
|
+
'butterworth': {'order': 4, 'cut_off_frequency': 6},
|
|
198
198
|
'gaussian': {'sigma_kernel': 1},
|
|
199
199
|
'loess': {'nb_values_used': 5},
|
|
200
200
|
'median': {'kernel_size': 3}
|
|
@@ -203,7 +203,8 @@ DEFAULT_CONFIG = {'project': {'video_input': ['demo.mp4'],
|
|
|
203
203
|
'person_orientation': ['front', '', 'left'],
|
|
204
204
|
'osim_setup_path': '../OpenSim_setup',
|
|
205
205
|
'close_to_zero_speed_m': 0.2
|
|
206
|
-
}
|
|
206
|
+
},
|
|
207
|
+
'logging': {'use_custom_logging': False}
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
CONFIG_HELP = {'config': ["C", "path to a toml configuration file"],
|
|
@@ -260,7 +261,8 @@ CONFIG_HELP = {'config': ["C", "path to a toml configuration file"],
|
|
|
260
261
|
'cut_off_frequency': ["", "cut-off frequency of the Butterworth filter. 3 if not specified"],
|
|
261
262
|
'sigma_kernel': ["", "sigma of the gaussian filter. 1 if not specified"],
|
|
262
263
|
'nb_values_used': ["", "number of values used for the loess filter. 5 if not specified"],
|
|
263
|
-
'kernel_size': ["", "kernel size of the median filter. 3 if not specified"]
|
|
264
|
+
'kernel_size': ["", "kernel size of the median filter. 3 if not specified"],
|
|
265
|
+
'use_custom_logging': ["", "use custom logging. false if not specified"]
|
|
264
266
|
}
|
|
265
267
|
|
|
266
268
|
|
|
@@ -414,11 +416,13 @@ def process(config='Config_demo.toml'):
|
|
|
414
416
|
else:
|
|
415
417
|
config_dict = read_config_file(config)
|
|
416
418
|
video_dir, video_files, frame_rates, time_ranges, result_dir = base_params(config_dict)
|
|
419
|
+
use_custom_logging = config_dict.get('logging').get('use_custom_logging')
|
|
417
420
|
|
|
418
421
|
result_dir.mkdir(parents=True, exist_ok=True)
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
+
if not use_custom_logging:
|
|
423
|
+
with open(result_dir / 'logs.txt', 'a+') as log_f: pass
|
|
424
|
+
logging.basicConfig(format='%(message)s', level=logging.INFO, force=True,
|
|
425
|
+
handlers = [logging.handlers.TimedRotatingFileHandler(result_dir / 'logs.txt', when='D', interval=7), logging.StreamHandler()])
|
|
422
426
|
|
|
423
427
|
for video_file, time_range, frame_rate in zip(video_files, time_ranges, frame_rates):
|
|
424
428
|
currentDateAndTime = datetime.now()
|
Sports2D/Utilities/common.py
CHANGED
|
@@ -20,6 +20,7 @@ import sys
|
|
|
20
20
|
import toml
|
|
21
21
|
import subprocess
|
|
22
22
|
from pathlib import Path
|
|
23
|
+
import logging
|
|
23
24
|
|
|
24
25
|
import numpy as np
|
|
25
26
|
from scipy import interpolate
|
|
@@ -164,21 +165,31 @@ def make_homogeneous(list_of_arrays):
|
|
|
164
165
|
'''
|
|
165
166
|
|
|
166
167
|
def get_max_shape(list_of_arrays):
|
|
168
|
+
'''
|
|
169
|
+
Recursively determine the maximum shape of a list of arrays.
|
|
170
|
+
'''
|
|
167
171
|
if isinstance(list_of_arrays[0], list):
|
|
168
172
|
# Maximum length at the current level plus the max shape at the next level
|
|
169
173
|
return [max(len(arr) for arr in list_of_arrays)] + get_max_shape(
|
|
170
174
|
[item for sublist in list_of_arrays for item in sublist])
|
|
171
175
|
else:
|
|
172
176
|
# Determine the maximum shape across all list_of_arrays at this level
|
|
173
|
-
return [len(list_of_arrays)] + [max(arr.shape[i] for arr in list_of_arrays) for i in range(list_of_arrays[0].ndim)]
|
|
177
|
+
return [len(list_of_arrays)] + [max(arr.shape[i] for arr in list_of_arrays if arr.size > 0) for i in range(list_of_arrays[0].ndim)]
|
|
174
178
|
|
|
175
179
|
def pad_with_nans(list_of_arrays, target_shape):
|
|
176
180
|
'''
|
|
177
181
|
Recursively pad list_of_arrays with nans to match the target shape.
|
|
178
182
|
'''
|
|
179
|
-
if isinstance(list_of_arrays, np.ndarray):
|
|
180
|
-
# Pad the current array to the target shape
|
|
181
|
-
pad_width = [
|
|
183
|
+
if isinstance(list_of_arrays, np.ndarray):
|
|
184
|
+
# Pad the current array to the target shape
|
|
185
|
+
pad_width = []
|
|
186
|
+
for dim_index in range(0, len(target_shape)):
|
|
187
|
+
if dim_index == len(list_of_arrays.shape) or dim_index > len(list_of_arrays.shape):
|
|
188
|
+
list_of_arrays = np.expand_dims(list_of_arrays, 0)
|
|
189
|
+
for dim_index in range(0, len(target_shape)):
|
|
190
|
+
max_dim = target_shape[dim_index]
|
|
191
|
+
curr_dim = list_of_arrays.shape[dim_index]
|
|
192
|
+
pad_width.append((0, max_dim - curr_dim))
|
|
182
193
|
return np.pad(list_of_arrays.astype(float), pad_width, constant_values=np.nan)
|
|
183
194
|
# Recursively pad each array in the list
|
|
184
195
|
return [pad_with_nans(array, target_shape[1:]) for array in list_of_arrays]
|
|
@@ -191,6 +202,28 @@ def make_homogeneous(list_of_arrays):
|
|
|
191
202
|
return np.array(list_of_arrays)
|
|
192
203
|
|
|
193
204
|
|
|
205
|
+
def get_start_time_ffmpeg(video_path):
|
|
206
|
+
'''
|
|
207
|
+
Get the start time of a video using FFmpeg.
|
|
208
|
+
'''
|
|
209
|
+
|
|
210
|
+
try:
|
|
211
|
+
ffmpeg_path = ffmpeg.get_ffmpeg_exe()
|
|
212
|
+
except Exception as e:
|
|
213
|
+
logging.warning(f"No ffmpeg exe could be found. Starting time set to 0.0. Error: {e}")
|
|
214
|
+
return 0.0
|
|
215
|
+
|
|
216
|
+
cmd = [ffmpeg_path, "-i", video_path]
|
|
217
|
+
result = subprocess.run(cmd, stderr=subprocess.PIPE, stdout=subprocess.DEVNULL, text=True)
|
|
218
|
+
for line in result.stderr.splitlines():
|
|
219
|
+
if "start:" in line:
|
|
220
|
+
parts = line.split("start:")
|
|
221
|
+
if len(parts) > 1:
|
|
222
|
+
start_time = parts[1].split(",")[0].strip()
|
|
223
|
+
return float(start_time)
|
|
224
|
+
return 0.0 # Default to 0 if not found
|
|
225
|
+
|
|
226
|
+
|
|
194
227
|
def resample_video(vid_output_path, fps, desired_framerate):
|
|
195
228
|
'''
|
|
196
229
|
Resample video to the desired fps using ffmpeg.
|
Sports2D/process.py
CHANGED
|
@@ -195,7 +195,7 @@ def setup_video(video_file_path, save_vid, vid_output_path):
|
|
|
195
195
|
if video_file_path.name == video_file_path.stem:
|
|
196
196
|
raise ValueError("Please set video_input to 'webcam' or to a video file (with extension) in Config.toml")
|
|
197
197
|
try:
|
|
198
|
-
cap = cv2.VideoCapture(video_file_path)
|
|
198
|
+
cap = cv2.VideoCapture(str(video_file_path.absolute()))
|
|
199
199
|
if not cap.isOpened():
|
|
200
200
|
raise
|
|
201
201
|
except:
|
|
@@ -210,12 +210,12 @@ def setup_video(video_file_path, save_vid, vid_output_path):
|
|
|
210
210
|
if save_vid:
|
|
211
211
|
# try:
|
|
212
212
|
# fourcc = cv2.VideoWriter_fourcc(*'avc1') # =h264. better compression and quality but may fail on some systems
|
|
213
|
-
# out_vid = cv2.VideoWriter(vid_output_path, fourcc, fps, (cam_width, cam_height))
|
|
213
|
+
# out_vid = cv2.VideoWriter(str(vid_output_path.absolute()), fourcc, fps, (cam_width, cam_height))
|
|
214
214
|
# if not out_vid.isOpened():
|
|
215
215
|
# raise ValueError("Failed to open video writer with 'avc1' (h264)")
|
|
216
216
|
# except Exception:
|
|
217
217
|
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
|
|
218
|
-
out_vid = cv2.VideoWriter(vid_output_path, fourcc, fps, (cam_width, cam_height))
|
|
218
|
+
out_vid = cv2.VideoWriter(str(vid_output_path.absolute()), fourcc, fps, (cam_width, cam_height))
|
|
219
219
|
# logging.info("Failed to open video writer with 'avc1' (h264). Using 'mp4v' instead.")
|
|
220
220
|
|
|
221
221
|
return cap, out_vid, cam_width, cam_height, fps
|
|
@@ -1195,6 +1195,15 @@ def best_coords_for_measurements(Q_coords, keypoints_names, fastest_frames_to_re
|
|
|
1195
1195
|
- Q_coords_low_speeds_low_angles: pd.DataFrame. The best coordinates for measurements
|
|
1196
1196
|
'''
|
|
1197
1197
|
|
|
1198
|
+
# Add Hip column if not present
|
|
1199
|
+
n_markers_init = len(keypoints_names)
|
|
1200
|
+
if 'Hip' not in keypoints_names:
|
|
1201
|
+
RHip_df = Q_coords.iloc[:,keypoints_names.index('RHip')*3:keypoints_names.index('RHip')*3+3]
|
|
1202
|
+
LHip_df = Q_coords.iloc[:,keypoints_names.index('LHip')*3:keypoints_names.index('RHip')*3+3]
|
|
1203
|
+
Hip_df = RHip_df.add(LHip_df, fill_value=0) /2
|
|
1204
|
+
Hip_df.columns = [col+ str(int(Q_coords.columns[-1][1:])+1) for col in ['X','Y','Z']]
|
|
1205
|
+
keypoints_names += ['Hip']
|
|
1206
|
+
Q_coords = pd.concat([Q_coords, Hip_df], axis=1)
|
|
1198
1207
|
n_markers = len(keypoints_names)
|
|
1199
1208
|
|
|
1200
1209
|
# Using 80% slowest frames
|
|
@@ -1210,6 +1219,9 @@ def best_coords_for_measurements(Q_coords, keypoints_names, fastest_frames_to_re
|
|
|
1210
1219
|
if len(Q_coords_low_speeds_low_angles) < 50:
|
|
1211
1220
|
Q_coords_low_speeds_low_angles = Q_coords_low_speeds.iloc[pd.Series(ang_mean).nsmallest(50).index]
|
|
1212
1221
|
|
|
1222
|
+
if n_markers_init < n_markers:
|
|
1223
|
+
Q_coords_low_speeds_low_angles = Q_coords_low_speeds_low_angles.iloc[:,:-3]
|
|
1224
|
+
|
|
1213
1225
|
return Q_coords_low_speeds_low_angles
|
|
1214
1226
|
|
|
1215
1227
|
|
|
@@ -1404,6 +1416,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1404
1416
|
gaussian_filter_kernel = config_dict.get('post-processing').get('gaussian').get('sigma_kernel')
|
|
1405
1417
|
loess_filter_kernel = config_dict.get('post-processing').get('loess').get('nb_values_used')
|
|
1406
1418
|
median_filter_kernel = config_dict.get('post-processing').get('median').get('kernel_size')
|
|
1419
|
+
butterworth_filter_cutoff /= slowmo_factor
|
|
1407
1420
|
filter_options = [do_filter, filter_type,
|
|
1408
1421
|
butterworth_filter_order, butterworth_filter_cutoff, frame_rate,
|
|
1409
1422
|
gaussian_filter_kernel, loess_filter_kernel, median_filter_kernel]
|
|
@@ -1440,7 +1453,8 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1440
1453
|
logging.warning('Webcam input: the framerate may vary. If results are filtered, Sports2D will use the average framerate as input.')
|
|
1441
1454
|
else:
|
|
1442
1455
|
cap, out_vid, cam_width, cam_height, fps = setup_video(video_file_path, save_vid, vid_output_path)
|
|
1443
|
-
|
|
1456
|
+
start_time = get_start_time_ffmpeg(video_file_path)
|
|
1457
|
+
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))]
|
|
1444
1458
|
frame_iterator = tqdm(range(*frame_range)) # use a progress bar
|
|
1445
1459
|
if show_realtime_results:
|
|
1446
1460
|
cv2.namedWindow(f'{video_file} Sports2D', cv2.WINDOW_NORMAL + cv2.WINDOW_KEEPRATIO)
|
|
@@ -1486,6 +1500,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
|
|
|
1486
1500
|
frame_processing_times = []
|
|
1487
1501
|
frame_count = 0
|
|
1488
1502
|
while cap.isOpened():
|
|
1503
|
+
# Skip to the starting frame
|
|
1489
1504
|
if frame_count < frame_range[0]:
|
|
1490
1505
|
cap.read()
|
|
1491
1506
|
frame_count += 1
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Sports2D/Sports2D.py,sha256=dZv4xglguFJZDu9Zv0AZKAGu1TQIW9ynmY8pMsNHw14,26377
|
|
2
|
+
Sports2D/__init__.py,sha256=TyCP7Uuuy6CNklhPf8W84MbYoO1_-1dxowSYAJyk_OI,102
|
|
3
|
+
Sports2D/process.py,sha256=6rFjME0pSJXZpYD9t7Ai8P5Ly2MVpmKgZAo912j24kM,87663
|
|
4
|
+
Sports2D/Demo/Config_demo.toml,sha256=kp2iqohOLlN3vzFBDgz69BB8kpaYqcGXDQpchlxeO9w,6769
|
|
5
|
+
Sports2D/Demo/demo.mp4,sha256=2aZkFxhWR7ESMEtXCT8MGA83p2jmoU2sp1ylQfO3gDk,3968304
|
|
6
|
+
Sports2D/Utilities/__init__.py,sha256=TyCP7Uuuy6CNklhPf8W84MbYoO1_-1dxowSYAJyk_OI,102
|
|
7
|
+
Sports2D/Utilities/common.py,sha256=FEWmlq9HNlHzA2ioV5MPPOeC-5Py4JaDbIIxQgq9hGE,14128
|
|
8
|
+
Sports2D/Utilities/filter.py,sha256=8mVefMjDzxmh9a30eNtIrUuK_mUKoOJ2Nr-OzcQKkKM,4922
|
|
9
|
+
Sports2D/Utilities/skeletons.py,sha256=44IWpz47zjh_6YDqkwaJnSysaGi7ovgYE25ji-hC-Kw,15660
|
|
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,,
|
sports2d-0.5.2.dist-info/RECORD
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
Sports2D/Sports2D.py,sha256=toqCATE3D3JDYKp_SnCUdQekW45F_KsIHK1vurDS6n8,26103
|
|
2
|
-
Sports2D/__init__.py,sha256=TyCP7Uuuy6CNklhPf8W84MbYoO1_-1dxowSYAJyk_OI,102
|
|
3
|
-
Sports2D/process.py,sha256=EQcLyjxyn-oKysZ3iCZ1eSvY8LITTuxXDKmfVTKRNZ4,86767
|
|
4
|
-
Sports2D/Demo/Config_demo.toml,sha256=03HL-4nMRVrSzL_NFRA6rjzSqirP6GfHjb1bAt8VeKU,6603
|
|
5
|
-
Sports2D/Demo/demo.mp4,sha256=2aZkFxhWR7ESMEtXCT8MGA83p2jmoU2sp1ylQfO3gDk,3968304
|
|
6
|
-
Sports2D/Utilities/__init__.py,sha256=TyCP7Uuuy6CNklhPf8W84MbYoO1_-1dxowSYAJyk_OI,102
|
|
7
|
-
Sports2D/Utilities/common.py,sha256=-LoJGs_nyNPcj6cQfVQI9X5mQmv7ibTcYUfaEwFN2uU,12880
|
|
8
|
-
Sports2D/Utilities/filter.py,sha256=8mVefMjDzxmh9a30eNtIrUuK_mUKoOJ2Nr-OzcQKkKM,4922
|
|
9
|
-
Sports2D/Utilities/skeletons.py,sha256=44IWpz47zjh_6YDqkwaJnSysaGi7ovgYE25ji-hC-Kw,15660
|
|
10
|
-
Sports2D/Utilities/tests.py,sha256=g06HBExGkvZrhZpNXN19G9Shisfgp1cqjAp0kFxiKEc,2574
|
|
11
|
-
sports2d-0.5.2.dist-info/LICENSE,sha256=f4qe3nE0Y7ltJho5w-xAR0jI5PUox5Xl-MsYiY7ZRM8,1521
|
|
12
|
-
sports2d-0.5.2.dist-info/METADATA,sha256=46UdMkJj9kmxHjsLaxBhyBo_ubE1cRZ8FDD0lFimfeA,23067
|
|
13
|
-
sports2d-0.5.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
14
|
-
sports2d-0.5.2.dist-info/entry_points.txt,sha256=h2CJTuydtNf8JyaLoWxWl5HTSIxx5Ra_FSiSGQsf7Sk,52
|
|
15
|
-
sports2d-0.5.2.dist-info/top_level.txt,sha256=DoURf9UDB8lQ_9lMUPQMQqhXCvWPFFjJco9NzPlHJ6I,9
|
|
16
|
-
sports2d-0.5.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|