Skip to content

Commit 0fcd37e

Browse files
committed
- Took slowmo into account for filtering
- start time sometimes randomly not zero: used ffmpeg to determine it instead of cv2 - solved wrong coord padding whe person disappears from frame
1 parent ad2e55f commit 0fcd37e

File tree

4 files changed

+35
-9
lines changed

4 files changed

+35
-9
lines changed

Sports2D/Demo/Config_demo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ show_graphs = true # Show plots of raw and processed results
102102
filter_type = 'butterworth' # butterworth, gaussian, LOESS, median
103103
[post-processing.butterworth]
104104
order = 4
105-
cut_off_frequency = 3 # Hz
105+
cut_off_frequency = 6 # Hz # Will be divided by slowmo_factor to be equivalent to non slowed-down video
106106
[post-processing.gaussian]
107107
sigma_kernel = 1 #px
108108
[post-processing.loess]

Sports2D/Sports2D.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@
194194
'filter': True,
195195
'show_graphs': True,
196196
'filter_type': 'butterworth',
197-
'butterworth': {'order': 4, 'cut_off_frequency': 3},
197+
'butterworth': {'order': 4, 'cut_off_frequency': 6},
198198
'gaussian': {'sigma_kernel': 1},
199199
'loess': {'nb_values_used': 5},
200200
'median': {'kernel_size': 3}

Sports2D/Utilities/common.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ def make_homogeneous(list_of_arrays):
164164
'''
165165

166166
def get_max_shape(list_of_arrays):
167+
'''
168+
Recursively determine the maximum shape of a list of arrays.
169+
'''
167170
if isinstance(list_of_arrays[0], list):
168171
# Maximum length at the current level plus the max shape at the next level
169172
return [max(len(arr) for arr in list_of_arrays)] + get_max_shape(
@@ -176,12 +179,16 @@ def pad_with_nans(list_of_arrays, target_shape):
176179
'''
177180
Recursively pad list_of_arrays with nans to match the target shape.
178181
'''
179-
if isinstance(list_of_arrays, np.ndarray):
180-
if list_of_arrays.size == 0: # Handle empty arrays
181-
# Return an array of nans with the correct target shape
182-
return np.full(target_shape, np.nan, dtype=float)
183-
# Pad the current array to the target shape
184-
pad_width = [(0, max_dim - curr_dim) for curr_dim, max_dim in zip(list_of_arrays.shape, target_shape)]
182+
if isinstance(list_of_arrays, np.ndarray):
183+
# Pad the current array to the target shape
184+
pad_width = []
185+
for dim_index in range(0, len(target_shape)):
186+
if dim_index == len(list_of_arrays.shape) or dim_index > len(list_of_arrays.shape):
187+
list_of_arrays = np.expand_dims(list_of_arrays, 0)
188+
for dim_index in range(0, len(target_shape)):
189+
max_dim = target_shape[dim_index]
190+
curr_dim = list_of_arrays.shape[dim_index]
191+
pad_width.append((0, max_dim - curr_dim))
185192
return np.pad(list_of_arrays.astype(float), pad_width, constant_values=np.nan)
186193
# Recursively pad each array in the list
187194
return [pad_with_nans(array, target_shape[1:]) for array in list_of_arrays]
@@ -194,6 +201,22 @@ def pad_with_nans(list_of_arrays, target_shape):
194201
return np.array(list_of_arrays)
195202

196203

204+
def get_start_time_ffmpeg(video_path):
205+
'''
206+
Get the start time of a video using FFmpeg.
207+
'''
208+
209+
cmd = ["ffmpeg", "-i", video_path]
210+
result = subprocess.run(cmd, stderr=subprocess.PIPE, stdout=subprocess.DEVNULL, text=True)
211+
for line in result.stderr.splitlines():
212+
if "start:" in line:
213+
parts = line.split("start:")
214+
if len(parts) > 1:
215+
start_time = parts[1].split(",")[0].strip()
216+
return float(start_time)
217+
return 0.0 # Default to 0 if not found
218+
219+
197220
def resample_video(vid_output_path, fps, desired_framerate):
198221
'''
199222
Resample video to the desired fps using ffmpeg.

Sports2D/process.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1416,6 +1416,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
14161416
gaussian_filter_kernel = config_dict.get('post-processing').get('gaussian').get('sigma_kernel')
14171417
loess_filter_kernel = config_dict.get('post-processing').get('loess').get('nb_values_used')
14181418
median_filter_kernel = config_dict.get('post-processing').get('median').get('kernel_size')
1419+
butterworth_filter_cutoff /= slowmo_factor
14191420
filter_options = [do_filter, filter_type,
14201421
butterworth_filter_order, butterworth_filter_cutoff, frame_rate,
14211422
gaussian_filter_kernel, loess_filter_kernel, median_filter_kernel]
@@ -1452,7 +1453,8 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
14521453
logging.warning('Webcam input: the framerate may vary. If results are filtered, Sports2D will use the average framerate as input.')
14531454
else:
14541455
cap, out_vid, cam_width, cam_height, fps = setup_video(video_file_path, save_vid, vid_output_path)
1455-
frame_range = [int(time_range[0] * frame_rate), int(time_range[1] * frame_rate)] if time_range else [0, int(cap.get(cv2.CAP_PROP_FRAME_COUNT))]
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))]
14561458
frame_iterator = tqdm(range(*frame_range)) # use a progress bar
14571459
if show_realtime_results:
14581460
cv2.namedWindow(f'{video_file} Sports2D', cv2.WINDOW_NORMAL + cv2.WINDOW_KEEPRATIO)
@@ -1498,6 +1500,7 @@ def process_fun(config_dict, video_file, time_range, frame_rate, result_dir):
14981500
frame_processing_times = []
14991501
frame_count = 0
15001502
while cap.isOpened():
1503+
# Skip to the starting frame
15011504
if frame_count < frame_range[0]:
15021505
cap.read()
15031506
frame_count += 1

0 commit comments

Comments
 (0)