simba-uw-tf-dev 4.7.1__py3-none-any.whl → 4.7.3__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.
Potentially problematic release.
This version of simba-uw-tf-dev might be problematic. Click here for more details.
- simba/SimBA.py +1178 -1171
- simba/assets/lookups/yolo_schematics/yolo_mitra.csv +9 -0
- simba/mixins/train_model_mixin.py +1 -4
- simba/model/inference_batch.py +1 -1
- simba/model/yolo_fit.py +22 -15
- simba/model/yolo_pose_inference.py +7 -2
- simba/sandbox/convert_h264_to_mp4_lossless.py +129 -0
- simba/sandbox/extract_and_convert_videos.py +257 -0
- simba/third_party_label_appenders/transform/simba_to_yolo.py +8 -5
- simba/ui/pop_ups/run_machine_models_popup.py +22 -22
- simba/ui/pop_ups/simba_to_yolo_keypoints_popup.py +2 -2
- simba/ui/pop_ups/yolo_inference_popup.py +1 -1
- simba/ui/pop_ups/yolo_pose_train_popup.py +1 -1
- simba/utils/lookups.py +67 -1
- simba/video_processors/video_processing.py +97 -39
- {simba_uw_tf_dev-4.7.1.dist-info → simba_uw_tf_dev-4.7.3.dist-info}/METADATA +1 -1
- {simba_uw_tf_dev-4.7.1.dist-info → simba_uw_tf_dev-4.7.3.dist-info}/RECORD +21 -18
- {simba_uw_tf_dev-4.7.1.dist-info → simba_uw_tf_dev-4.7.3.dist-info}/LICENSE +0 -0
- {simba_uw_tf_dev-4.7.1.dist-info → simba_uw_tf_dev-4.7.3.dist-info}/WHEEL +0 -0
- {simba_uw_tf_dev-4.7.1.dist-info → simba_uw_tf_dev-4.7.3.dist-info}/entry_points.txt +0 -0
- {simba_uw_tf_dev-4.7.1.dist-info → simba_uw_tf_dev-4.7.3.dist-info}/top_level.txt +0 -0
simba/utils/lookups.py
CHANGED
|
@@ -1201,4 +1201,70 @@ def check_for_updates(time_out: int = 2):
|
|
|
1201
1201
|
else:
|
|
1202
1202
|
msg = (f'NEW SimBA VERSION AVAILABLE. \nYou have SimBA version {env_simba_version}. \nThe latest version is {latest_simba_version}. '
|
|
1203
1203
|
f'\nYou can update using "pip install simba-uw-tf-dev --upgrade"')
|
|
1204
|
-
stdout_information(msg=msg, source=check_for_updates.__name__)
|
|
1204
|
+
stdout_information(msg=msg, source=check_for_updates.__name__)
|
|
1205
|
+
|
|
1206
|
+
|
|
1207
|
+
def get_ext_codec_map() -> Dict[str, str]:
|
|
1208
|
+
"""
|
|
1209
|
+
Get a dictionary mapping video file extensions to their recommended FFmpeg codecs.
|
|
1210
|
+
Automatically falls back to alternative codecs if the preferred codec is not available.
|
|
1211
|
+
|
|
1212
|
+
:return: Dictionary mapping file extensions (without leading dot) to codec names.
|
|
1213
|
+
:rtype: Dict[str, str]
|
|
1214
|
+
|
|
1215
|
+
:example:
|
|
1216
|
+
>>> codec_map = get_ext_codec_map()
|
|
1217
|
+
>>> codec = codec_map.get('webm', 'libx264') # Returns 'libvpx-vp9' or fallback
|
|
1218
|
+
"""
|
|
1219
|
+
codecs_available = get_ffmpeg_encoders(raise_error=False)
|
|
1220
|
+
if not codecs_available: codecs_available = []
|
|
1221
|
+
|
|
1222
|
+
common_codecs = ['libx264', 'mpeg4', 'h264', 'mjpeg', 'libx265']
|
|
1223
|
+
fallback_codec = None
|
|
1224
|
+
for codec in common_codecs:
|
|
1225
|
+
if codec in codecs_available:
|
|
1226
|
+
fallback_codec = codec
|
|
1227
|
+
break
|
|
1228
|
+
|
|
1229
|
+
# If no common codec found, use first available or default to mpeg4 (most universal)
|
|
1230
|
+
if fallback_codec is None:
|
|
1231
|
+
fallback_codec = codecs_available[0] if codecs_available else 'mpeg4'
|
|
1232
|
+
|
|
1233
|
+
def get_codec(preferred: str, alternative: str = None) -> str:
|
|
1234
|
+
if preferred in codecs_available:
|
|
1235
|
+
return preferred
|
|
1236
|
+
alt = alternative if alternative else fallback_codec
|
|
1237
|
+
return alt if alt in codecs_available else preferred
|
|
1238
|
+
|
|
1239
|
+
return {
|
|
1240
|
+
'webm': get_codec(preferred='libvpx-vp9', alternative='libvpx'),
|
|
1241
|
+
'avi': get_codec(preferred='mpeg4', alternative='libx264'),
|
|
1242
|
+
'mp4': get_codec(preferred='libx264', alternative='mpeg4'),
|
|
1243
|
+
'mov': get_codec(preferred='libx264', alternative='mpeg4'),
|
|
1244
|
+
'mkv': get_codec(preferred='libx264', alternative='mpeg4'),
|
|
1245
|
+
'flv': get_codec(preferred='libx264', alternative='mpeg4'),
|
|
1246
|
+
'm4v': get_codec(preferred='libx264', alternative='mpeg4'),
|
|
1247
|
+
'h264': get_codec(preferred='libx264', alternative='h264'),
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
def get_ffmpeg_codec(file_name: Union[str, os.PathLike],
|
|
1251
|
+
fallback: str = 'mpeg4') -> str:
|
|
1252
|
+
"""
|
|
1253
|
+
Get the recommended FFmpeg codec for a video file based on its extension.
|
|
1254
|
+
|
|
1255
|
+
:param Union[str, os.PathLike] file_name: Path to video file or file extension.
|
|
1256
|
+
:param str fallback: Codec to return if file extension is not recognized. Default: 'mpeg4'.
|
|
1257
|
+
:return: Recommended FFmpeg codec name for the video file.
|
|
1258
|
+
:rtype: str
|
|
1259
|
+
|
|
1260
|
+
:example:
|
|
1261
|
+
>>> codec = get_ffmpeg_codec(file_name='video.mp4')
|
|
1262
|
+
>>> codec = get_ffmpeg_codec(file_name='video.webm', fallback='libx264')
|
|
1263
|
+
>>> codec = get_ffmpeg_codec(file_name=r'C:/videos/my_video.avi')
|
|
1264
|
+
"""
|
|
1265
|
+
codec_map = get_ext_codec_map()
|
|
1266
|
+
_, file_name, ext = get_fn_ext(filepath=file_name)
|
|
1267
|
+
if ext[1:] in codec_map.keys():
|
|
1268
|
+
return codec_map[ext[1:]]
|
|
1269
|
+
else:
|
|
1270
|
+
return fallback
|
|
@@ -56,8 +56,8 @@ from simba.utils.lookups import (get_current_time,
|
|
|
56
56
|
get_ffmpeg_crossfade_methods, get_fonts,
|
|
57
57
|
get_named_colors, percent_to_crf_lookup,
|
|
58
58
|
percent_to_qv_lk, quality_pct_to_crf,
|
|
59
|
-
video_quality_to_preset_lookup)
|
|
60
|
-
from simba.utils.printing import SimbaTimer, stdout_success
|
|
59
|
+
video_quality_to_preset_lookup, get_ffmpeg_codec)
|
|
60
|
+
from simba.utils.printing import SimbaTimer, stdout_success, stdout_information
|
|
61
61
|
from simba.utils.read_write import (
|
|
62
62
|
check_if_hhmmss_timestamp_is_valid_part_of_video,
|
|
63
63
|
concatenate_videos_in_folder, create_directory,
|
|
@@ -704,12 +704,9 @@ def change_single_video_fps(file_path: Union[str, os.PathLike],
|
|
|
704
704
|
quality = 23 if not check_int(name='quality', value=quality, min_value=0, max_value=52, raise_error=False)[0] else int(quality)
|
|
705
705
|
if verbose: print(f"Converting the FPS {video_meta_data['fps']} -> {fps} for video {file_name} ...")
|
|
706
706
|
if codec is None:
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
codec = 'mpeg4'
|
|
711
|
-
else:
|
|
712
|
-
codec = 'libx264'
|
|
707
|
+
codec = get_ffmpeg_codec(file_name=file_path)
|
|
708
|
+
else:
|
|
709
|
+
check_valid_codec(codec=codec, raise_error=True, source=change_single_video_fps.__name__)
|
|
713
710
|
if os.path.isfile(save_path):
|
|
714
711
|
FileExistWarning(msg=f"Overwriting existing file at {save_path}...", source=change_single_video_fps.__name__,)
|
|
715
712
|
if gpu:
|
|
@@ -742,6 +739,9 @@ def change_fps_of_multiple_videos(path: Union[str, os.PathLike, List[Union[str,
|
|
|
742
739
|
:param Optional[bool] gpu: If True, use NVIDEA GPU codecs. Default False.
|
|
743
740
|
:param bool verbose: If True, prints conversion progress. Default True.
|
|
744
741
|
:returns: None.
|
|
742
|
+
|
|
743
|
+
.. note::
|
|
744
|
+
Codec is automatically selected based on file extension: libvpx-vp9 for .webm, mpeg4 for .avi, libx264 for others.
|
|
745
745
|
|
|
746
746
|
:example:
|
|
747
747
|
>>> _ = change_fps_of_multiple_videos(path='project_folder/videos/Video_1.mp4', fps=15)
|
|
@@ -800,13 +800,17 @@ def change_fps_of_multiple_videos(path: Union[str, os.PathLike, List[Union[str,
|
|
|
800
800
|
if verbose: stdout_success(msg=f"SIMBA COMPLETE: FPS of {len(video_paths)} video(s) changed to {fps}", elapsed_time=timer.elapsed_time_str, source=change_fps_of_multiple_videos.__name__,)
|
|
801
801
|
|
|
802
802
|
|
|
803
|
-
def convert_video_powerpoint_compatible_format(file_path: Union[str, os.PathLike],
|
|
803
|
+
def convert_video_powerpoint_compatible_format(file_path: Union[str, os.PathLike],
|
|
804
|
+
gpu: Optional[bool] = False) -> None:
|
|
804
805
|
"""
|
|
805
806
|
Create MS PowerPoint compatible copy of a video file.
|
|
806
807
|
|
|
807
808
|
:param Union[str, os.PathLike] file_path: Path to video file.
|
|
808
809
|
:param Optional[bool] gpu: If True, use NVIDEA GPU codecs. Default False.
|
|
809
810
|
:returns: None. The result is stored in the same directory as the input file with the ``_powerpointready`` suffix.
|
|
811
|
+
|
|
812
|
+
.. note::
|
|
813
|
+
Codec is automatically selected: libx264 for CPU encoding (ignored if gpu=True).
|
|
810
814
|
|
|
811
815
|
:example:
|
|
812
816
|
>>> _ = convert_video_powerpoint_compatible_format(file_path='project_folder/videos/Video_1.mp4')
|
|
@@ -846,7 +850,7 @@ def convert_video_powerpoint_compatible_format(file_path: Union[str, os.PathLike
|
|
|
846
850
|
|
|
847
851
|
def video_to_greyscale(file_path: Union[str, os.PathLike],
|
|
848
852
|
gpu: Optional[bool] = False,
|
|
849
|
-
codec: str =
|
|
853
|
+
codec: Optional[str] = None,
|
|
850
854
|
verbose: bool = True,
|
|
851
855
|
quality: int = 23,
|
|
852
856
|
save_path: Optional[Union[str, os.PathLike]] = None) -> None:
|
|
@@ -890,6 +894,10 @@ def video_to_greyscale(file_path: Union[str, os.PathLike],
|
|
|
890
894
|
check_if_dir_exists(in_dir=os.path.dirname(save_path))
|
|
891
895
|
save_name = deepcopy(save_path)
|
|
892
896
|
quality = 23 if not check_int(name='quality', value=quality, min_value=0, max_value=52, raise_error=False)[0] else int(quality)
|
|
897
|
+
if codec is None:
|
|
898
|
+
codec = get_ffmpeg_codec(file_name=file_path)
|
|
899
|
+
else:
|
|
900
|
+
check_valid_codec(codec=codec, raise_error=True, source=video_to_greyscale.__name__)
|
|
893
901
|
if gpu:
|
|
894
902
|
cmd = f'ffmpeg -hwaccel auto -c:v h264_cuvid -i "{file_path}" -vf "hwupload_cuda, hwdownload, format=nv12, format=gray" -c:v h264_nvenc -rc vbr -cq {quality} -c:a copy "{save_name}" -loglevel error -stats -hide_banner -y'
|
|
895
903
|
else:
|
|
@@ -924,6 +932,9 @@ def batch_video_to_greyscale(path: Union[str, os.PathLike, List[Union[str, os.Pa
|
|
|
924
932
|
:param Optional[bool] gpu: If True, use NVIDEA GPU codecs. Default False.
|
|
925
933
|
:raise FFMPEGCodecGPUError: If no GPU is found and ``gpu == True``.
|
|
926
934
|
:returns: None.
|
|
935
|
+
|
|
936
|
+
.. note::
|
|
937
|
+
Codec is automatically selected: libx264 for CPU encoding (ignored if gpu=True).
|
|
927
938
|
|
|
928
939
|
:example:
|
|
929
940
|
>>> _ = batch_video_to_greyscale(path='/Users/simon/Desktop/envs/simba/troubleshooting/mouse_open_field/project_folder/videos/test_2')
|
|
@@ -971,7 +982,7 @@ def superimpose_frame_count(file_path: Union[str, os.PathLike],
|
|
|
971
982
|
font: Optional[str] = 'Arial',
|
|
972
983
|
font_color: Optional[str] = 'black',
|
|
973
984
|
bg_color: Optional[str] = 'white',
|
|
974
|
-
codec: Optional[str] =
|
|
985
|
+
codec: Optional[str] = None,
|
|
975
986
|
quality: Optional[int] = None,
|
|
976
987
|
verbose: bool = True,
|
|
977
988
|
save_path: Optional[Union[str, os.PathLike]] = None,
|
|
@@ -1037,11 +1048,13 @@ def superimpose_frame_count(file_path: Union[str, os.PathLike],
|
|
|
1037
1048
|
print(len(file_paths), file_path)
|
|
1038
1049
|
else:
|
|
1039
1050
|
raise InvalidInputError(msg=f'{file_path} is not a valid file path or file directory.', source=superimpose_frame_count.__name__)
|
|
1040
|
-
|
|
1051
|
+
if codec is not None: check_valid_codec(codec=codec, raise_error=True, source=superimpose_frame_count.__name__)
|
|
1041
1052
|
quality = 23 if not check_int(name='quality', value=quality, min_value=0, max_value=52, raise_error=False)[0] else int(quality)
|
|
1042
1053
|
for video_cnt, file_path in enumerate(file_paths):
|
|
1043
1054
|
dir, file_name, ext = get_fn_ext(filepath=file_path)
|
|
1044
|
-
if verbose:
|
|
1055
|
+
if verbose:
|
|
1056
|
+
stdout_information(msg=f'Superimposing frame count video {video_cnt+1}/{len(file_paths)}...', source=change_single_video_fps.__name__)
|
|
1057
|
+
codec = get_ffmpeg_codec(file_name=file_path) if codec is None else codec
|
|
1045
1058
|
if save_path is None:
|
|
1046
1059
|
save_name = os.path.join(dir, f"{file_name}_frame_no.mp4")
|
|
1047
1060
|
elif os.path.isdir(save_path):
|
|
@@ -1094,6 +1107,9 @@ def remove_beginning_of_video(file_path: Union[str, os.PathLike],
|
|
|
1094
1107
|
:param Optional[Union[str, os.PathLike]] save_path: Optional save location for the shortened video. If None, then the new video is saved in the same directory as the input video with the ``_shortened`` suffix.
|
|
1095
1108
|
:param Optional[bool] gpu: If True, use NVIDEA GPU codecs. Default False.
|
|
1096
1109
|
:returns: None. If save_path is not passed, the result is stored in the same directory as the input file with the ``_shorten.mp4`` suffix.
|
|
1110
|
+
|
|
1111
|
+
.. note::
|
|
1112
|
+
Codec is automatically selected: libx264 for CPU encoding (ignored if gpu=True).
|
|
1097
1113
|
|
|
1098
1114
|
:example:
|
|
1099
1115
|
>>> _ = remove_beginning_of_video(file_path='project_folder/videos/Video_1.avi', time=10)
|
|
@@ -1133,7 +1149,7 @@ def clip_video_in_range(file_path: Union[str, os.PathLike],
|
|
|
1133
1149
|
end_time: str,
|
|
1134
1150
|
out_dir: Optional[Union[str, os.PathLike]] = None,
|
|
1135
1151
|
save_path: Optional[Union[str, os.PathLike]] = None,
|
|
1136
|
-
codec: str =
|
|
1152
|
+
codec: Optional[str] = None,
|
|
1137
1153
|
quality: int = 60,
|
|
1138
1154
|
verbose: bool = True,
|
|
1139
1155
|
overwrite: Optional[bool] = False,
|
|
@@ -1147,7 +1163,7 @@ def clip_video_in_range(file_path: Union[str, os.PathLike],
|
|
|
1147
1163
|
:param str end_time: End time in HH:MM:SS format.
|
|
1148
1164
|
:param Optional[Union[str, os.PathLike]] out_dir: If None, then the clip will be stored in the same dir as the input video. If directory, then the location of the output files.
|
|
1149
1165
|
:param Optional[Union[str, os.PathLike]] save_path: Optional save path for the clipped video. If provided, overrides out_dir and filename generation. Default None.
|
|
1150
|
-
:param str codec: Video codec to use for CPU encoding.
|
|
1166
|
+
:param Optional[str] codec: Video codec to use for CPU encoding. If None, automatically selects based on file extension (libvpx-vp9 for .webm, mpeg4 for .avi, libx264 for others). Default None. Ignored if gpu=True.
|
|
1151
1167
|
:param int quality: Video quality percentage (0-100). Higher values = higher quality. Default 60.
|
|
1152
1168
|
:param bool verbose: If True, prints conversion progress. Default True.
|
|
1153
1169
|
:param Optional[bool] overwrite: If True, overwrite output file if path already exists. If False, then raise FileExistError. Default False.
|
|
@@ -1176,7 +1192,10 @@ def clip_video_in_range(file_path: Union[str, os.PathLike],
|
|
|
1176
1192
|
check_if_hhmmss_timestamp_is_valid_part_of_video(timestamp=end_time, video_path=file_path)
|
|
1177
1193
|
quality = 60 if not check_int(name='quality', value=quality, min_value=0, max_value=100, raise_error=False)[0] else int(quality)
|
|
1178
1194
|
quality_crf = quality_pct_to_crf(pct=quality)
|
|
1179
|
-
|
|
1195
|
+
if codec is None:
|
|
1196
|
+
codec = get_ffmpeg_codec(file_name=file_path)
|
|
1197
|
+
else:
|
|
1198
|
+
check_valid_codec(codec=codec, raise_error=True, source=clip_video_in_range.__name__)
|
|
1180
1199
|
if not include_clip_time_in_filename:
|
|
1181
1200
|
save_name = os.path.join(dir, file_name + "_clipped.mp4")
|
|
1182
1201
|
else:
|
|
@@ -1200,7 +1219,7 @@ def downsample_video(file_path: Union[str, os.PathLike],
|
|
|
1200
1219
|
video_height: int,
|
|
1201
1220
|
video_width: int,
|
|
1202
1221
|
gpu: bool = False,
|
|
1203
|
-
codec: str =
|
|
1222
|
+
codec: Optional[str] = None,
|
|
1204
1223
|
quality: int = 23,
|
|
1205
1224
|
save_path: Optional[Union[str, os.PathLike]] = None,
|
|
1206
1225
|
verbose: bool = True) -> None:
|
|
@@ -1240,6 +1259,10 @@ def downsample_video(file_path: Union[str, os.PathLike],
|
|
|
1240
1259
|
if os.path.isfile(save_name):
|
|
1241
1260
|
raise FileExistError("SIMBA ERROR: The outfile file already exist: {}.".format(save_name), source=downsample_video.__name__)
|
|
1242
1261
|
quality = 23 if not check_int(name=f'{downsample_video.__name__} quality', value=quality, min_value=0, max_value=52, raise_error=False)[0] else int(quality)
|
|
1262
|
+
if codec is None:
|
|
1263
|
+
codec = get_ffmpeg_codec(file_name=file_path)
|
|
1264
|
+
else:
|
|
1265
|
+
check_valid_codec(codec=codec, raise_error=True, source=downsample_video.__name__)
|
|
1243
1266
|
if gpu:
|
|
1244
1267
|
command = f'ffmpeg -y -hwaccel auto -c:v h264_cuvid -i "{file_path}" -vf "scale=w={video_width}:h={video_height}" -c:v h264_nvenc -rc vbr -cq {quality} "{save_name}" -hide_banner -loglevel error -stats -y'
|
|
1245
1268
|
#command = f'ffmpeg -y -hwaccel auto -c:v h264_cuvid -i "{file_path}" -vf "scale_cuda=w={video_width}:h={video_height}:force_original_aspect_ratio=decrease:flags=bicubic" -c:v h264_nvenc -rc vbr -cq {quality} "{save_name}" -loglevel error -stats -hide_banner -y'
|
|
@@ -1332,6 +1355,9 @@ def batch_convert_video_format(directory: Union[str, os.PathLike],
|
|
|
1332
1355
|
:parameter str output_format: Format of the output files (e.g., mp4).
|
|
1333
1356
|
:parameter Optional[bool] gpu: If True, use NVIDEA GPU codecs. Default False.
|
|
1334
1357
|
:returns: None. The results are stored in the same directory as the input files.
|
|
1358
|
+
|
|
1359
|
+
.. note::
|
|
1360
|
+
Codec is automatically selected: libx264 for CPU encoding (ignored if gpu=True).
|
|
1335
1361
|
|
|
1336
1362
|
:example:
|
|
1337
1363
|
>>> _ = batch_convert_video_format(directory='project_folder/videos', input_format='avi', output_format='mp4')
|
|
@@ -1487,6 +1513,7 @@ def multi_split_video(file_path: Union[str, os.PathLike],
|
|
|
1487
1513
|
end_times: List[str],
|
|
1488
1514
|
out_dir: Optional[Union[str, os.PathLike]] = None,
|
|
1489
1515
|
quality: Optional[int] = None,
|
|
1516
|
+
codec: Optional[str] = None,
|
|
1490
1517
|
include_clip_time_in_filename: Optional[bool] = False,
|
|
1491
1518
|
gpu: Optional[bool] = False) -> None:
|
|
1492
1519
|
"""
|
|
@@ -1499,6 +1526,7 @@ def multi_split_video(file_path: Union[str, os.PathLike],
|
|
|
1499
1526
|
:param Optional[int] quality: Video quality (CRF value). Lower values = higher quality. Range 0-52. If None, defaults to 23. Default None.
|
|
1500
1527
|
:param Optional[bool] include_clip_time_in_filename: If True, include the clip start and end in HH-MM-SS format as suffix in the filename. If False, then use integer suffic representing the count.
|
|
1501
1528
|
:param Optional[bool] gpu: If True, use NVIDEA GPU codecs. Default False.
|
|
1529
|
+
:param Optional[str] codec: Video codec to use for CPU encoding. If None, automatically selects based on file extension (libvpx-vp9 for .webm, mpeg4 for .avi, libx264 for others). Default None. Ignored if gpu=True.
|
|
1502
1530
|
:returns: None.
|
|
1503
1531
|
|
|
1504
1532
|
:example:
|
|
@@ -1510,6 +1538,8 @@ def multi_split_video(file_path: Union[str, os.PathLike],
|
|
|
1510
1538
|
check_file_exist_and_readable(file_path=file_path)
|
|
1511
1539
|
dir_name, file_name, ext = get_fn_ext(filepath=file_path)
|
|
1512
1540
|
quality = 23 if not check_int(name=f'{multi_split_video.__name__} quality', value=quality, min_value=0, max_value=52, raise_error=False)[0] else int(quality)
|
|
1541
|
+
if codec is not None: check_valid_codec(codec=codec, raise_error=True, source=multi_split_video.__name__)
|
|
1542
|
+
codec = get_ffmpeg_codec(file_name=file_name) if codec is None else codec
|
|
1513
1543
|
if out_dir is not None:
|
|
1514
1544
|
if not os.path.isdir(out_dir):
|
|
1515
1545
|
os.makedirs(out_dir)
|
|
@@ -1555,7 +1585,7 @@ def multi_split_video(file_path: Union[str, os.PathLike],
|
|
|
1555
1585
|
)
|
|
1556
1586
|
command = f'ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i "{file_path}" -ss {start_time} -to {end_time} -c:v h264_nvenc -rc vbr -cq {quality} "{save_path}" -hide_banner -loglevel error -stats -y'
|
|
1557
1587
|
else:
|
|
1558
|
-
command = f'ffmpeg -i "{file_path}" -ss {start_time} -to {end_time} -async 1 -crf {quality} "{save_path}" -loglevel error -stats -hide_banner -y'
|
|
1588
|
+
command = f'ffmpeg -i "{file_path}" -ss {start_time} -to {end_time} -c:v {codec} -async 1 -crf {quality} "{save_path}" -loglevel error -stats -hide_banner -y'
|
|
1559
1589
|
clip_timer = SimbaTimer(start=True)
|
|
1560
1590
|
subprocess.call(command, shell=True, stdout=subprocess.PIPE)
|
|
1561
1591
|
clip_timer.stop_timer()
|
|
@@ -1623,6 +1653,7 @@ def crop_single_video(file_path: Union[str, os.PathLike],
|
|
|
1623
1653
|
def crop_multiple_videos(directory_path: Union[str, os.PathLike],
|
|
1624
1654
|
output_path: Union[str, os.PathLike],
|
|
1625
1655
|
gpu: Optional[bool] = False,
|
|
1656
|
+
codec: Optional[str] = None,
|
|
1626
1657
|
quality: int = 60) -> None:
|
|
1627
1658
|
"""
|
|
1628
1659
|
Crop multiple videos in a folder according to crop-coordinates defined in the **first** video.
|
|
@@ -1636,6 +1667,8 @@ def crop_multiple_videos(directory_path: Union[str, os.PathLike],
|
|
|
1636
1667
|
:param Union[str, os.PathLike] directory_path: Directory containing input videos.
|
|
1637
1668
|
:param Union[str, os.PathLike] output_path: Directory where to save the cropped videos.
|
|
1638
1669
|
:param Optional[bool] gpu: If True, use NVIDEA GPU codecs. Default False.
|
|
1670
|
+
:param Optional[str] codec: Video codec to use for CPU encoding. If None, automatically selects based on file extension (libvpx-vp9 for .webm, mpeg4 for .avi, libx264 for others). Default None. Ignored if gpu=True.
|
|
1671
|
+
:param int quality: Video quality percentage (1-100). Higher values = higher quality. Default 60.
|
|
1639
1672
|
:returns: None. Results are stored in passed ``output_path``.
|
|
1640
1673
|
|
|
1641
1674
|
:example:
|
|
@@ -1661,15 +1694,18 @@ def crop_multiple_videos(directory_path: Union[str, os.PathLike],
|
|
|
1661
1694
|
if ((roi_selector.top_left[0] < 0) or (roi_selector.top_left[1] < 0) or (roi_selector.bottom_right[0] < 0) or (roi_selector.bottom_right[1] < 1)):
|
|
1662
1695
|
raise CountError(msg=f"CROP FAILED: Cannot use negative crop coordinates. Got top_left: {roi_selector.top_left}, bottom_right: {roi_selector.bottom_right}", source=crop_multiple_videos.__name__)
|
|
1663
1696
|
timer = SimbaTimer(start=True)
|
|
1697
|
+
if codec is not None: check_valid_codec(codec=codec, raise_error=True, source=change_single_video_fps.__name__)
|
|
1698
|
+
|
|
1664
1699
|
for file_cnt, file_path in enumerate(video_paths):
|
|
1665
1700
|
video_timer = SimbaTimer(start=True)
|
|
1666
1701
|
dir_name, file_name, ext = get_fn_ext(filepath=file_path)
|
|
1702
|
+
video_codec = Formats.BATCH_CODEC.value if codec is None else get_ffmpeg_codec(file_name=file_path)
|
|
1667
1703
|
print(f"Cropping video {file_name} ({file_cnt+1}/{len(video_paths)})...")
|
|
1668
1704
|
video_meta_data = get_video_meta_data(file_path)
|
|
1669
1705
|
if (roi_selector.bottom_right[0] > video_meta_data["width"]) or (roi_selector.bottom_right[1] > video_meta_data["height"]):
|
|
1670
1706
|
raise InvalidInputError(msg=f'Cannot crop video {file_name} of size {video_meta_data["resolution_str"]} at location top left: {roi_selector.top_left}, bottom right: {roi_selector.bottom_right}', source=crop_multiple_videos.__name__)
|
|
1671
1707
|
save_path = os.path.join(output_path, f"{file_name}_cropped.mp4")
|
|
1672
|
-
crop_video(video_path=file_path, save_path=save_path, size=(roi_selector.width, roi_selector.height), top_left=(roi_selector.top_left[0], roi_selector.top_left[1]), gpu=gpu, verbose=False, quality=quality)
|
|
1708
|
+
crop_video(video_path=file_path, save_path=save_path, size=(roi_selector.width, roi_selector.height), top_left=(roi_selector.top_left[0], roi_selector.top_left[1]), gpu=gpu, verbose=False, quality=quality, codec=video_codec)
|
|
1673
1709
|
video_timer.stop_timer()
|
|
1674
1710
|
print(f"Video {file_name} cropped (Video {file_cnt+1}/{len(video_paths)}, elapsed time: {video_timer.elapsed_time_str})")
|
|
1675
1711
|
timer.stop_timer()
|
|
@@ -1692,6 +1728,9 @@ def frames_to_movie(directory: Union[str, os.PathLike],
|
|
|
1692
1728
|
:param out_format: Format of the output video. One of: 'mp4', 'avi', 'webm'. Default is 'mp4'.
|
|
1693
1729
|
:param gpu: If True, use NVIDIA GPU codecs (if available). Default is False.
|
|
1694
1730
|
:return: None. Video is saved to disk.
|
|
1731
|
+
|
|
1732
|
+
.. note::
|
|
1733
|
+
Codec is automatically selected based on out_format: libvpx-vp9 for 'webm', mpeg4 for 'avi', libx264 for 'mp4' (ignored if gpu=True).
|
|
1695
1734
|
"""
|
|
1696
1735
|
|
|
1697
1736
|
import re
|
|
@@ -1776,6 +1815,9 @@ def video_concatenator(video_one_path: Union[str, os.PathLike],
|
|
|
1776
1815
|
:param Optional[int] quality: Integer (1-100) representing output video quality. Higher = better quality + bigger size. If None, defaults to 60. Default None.
|
|
1777
1816
|
:param Optional[bool] gpu: If True, use NVIDEA GPU codecs. Default False.
|
|
1778
1817
|
:returns: None. The video is stored in the same directory as the ``video_one_path`` using the video names concatenated as filename.
|
|
1818
|
+
|
|
1819
|
+
.. note::
|
|
1820
|
+
Codec is automatically selected: libx264 for CPU encoding (ignored if gpu=True).
|
|
1779
1821
|
|
|
1780
1822
|
:example:
|
|
1781
1823
|
>>> video_concatenator(video_one_path='project_folder/videos/Video_1.mp4', video_two_path='project_folder/videos/Video_2.mp4', resolution=800, horizontal=True)
|
|
@@ -2400,6 +2442,7 @@ def resize_videos_by_height(video_paths: List[Union[str, os.PathLike]],
|
|
|
2400
2442
|
gpu: Optional[bool] = False,
|
|
2401
2443
|
quality: Optional[int] = None,
|
|
2402
2444
|
suffix: Optional[str] = None,
|
|
2445
|
+
codec: Optional[str] = None,
|
|
2403
2446
|
verbose: Optional[bool] = True) -> Union[None, List[Union[None, str, os.PathLike]]]:
|
|
2404
2447
|
"""
|
|
2405
2448
|
Re-size a list of videos to a specified height while retaining their aspect ratios.
|
|
@@ -2442,10 +2485,13 @@ def resize_videos_by_height(video_paths: List[Union[str, os.PathLike]],
|
|
|
2442
2485
|
for i in video_paths:
|
|
2443
2486
|
video_heights.append(get_video_meta_data(video_path=i)["height"])
|
|
2444
2487
|
height = video_heights[int(height)]
|
|
2488
|
+
if codec is not None: check_valid_codec(codec=codec, raise_error=True, source=resize_videos_by_height.__name__)
|
|
2489
|
+
|
|
2445
2490
|
for cnt, video_path in enumerate(video_paths):
|
|
2446
2491
|
dir_name, video_name, ext = get_fn_ext(video_path)
|
|
2492
|
+
video_codec = get_ffmpeg_codec(file_name=video_path) if codec is None else codec
|
|
2447
2493
|
if verbose:
|
|
2448
|
-
|
|
2494
|
+
stdout_information(msg=f"Resizing height video {video_name} (Video {cnt+1}/{len(video_paths)})...", source=resize_videos_by_height.__name__)
|
|
2449
2495
|
if overwrite:
|
|
2450
2496
|
dt = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
2451
2497
|
save_path = os.path.join(dir_name, f"{video_name}_{dt}.mp4")
|
|
@@ -2459,7 +2505,7 @@ def resize_videos_by_height(video_paths: List[Union[str, os.PathLike]],
|
|
|
2459
2505
|
if gpu:
|
|
2460
2506
|
cmd = f'ffmpeg -y -hwaccel auto -c:v h264_cuvid -i "{video_path}" -vf scale_npp=-2:{height} -c:v h264_nvenc -rc vbr -cq {quality} "{save_path}" -hide_banner -loglevel error -y'
|
|
2461
2507
|
else:
|
|
2462
|
-
cmd = f'ffmpeg -y -i "{video_path}" -vf scale=-2:{height} -crf {quality} "{save_path}" -hide_banner -loglevel error -stats -y'
|
|
2508
|
+
cmd = f'ffmpeg -y -i "{video_path}" -vf scale=-2:{height} -c:v {video_codec} -crf {quality} "{save_path}" -hide_banner -loglevel error -stats -y'
|
|
2463
2509
|
subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
|
|
2464
2510
|
if overwrite:
|
|
2465
2511
|
shutil.copy(save_path, video_path)
|
|
@@ -2477,6 +2523,7 @@ def resize_videos_by_width(video_paths: List[Union[str, os.PathLike]],
|
|
|
2477
2523
|
overwrite: Optional[bool] = False,
|
|
2478
2524
|
save_dir: Optional[Union[str, os.PathLike]] = None,
|
|
2479
2525
|
gpu: Optional[bool] = False,
|
|
2526
|
+
codec: Optional[str] = None,
|
|
2480
2527
|
quality: Optional[int] = None,
|
|
2481
2528
|
suffix: Optional[str] = None,
|
|
2482
2529
|
verbose: Optional[bool] = True) -> Union[None, List[Union[None, str, os.PathLike]]]:
|
|
@@ -2527,12 +2574,12 @@ def resize_videos_by_width(video_paths: List[Union[str, os.PathLike]],
|
|
|
2527
2574
|
for i in video_paths:
|
|
2528
2575
|
video_widths.append(get_video_meta_data(video_path=i)["width"])
|
|
2529
2576
|
width = video_widths[int(width)]
|
|
2577
|
+
if codec is not None: check_valid_codec(codec=codec, raise_error=True, source=resize_videos_by_width.__name__)
|
|
2530
2578
|
for cnt, video_path in enumerate(video_paths):
|
|
2531
2579
|
dir_name, video_name, ext = get_fn_ext(video_path)
|
|
2580
|
+
video_codec = get_ffmpeg_codec(file_name=video_path) if codec is None else codec
|
|
2532
2581
|
if verbose:
|
|
2533
|
-
|
|
2534
|
-
f"Resizing width video {video_name} (Video {cnt+1}/{len(video_paths)})..."
|
|
2535
|
-
)
|
|
2582
|
+
stdout_information(msg=f"Resizing width video {video_name} (Video {cnt+1}/{len(video_paths)})...", source=resize_videos_by_height.__name__)
|
|
2536
2583
|
if overwrite:
|
|
2537
2584
|
dt = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
2538
2585
|
save_path = os.path.join(dir_name, f"{video_name}_{dt}.mp4")
|
|
@@ -2546,7 +2593,7 @@ def resize_videos_by_width(video_paths: List[Union[str, os.PathLike]],
|
|
|
2546
2593
|
if gpu:
|
|
2547
2594
|
cmd = f'ffmpeg -y -hwaccel auto -i "{video_path}" -vf "scale={width}:-2" -c:v h264_nvenc -rc vbr -cq {quality} "{save_path}" -hide_banner -loglevel error -stats -y'
|
|
2548
2595
|
else:
|
|
2549
|
-
cmd = f'ffmpeg -y -i "{video_path}" -vf scale={width}:-2 -crf {quality} "{save_path}" -hide_banner -loglevel error -stats -y'
|
|
2596
|
+
cmd = f'ffmpeg -y -i "{video_path}" -vf scale={width}:-2 -c:v {video_codec} -crf {quality} "{save_path}" -hide_banner -loglevel error -stats -y'
|
|
2550
2597
|
subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
|
|
2551
2598
|
if overwrite:
|
|
2552
2599
|
shutil.copy(save_path, video_path)
|
|
@@ -2685,7 +2732,6 @@ def vertical_video_concatenator(video_paths: List[Union[str, os.PathLike]],
|
|
|
2685
2732
|
:width: 300
|
|
2686
2733
|
:align: center
|
|
2687
2734
|
|
|
2688
|
-
|
|
2689
2735
|
.. seealso::
|
|
2690
2736
|
:func:`simba.video_processors.video_processing.horizontal_video_concatenator`
|
|
2691
2737
|
|
|
@@ -2991,6 +3037,7 @@ def clip_videos_by_frame_ids(file_paths: List[Union[str, os.PathLike]],
|
|
|
2991
3037
|
frm_ids: List[List[int]],
|
|
2992
3038
|
save_dir: Optional[Union[str, os.PathLike]] = None,
|
|
2993
3039
|
gpu: Optional[bool] = False,
|
|
3040
|
+
codec: Optional[str] = None,
|
|
2994
3041
|
quality: Optional[int] = None):
|
|
2995
3042
|
|
|
2996
3043
|
"""
|
|
@@ -3033,13 +3080,14 @@ def clip_videos_by_frame_ids(file_paths: List[Union[str, os.PathLike]],
|
|
|
3033
3080
|
video_timer = SimbaTimer(start=True)
|
|
3034
3081
|
dir, video_name, ext = get_fn_ext(filepath=file_path)
|
|
3035
3082
|
s_f, e_f = frm_ids[cnt][0], frm_ids[cnt][1]
|
|
3036
|
-
|
|
3083
|
+
stdout_information(msg=f"Trimming {video_name} from frame {s_f} to frame {e_f} (Video {cnt+1}/{len(file_paths)})...", source=clip_videos_by_frame_ids.__name__)
|
|
3084
|
+
video_codec = get_ffmpeg_codec(file_name=file_path) if codec is None else codec
|
|
3037
3085
|
if save_dir is not None:
|
|
3038
3086
|
out_path = os.path.join(save_dir, os.path.basename(file_path))
|
|
3039
3087
|
else:
|
|
3040
3088
|
out_path = os.path.join(dir, f"{video_name}_{s_f}_{e_f}{ext}")
|
|
3041
3089
|
if not gpu:
|
|
3042
|
-
cmd = f'ffmpeg -i "{file_path}" -vf trim=start_frame={s_f}:end_frame={e_f} -an -crf {quality} "{out_path}" -loglevel error -stats -y'
|
|
3090
|
+
cmd = f'ffmpeg -i "{file_path}" -vf trim=start_frame={s_f}:end_frame={e_f} -c:v {video_codec} -an -crf {quality} "{out_path}" -loglevel error -stats -y'
|
|
3043
3091
|
else:
|
|
3044
3092
|
cmd = f'ffmpeg -hwaccel auto -i "{file_path}" -vf trim=start_frame={s_f}:end_frame={e_f} -c:v h264_nvenc -rc vbr -cq {quality} -an "{out_path}" -loglevel error -stats -y'
|
|
3045
3093
|
subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
|
|
@@ -3309,6 +3357,7 @@ def convert_to_mov(path: Union[str, os.PathLike],
|
|
|
3309
3357
|
def superimpose_video_progressbar(video_path: Union[str, os.PathLike],
|
|
3310
3358
|
bar_height: Optional[int] = 10,
|
|
3311
3359
|
color: Optional[str] = 'red',
|
|
3360
|
+
codec: Optional[str] = None,
|
|
3312
3361
|
position: Optional[Literal['top', 'bottom']] = 'bottom',
|
|
3313
3362
|
save_dir: Optional[Union[str, os.PathLike]] = None,
|
|
3314
3363
|
gpu: Optional[bool] = False) -> None:
|
|
@@ -3345,28 +3394,30 @@ def superimpose_video_progressbar(video_path: Union[str, os.PathLike],
|
|
|
3345
3394
|
video_path = find_all_videos_in_directory(directory=video_path, as_dict=True, raise_error=True)
|
|
3346
3395
|
video_paths = list(video_path.values())
|
|
3347
3396
|
else:
|
|
3348
|
-
raise InvalidInputError(msg='{} is not a valid file path or directory path.', source=superimpose_video_progressbar.__name__)
|
|
3397
|
+
raise InvalidInputError(msg=f'{video_path} is not a valid file path or directory path.', source=superimpose_video_progressbar.__name__)
|
|
3349
3398
|
if save_dir is not None:
|
|
3350
3399
|
check_if_dir_exists(in_dir=save_dir)
|
|
3351
3400
|
else:
|
|
3352
3401
|
save_dir, _, _ = get_fn_ext(filepath=video_paths[0])
|
|
3402
|
+
if codec is not None: check_valid_codec(codec=codec, raise_error=True, source=superimpose_video_progressbar.__name__)
|
|
3353
3403
|
for cnt, video_path in enumerate(video_paths):
|
|
3354
3404
|
video_meta_data = get_video_meta_data(video_path=video_path)
|
|
3355
3405
|
video_length = video_meta_data['video_length_s']
|
|
3356
3406
|
width, height = video_meta_data['width'], video_meta_data['height']
|
|
3357
3407
|
bar_height = int(height * (bar_height/100))
|
|
3358
3408
|
_, video_name, ext = get_fn_ext(filepath=video_path)
|
|
3359
|
-
|
|
3409
|
+
video_codec = get_ffmpeg_codec(file_name=video_path) if codec is None else codec
|
|
3410
|
+
stdout_information(msg=f'Inserting progress bar on video {video_name}...', source=superimpose_video_progressbar.__name__)
|
|
3360
3411
|
save_path = os.path.join(save_dir, f'{video_name}_progress_bar{ext}')
|
|
3361
3412
|
check_int(name=f'{superimpose_video_progressbar} height', value=bar_height, max_value=height, min_value=1)
|
|
3362
3413
|
if position == 'bottom':
|
|
3363
3414
|
if not gpu:
|
|
3364
|
-
cmd = f'ffmpeg -i "{video_path}" -filter_complex "color=c={color}:s={width}x{bar_height}[bar];[0][bar]overlay=-w+(w/{video_length})*t:H-h:shortest=1" -c:a copy "{save_path}" -loglevel error -stats -hide_banner -y'
|
|
3415
|
+
cmd = f'ffmpeg -i "{video_path}" -filter_complex "color=c={color}:s={width}x{bar_height}[bar];[0][bar]overlay=-w+(w/{video_length})*t:H-h:shortest=1" -c:v {video_codec} -c:a copy "{save_path}" -loglevel error -stats -hide_banner -y'
|
|
3365
3416
|
else:
|
|
3366
3417
|
cmd = f'ffmpeg -hwaccel auto -i "{video_path}" -filter_complex "color=c={color}:s={width}x{bar_height}[bar];[0][bar]overlay=-w+(w/{video_length})*t:H-h:shortest=1" -c:v h264_nvenc -c:a copy "{save_path}" -loglevel error -stats -hide_banner -y'
|
|
3367
3418
|
else:
|
|
3368
3419
|
if not gpu:
|
|
3369
|
-
cmd = f'ffmpeg -i "{video_path}" -filter_complex "color=c={color}:s={width}x{bar_height}[bar];[0][bar]overlay=-w+(w/{video_length})*t:{bar_height}-h:shortest=1" -c:a copy "{save_path}" -loglevel error -stats -hide_banner -y'
|
|
3420
|
+
cmd = f'ffmpeg -i "{video_path}" -filter_complex "color=c={color}:s={width}x{bar_height}[bar];[0][bar]overlay=-w+(w/{video_length})*t:{bar_height}-h:shortest=1" -c:a copy -c:v {video_codec} "{save_path}" -loglevel error -stats -hide_banner -y'
|
|
3370
3421
|
else:
|
|
3371
3422
|
cmd = f'ffmpeg -hwaccel auto -c:v h264_cuvid -i "{video_path}" -filter_complex "color=c={color}:s={width}x{bar_height}[bar];[0][bar]overlay=-w+(w/{video_length})*t:{bar_height}-h:shortest=1" -c:a copy "{save_path}" -loglevel error -stats -hide_banner -y'
|
|
3372
3423
|
subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
|
|
@@ -3757,7 +3808,7 @@ def superimpose_elapsed_time(video_path: Union[str, os.PathLike],
|
|
|
3757
3808
|
|
|
3758
3809
|
def reverse_videos(path: Union[str, os.PathLike],
|
|
3759
3810
|
save_dir: Optional[Union[str, os.PathLike]] = None,
|
|
3760
|
-
codec:
|
|
3811
|
+
codec: Optional[str] = None,
|
|
3761
3812
|
quality: Optional[int] = 60,
|
|
3762
3813
|
gpu: Optional[bool] = False) -> None:
|
|
3763
3814
|
|
|
@@ -3803,13 +3854,15 @@ def reverse_videos(path: Union[str, os.PathLike],
|
|
|
3803
3854
|
os.makedirs(save_dir)
|
|
3804
3855
|
else:
|
|
3805
3856
|
raise InvalidInputError(msg=f'Path is not a valid file or directory path.', source=reverse_videos.__name__)
|
|
3857
|
+
if codec is not None: check_valid_codec(codec=codec, raise_error=True, source=reverse_videos.__name__)
|
|
3806
3858
|
for file_cnt, file_path in enumerate(file_paths):
|
|
3807
3859
|
_, video_name, ext = get_fn_ext(filepath=file_path)
|
|
3808
|
-
|
|
3860
|
+
stdout_information(msg=f'Reversing video {video_name} (Video {file_cnt+1}/{len(file_paths)})...', source=reverse_videos.__name__)
|
|
3861
|
+
video_codec = get_ffmpeg_codec(file_name=file_path) if codec is None else codec
|
|
3809
3862
|
_ = get_video_meta_data(video_path=file_path)
|
|
3810
3863
|
out_path = os.path.join(save_dir, f'{video_name}{ext}')
|
|
3811
3864
|
if not gpu:
|
|
3812
|
-
cmd = f'ffmpeg -i "{file_path}" -vf reverse -af areverse -c:v {
|
|
3865
|
+
cmd = f'ffmpeg -i "{file_path}" -vf reverse -af areverse -c:v {video_codec} -crf {crf} "{out_path}" -loglevel error -stats -hide_banner -y'
|
|
3813
3866
|
else:
|
|
3814
3867
|
cmd = f'ffmpeg -hwaccel auto -i "{file_path}" -vf reverse -af areverse -c:v h264_nvenc -crf {crf} "{out_path}" -loglevel error -stats -hide_banner -y'
|
|
3815
3868
|
subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
|
|
@@ -5080,7 +5133,7 @@ def change_playback_speed(video_path: Union[str, os.PathLike],
|
|
|
5080
5133
|
quality: int = 60,
|
|
5081
5134
|
gpu: bool = False,
|
|
5082
5135
|
verbose: bool = True,
|
|
5083
|
-
codec: str =
|
|
5136
|
+
codec: Optional[str] = None):
|
|
5084
5137
|
"""
|
|
5085
5138
|
Change the playback speed of a video file. Speed > 1.0 makes the video faster, speed < 1.0 makes it slower.
|
|
5086
5139
|
|
|
@@ -5123,6 +5176,10 @@ def change_playback_speed(video_path: Union[str, os.PathLike],
|
|
|
5123
5176
|
else:
|
|
5124
5177
|
save_path = os.path.join(dir, f'{video_name}_playback_speed_{speed}{ext}')
|
|
5125
5178
|
video_pts = 1.0 / speed
|
|
5179
|
+
if codec is None:
|
|
5180
|
+
codec = get_ffmpeg_codec(file_name=video_path)
|
|
5181
|
+
else:
|
|
5182
|
+
check_valid_codec(codec=codec, raise_error=True, source=change_playback_speed.__name__)
|
|
5126
5183
|
if gpu:
|
|
5127
5184
|
cmd = f'ffmpeg -hwaccel auto -c:v h264_cuvid -i "{video_path}" -vf "setpts={video_pts:.6f}*PTS" -an -c:v h264_nvenc -rc vbr -cq {quality_code} "{save_path}" -hide_banner -loglevel error -stats -y'
|
|
5128
5185
|
else:
|
|
@@ -5140,7 +5197,7 @@ def change_playback_speed_dir(data_dir: Union[str, os.PathLike],
|
|
|
5140
5197
|
quality: int = 60,
|
|
5141
5198
|
gpu: bool = False,
|
|
5142
5199
|
verbose: bool = True,
|
|
5143
|
-
codec: str =
|
|
5200
|
+
codec: Optional[str] = None):
|
|
5144
5201
|
"""
|
|
5145
5202
|
Change the playback speed of all video files in a directory. Speed > 1.0 makes videos faster, speed < 1.0 makes them slower.
|
|
5146
5203
|
|
|
@@ -5174,7 +5231,7 @@ def change_playback_speed_dir(data_dir: Union[str, os.PathLike],
|
|
|
5174
5231
|
check_float(name=f'{change_playback_speed.__name__} speed', value=speed, min_value=0.000001, max_value=100, raise_error=True)
|
|
5175
5232
|
check_int(name=f'{change_playback_speed.__name__} quality', value=quality, min_value=1, max_value=100, raise_error=True)
|
|
5176
5233
|
check_if_dir_exists(in_dir=data_dir, source=f'{change_playback_speed.__name__} data_dir')
|
|
5177
|
-
check_valid_codec(codec=codec, raise_error=True, source=change_playback_speed_dir.__name__)
|
|
5234
|
+
if codec is not None: check_valid_codec(codec=codec, raise_error=True, source=change_playback_speed_dir.__name__)
|
|
5178
5235
|
video_dict = find_all_videos_in_directory(directory=data_dir, as_dict=True, raise_error=True, sort_alphabetically=True)
|
|
5179
5236
|
total_video_cnt = len(list(video_dict.keys()))
|
|
5180
5237
|
if save_dir is not None:
|
|
@@ -5186,9 +5243,10 @@ def change_playback_speed_dir(data_dir: Union[str, os.PathLike],
|
|
|
5186
5243
|
raise DuplicationError(msg=f'The video directory and the save directory cannot be the same folder: {save_dir}', source=change_playback_speed_dir.__name__)
|
|
5187
5244
|
for video_cnt, (video_name, video_path) in enumerate(video_dict.items()):
|
|
5188
5245
|
_, _, ext = get_fn_ext(filepath=video_path)
|
|
5246
|
+
video_codec = get_ffmpeg_codec(file_name=video_path) if codec is None else codec
|
|
5189
5247
|
save_path = os.path.join(save_dir, f'{video_name}{ext}')
|
|
5190
5248
|
if verbose: print(f'Changing video speed for video {video_name} ({video_cnt+1}/{total_video_cnt})...')
|
|
5191
|
-
change_playback_speed(video_path=video_path, speed=speed, save_path=save_path, quality=quality, codec=
|
|
5249
|
+
change_playback_speed(video_path=video_path, speed=speed, save_path=save_path, quality=quality, codec=video_codec, verbose=False)
|
|
5192
5250
|
|
|
5193
5251
|
timer.stop_timer()
|
|
5194
5252
|
if verbose:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: simba-uw-tf-dev
|
|
3
|
-
Version: 4.7.
|
|
3
|
+
Version: 4.7.3
|
|
4
4
|
Summary: Toolkit for computer classification and analysis of behaviors in experimental animals
|
|
5
5
|
Home-page: https://github.com/sgoldenlab/simba
|
|
6
6
|
Author: Simon Nilsson, Jia Jie Choong, Sophia Hwang
|