simba-uw-tf-dev 4.7.1__py3-none-any.whl → 4.7.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.
Potentially problematic release.
This version of simba-uw-tf-dev might be problematic. Click here for more details.
- simba/SimBA.py +13 -4
- simba/assets/icons/left_arrow_green.png +0 -0
- simba/assets/icons/left_arrow_red.png +0 -0
- simba/assets/icons/right_arrow_green.png +0 -0
- simba/assets/icons/right_arrow_red.png +0 -0
- simba/assets/lookups/yolo_schematics/yolo_mitra.csv +9 -0
- simba/mixins/geometry_mixin.py +357 -302
- simba/mixins/image_mixin.py +129 -4
- 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/outlier_tools/skip_outlier_correction.py +2 -2
- simba/plotting/heat_mapper_clf_mp.py +45 -23
- simba/plotting/plot_clf_results.py +2 -1
- simba/plotting/plot_clf_results_mp.py +456 -455
- simba/roi_tools/roi_utils.py +2 -2
- simba/sandbox/convert_h264_to_mp4_lossless.py +129 -0
- simba/sandbox/extract_and_convert_videos.py +257 -0
- simba/sandbox/remove_end_of_video.py +80 -0
- simba/sandbox/video_timelaps.py +291 -0
- simba/third_party_label_appenders/transform/simba_to_yolo.py +8 -5
- simba/ui/import_pose_frame.py +13 -13
- simba/ui/pop_ups/clf_plot_pop_up.py +1 -1
- 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/video_processing_pop_up.py +3638 -3469
- simba/ui/pop_ups/yolo_inference_popup.py +1 -1
- simba/ui/pop_ups/yolo_pose_train_popup.py +1 -1
- simba/ui/tkinter_functions.py +3 -1
- simba/ui/video_timelaps.py +454 -0
- simba/utils/lookups.py +67 -1
- simba/utils/read_write.py +10 -3
- simba/video_processors/batch_process_create_ffmpeg_commands.py +0 -1
- simba/video_processors/video_processing.py +160 -39
- {simba_uw_tf_dev-4.7.1.dist-info → simba_uw_tf_dev-4.7.5.dist-info}/METADATA +1 -1
- {simba_uw_tf_dev-4.7.1.dist-info → simba_uw_tf_dev-4.7.5.dist-info}/RECORD +41 -31
- {simba_uw_tf_dev-4.7.1.dist-info → simba_uw_tf_dev-4.7.5.dist-info}/LICENSE +0 -0
- {simba_uw_tf_dev-4.7.1.dist-info → simba_uw_tf_dev-4.7.5.dist-info}/WHEEL +0 -0
- {simba_uw_tf_dev-4.7.1.dist-info → simba_uw_tf_dev-4.7.5.dist-info}/entry_points.txt +0 -0
- {simba_uw_tf_dev-4.7.1.dist-info → simba_uw_tf_dev-4.7.5.dist-info}/top_level.txt +0 -0
|
@@ -52,12 +52,12 @@ from simba.utils.errors import (CountError, DirectoryExistError,
|
|
|
52
52
|
NoDataError, NoFilesFoundError,
|
|
53
53
|
NotDirectoryError, ResolutionError,
|
|
54
54
|
SimBAGPUError)
|
|
55
|
-
from simba.utils.lookups import (get_current_time,
|
|
55
|
+
from simba.utils.lookups import (get_current_time, get_ffmpeg_codec,
|
|
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
59
|
video_quality_to_preset_lookup)
|
|
60
|
-
from simba.utils.printing import SimbaTimer, stdout_success
|
|
60
|
+
from simba.utils.printing import SimbaTimer, stdout_information, stdout_success
|
|
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):
|
|
@@ -1088,12 +1101,18 @@ def remove_beginning_of_video(file_path: Union[str, os.PathLike],
|
|
|
1088
1101
|
"""
|
|
1089
1102
|
Remove N seconds from the beginning of a video file.
|
|
1090
1103
|
|
|
1104
|
+
.. seealso::
|
|
1105
|
+
To remove N seconds from the end of the video, see :func:`simba.video_processors.video_processing.remove_end_of_video`.
|
|
1106
|
+
|
|
1091
1107
|
:param Union[str, os.PathLike] file_path: Path to video file
|
|
1092
1108
|
:param int time: Number of seconds to remove from the beginning of the video.
|
|
1093
1109
|
:param int quality: Video quality percentage (1-100). Higher values = higher quality. Default 60.
|
|
1094
1110
|
: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
1111
|
:param Optional[bool] gpu: If True, use NVIDEA GPU codecs. Default False.
|
|
1096
1112
|
: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.
|
|
1113
|
+
|
|
1114
|
+
.. note::
|
|
1115
|
+
Codec is automatically selected: libx264 for CPU encoding (ignored if gpu=True).
|
|
1097
1116
|
|
|
1098
1117
|
:example:
|
|
1099
1118
|
>>> _ = remove_beginning_of_video(file_path='project_folder/videos/Video_1.avi', time=10)
|
|
@@ -1128,12 +1147,72 @@ def remove_beginning_of_video(file_path: Union[str, os.PathLike],
|
|
|
1128
1147
|
stdout_success(msg=f"SIMBA COMPLETE: Video converted! {save_name} generated!", elapsed_time=timer.elapsed_time_str, source=remove_beginning_of_video.__name__)
|
|
1129
1148
|
|
|
1130
1149
|
|
|
1150
|
+
def remove_end_of_video(file_path: Union[str, os.PathLike],
|
|
1151
|
+
time: int,
|
|
1152
|
+
quality: int = 60,
|
|
1153
|
+
save_path: Optional[Union[str, os.PathLike]] = None,
|
|
1154
|
+
gpu: Optional[bool] = False) -> None:
|
|
1155
|
+
"""
|
|
1156
|
+
Remove N seconds from the end of a video file.
|
|
1157
|
+
|
|
1158
|
+
.. seealso::
|
|
1159
|
+
To remove N seconds from the beginning of the video, see :func:`simba.video_processors.video_processing.remove_beginning_of_video`
|
|
1160
|
+
|
|
1161
|
+
:param Union[str, os.PathLike] file_path: Path to video file
|
|
1162
|
+
:param int time: Number of seconds to remove from the end of the video.
|
|
1163
|
+
:param int quality: Video quality percentage (1-100). Higher values = higher quality. Default 60.
|
|
1164
|
+
: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.
|
|
1165
|
+
:param Optional[bool] gpu: If True, use NVIDEA GPU codecs. Default False.
|
|
1166
|
+
: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.
|
|
1167
|
+
|
|
1168
|
+
.. note::
|
|
1169
|
+
Codec is automatically selected: libx264 for CPU encoding (ignored if gpu=True).
|
|
1170
|
+
|
|
1171
|
+
:example:
|
|
1172
|
+
>>> _ = remove_end_of_video(file_path='project_folder/videos/Video_1.avi', time=10)
|
|
1173
|
+
>>> remove_end_of_video(file_path=f'/Users/simon/Desktop/imgs_4/test/blahhhh.mp4', save_path='/Users/simon/Desktop/imgs_4/test/CUT.mp4', time=3)
|
|
1174
|
+
"""
|
|
1175
|
+
|
|
1176
|
+
check_ffmpeg_available(raise_error=True)
|
|
1177
|
+
if gpu and not check_nvidea_gpu_available():
|
|
1178
|
+
raise FFMPEGCodecGPUError(msg="No GPU found (as evaluated by nvidea-smi returning None)",
|
|
1179
|
+
source=remove_end_of_video.__name__)
|
|
1180
|
+
timer = SimbaTimer(start=True)
|
|
1181
|
+
check_file_exist_and_readable(file_path=file_path)
|
|
1182
|
+
video_meta_data = get_video_meta_data(video_path=file_path)
|
|
1183
|
+
check_int(name="Cut time", value=time, min_value=1)
|
|
1184
|
+
check_int(name=f'{remove_end_of_video.__name__} quality', value=quality, min_value=1, max_value=100,
|
|
1185
|
+
raise_error=True)
|
|
1186
|
+
quality_crf = quality_pct_to_crf(pct=int(quality))
|
|
1187
|
+
time = int(time)
|
|
1188
|
+
dir, file_name, ext = get_fn_ext(filepath=file_path)
|
|
1189
|
+
if video_meta_data['video_length_s'] <= time:
|
|
1190
|
+
raise InvalidInputError(
|
|
1191
|
+
msg=f"The cut time {time}s is invalid for video {file_name} with length {video_meta_data['video_length_s']}s",
|
|
1192
|
+
source=remove_end_of_video.__name__)
|
|
1193
|
+
if save_path is None:
|
|
1194
|
+
save_name = os.path.join(dir, f"{file_name}_shorten.mp4")
|
|
1195
|
+
else:
|
|
1196
|
+
check_if_dir_exists(in_dir=os.path.dirname(save_path), source=f'{remove_end_of_video.__name__} save_path',
|
|
1197
|
+
create_if_not_exist=True)
|
|
1198
|
+
save_name = save_path
|
|
1199
|
+
duration = video_meta_data['video_length_s'] - time
|
|
1200
|
+
if gpu:
|
|
1201
|
+
cmd = f'ffmpeg -hwaccel auto -c:v h264_cuvid -i "{file_path}" -t {duration} -rc vbr -cq {quality_crf} -c:v h264_nvenc -c:a aac "{save_name}" -loglevel error -stats -hide_banner -y'
|
|
1202
|
+
else:
|
|
1203
|
+
cmd = f'ffmpeg -i "{file_path}" -t {duration} -c:v libx264 -crf {quality_crf} -c:a aac "{save_name}" -loglevel error -stats -hide_banner -y'
|
|
1204
|
+
print(f"Removing final {time}s from {file_name}... ")
|
|
1205
|
+
subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
|
|
1206
|
+
timer.stop_timer()
|
|
1207
|
+
stdout_success(msg=f"SIMBA COMPLETE: Video converted! {save_name} generated!", elapsed_time=timer.elapsed_time_str, source=remove_end_of_video.__name__)
|
|
1208
|
+
|
|
1209
|
+
|
|
1131
1210
|
def clip_video_in_range(file_path: Union[str, os.PathLike],
|
|
1132
1211
|
start_time: str,
|
|
1133
1212
|
end_time: str,
|
|
1134
1213
|
out_dir: Optional[Union[str, os.PathLike]] = None,
|
|
1135
1214
|
save_path: Optional[Union[str, os.PathLike]] = None,
|
|
1136
|
-
codec: str =
|
|
1215
|
+
codec: Optional[str] = None,
|
|
1137
1216
|
quality: int = 60,
|
|
1138
1217
|
verbose: bool = True,
|
|
1139
1218
|
overwrite: Optional[bool] = False,
|
|
@@ -1147,7 +1226,7 @@ def clip_video_in_range(file_path: Union[str, os.PathLike],
|
|
|
1147
1226
|
:param str end_time: End time in HH:MM:SS format.
|
|
1148
1227
|
: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
1228
|
: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.
|
|
1229
|
+
: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
1230
|
:param int quality: Video quality percentage (0-100). Higher values = higher quality. Default 60.
|
|
1152
1231
|
:param bool verbose: If True, prints conversion progress. Default True.
|
|
1153
1232
|
:param Optional[bool] overwrite: If True, overwrite output file if path already exists. If False, then raise FileExistError. Default False.
|
|
@@ -1176,7 +1255,10 @@ def clip_video_in_range(file_path: Union[str, os.PathLike],
|
|
|
1176
1255
|
check_if_hhmmss_timestamp_is_valid_part_of_video(timestamp=end_time, video_path=file_path)
|
|
1177
1256
|
quality = 60 if not check_int(name='quality', value=quality, min_value=0, max_value=100, raise_error=False)[0] else int(quality)
|
|
1178
1257
|
quality_crf = quality_pct_to_crf(pct=quality)
|
|
1179
|
-
|
|
1258
|
+
if codec is None:
|
|
1259
|
+
codec = get_ffmpeg_codec(file_name=file_path)
|
|
1260
|
+
else:
|
|
1261
|
+
check_valid_codec(codec=codec, raise_error=True, source=clip_video_in_range.__name__)
|
|
1180
1262
|
if not include_clip_time_in_filename:
|
|
1181
1263
|
save_name = os.path.join(dir, file_name + "_clipped.mp4")
|
|
1182
1264
|
else:
|
|
@@ -1200,7 +1282,7 @@ def downsample_video(file_path: Union[str, os.PathLike],
|
|
|
1200
1282
|
video_height: int,
|
|
1201
1283
|
video_width: int,
|
|
1202
1284
|
gpu: bool = False,
|
|
1203
|
-
codec: str =
|
|
1285
|
+
codec: Optional[str] = None,
|
|
1204
1286
|
quality: int = 23,
|
|
1205
1287
|
save_path: Optional[Union[str, os.PathLike]] = None,
|
|
1206
1288
|
verbose: bool = True) -> None:
|
|
@@ -1240,6 +1322,10 @@ def downsample_video(file_path: Union[str, os.PathLike],
|
|
|
1240
1322
|
if os.path.isfile(save_name):
|
|
1241
1323
|
raise FileExistError("SIMBA ERROR: The outfile file already exist: {}.".format(save_name), source=downsample_video.__name__)
|
|
1242
1324
|
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)
|
|
1325
|
+
if codec is None:
|
|
1326
|
+
codec = get_ffmpeg_codec(file_name=file_path)
|
|
1327
|
+
else:
|
|
1328
|
+
check_valid_codec(codec=codec, raise_error=True, source=downsample_video.__name__)
|
|
1243
1329
|
if gpu:
|
|
1244
1330
|
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
1331
|
#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 +1418,9 @@ def batch_convert_video_format(directory: Union[str, os.PathLike],
|
|
|
1332
1418
|
:parameter str output_format: Format of the output files (e.g., mp4).
|
|
1333
1419
|
:parameter Optional[bool] gpu: If True, use NVIDEA GPU codecs. Default False.
|
|
1334
1420
|
:returns: None. The results are stored in the same directory as the input files.
|
|
1421
|
+
|
|
1422
|
+
.. note::
|
|
1423
|
+
Codec is automatically selected: libx264 for CPU encoding (ignored if gpu=True).
|
|
1335
1424
|
|
|
1336
1425
|
:example:
|
|
1337
1426
|
>>> _ = batch_convert_video_format(directory='project_folder/videos', input_format='avi', output_format='mp4')
|
|
@@ -1487,6 +1576,7 @@ def multi_split_video(file_path: Union[str, os.PathLike],
|
|
|
1487
1576
|
end_times: List[str],
|
|
1488
1577
|
out_dir: Optional[Union[str, os.PathLike]] = None,
|
|
1489
1578
|
quality: Optional[int] = None,
|
|
1579
|
+
codec: Optional[str] = None,
|
|
1490
1580
|
include_clip_time_in_filename: Optional[bool] = False,
|
|
1491
1581
|
gpu: Optional[bool] = False) -> None:
|
|
1492
1582
|
"""
|
|
@@ -1499,6 +1589,7 @@ def multi_split_video(file_path: Union[str, os.PathLike],
|
|
|
1499
1589
|
:param Optional[int] quality: Video quality (CRF value). Lower values = higher quality. Range 0-52. If None, defaults to 23. Default None.
|
|
1500
1590
|
: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
1591
|
:param Optional[bool] gpu: If True, use NVIDEA GPU codecs. Default False.
|
|
1592
|
+
: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
1593
|
:returns: None.
|
|
1503
1594
|
|
|
1504
1595
|
:example:
|
|
@@ -1510,6 +1601,8 @@ def multi_split_video(file_path: Union[str, os.PathLike],
|
|
|
1510
1601
|
check_file_exist_and_readable(file_path=file_path)
|
|
1511
1602
|
dir_name, file_name, ext = get_fn_ext(filepath=file_path)
|
|
1512
1603
|
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)
|
|
1604
|
+
if codec is not None: check_valid_codec(codec=codec, raise_error=True, source=multi_split_video.__name__)
|
|
1605
|
+
codec = get_ffmpeg_codec(file_name=file_name) if codec is None else codec
|
|
1513
1606
|
if out_dir is not None:
|
|
1514
1607
|
if not os.path.isdir(out_dir):
|
|
1515
1608
|
os.makedirs(out_dir)
|
|
@@ -1555,7 +1648,7 @@ def multi_split_video(file_path: Union[str, os.PathLike],
|
|
|
1555
1648
|
)
|
|
1556
1649
|
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
1650
|
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'
|
|
1651
|
+
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
1652
|
clip_timer = SimbaTimer(start=True)
|
|
1560
1653
|
subprocess.call(command, shell=True, stdout=subprocess.PIPE)
|
|
1561
1654
|
clip_timer.stop_timer()
|
|
@@ -1623,6 +1716,7 @@ def crop_single_video(file_path: Union[str, os.PathLike],
|
|
|
1623
1716
|
def crop_multiple_videos(directory_path: Union[str, os.PathLike],
|
|
1624
1717
|
output_path: Union[str, os.PathLike],
|
|
1625
1718
|
gpu: Optional[bool] = False,
|
|
1719
|
+
codec: Optional[str] = None,
|
|
1626
1720
|
quality: int = 60) -> None:
|
|
1627
1721
|
"""
|
|
1628
1722
|
Crop multiple videos in a folder according to crop-coordinates defined in the **first** video.
|
|
@@ -1636,6 +1730,8 @@ def crop_multiple_videos(directory_path: Union[str, os.PathLike],
|
|
|
1636
1730
|
:param Union[str, os.PathLike] directory_path: Directory containing input videos.
|
|
1637
1731
|
:param Union[str, os.PathLike] output_path: Directory where to save the cropped videos.
|
|
1638
1732
|
:param Optional[bool] gpu: If True, use NVIDEA GPU codecs. Default False.
|
|
1733
|
+
: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.
|
|
1734
|
+
:param int quality: Video quality percentage (1-100). Higher values = higher quality. Default 60.
|
|
1639
1735
|
:returns: None. Results are stored in passed ``output_path``.
|
|
1640
1736
|
|
|
1641
1737
|
:example:
|
|
@@ -1661,15 +1757,18 @@ def crop_multiple_videos(directory_path: Union[str, os.PathLike],
|
|
|
1661
1757
|
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
1758
|
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
1759
|
timer = SimbaTimer(start=True)
|
|
1760
|
+
if codec is not None: check_valid_codec(codec=codec, raise_error=True, source=change_single_video_fps.__name__)
|
|
1761
|
+
|
|
1664
1762
|
for file_cnt, file_path in enumerate(video_paths):
|
|
1665
1763
|
video_timer = SimbaTimer(start=True)
|
|
1666
1764
|
dir_name, file_name, ext = get_fn_ext(filepath=file_path)
|
|
1765
|
+
video_codec = Formats.BATCH_CODEC.value if codec is None else get_ffmpeg_codec(file_name=file_path)
|
|
1667
1766
|
print(f"Cropping video {file_name} ({file_cnt+1}/{len(video_paths)})...")
|
|
1668
1767
|
video_meta_data = get_video_meta_data(file_path)
|
|
1669
1768
|
if (roi_selector.bottom_right[0] > video_meta_data["width"]) or (roi_selector.bottom_right[1] > video_meta_data["height"]):
|
|
1670
1769
|
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
1770
|
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)
|
|
1771
|
+
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
1772
|
video_timer.stop_timer()
|
|
1674
1773
|
print(f"Video {file_name} cropped (Video {file_cnt+1}/{len(video_paths)}, elapsed time: {video_timer.elapsed_time_str})")
|
|
1675
1774
|
timer.stop_timer()
|
|
@@ -1692,6 +1791,9 @@ def frames_to_movie(directory: Union[str, os.PathLike],
|
|
|
1692
1791
|
:param out_format: Format of the output video. One of: 'mp4', 'avi', 'webm'. Default is 'mp4'.
|
|
1693
1792
|
:param gpu: If True, use NVIDIA GPU codecs (if available). Default is False.
|
|
1694
1793
|
:return: None. Video is saved to disk.
|
|
1794
|
+
|
|
1795
|
+
.. note::
|
|
1796
|
+
Codec is automatically selected based on out_format: libvpx-vp9 for 'webm', mpeg4 for 'avi', libx264 for 'mp4' (ignored if gpu=True).
|
|
1695
1797
|
"""
|
|
1696
1798
|
|
|
1697
1799
|
import re
|
|
@@ -1776,6 +1878,9 @@ def video_concatenator(video_one_path: Union[str, os.PathLike],
|
|
|
1776
1878
|
:param Optional[int] quality: Integer (1-100) representing output video quality. Higher = better quality + bigger size. If None, defaults to 60. Default None.
|
|
1777
1879
|
:param Optional[bool] gpu: If True, use NVIDEA GPU codecs. Default False.
|
|
1778
1880
|
:returns: None. The video is stored in the same directory as the ``video_one_path`` using the video names concatenated as filename.
|
|
1881
|
+
|
|
1882
|
+
.. note::
|
|
1883
|
+
Codec is automatically selected: libx264 for CPU encoding (ignored if gpu=True).
|
|
1779
1884
|
|
|
1780
1885
|
:example:
|
|
1781
1886
|
>>> 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 +2505,7 @@ def resize_videos_by_height(video_paths: List[Union[str, os.PathLike]],
|
|
|
2400
2505
|
gpu: Optional[bool] = False,
|
|
2401
2506
|
quality: Optional[int] = None,
|
|
2402
2507
|
suffix: Optional[str] = None,
|
|
2508
|
+
codec: Optional[str] = None,
|
|
2403
2509
|
verbose: Optional[bool] = True) -> Union[None, List[Union[None, str, os.PathLike]]]:
|
|
2404
2510
|
"""
|
|
2405
2511
|
Re-size a list of videos to a specified height while retaining their aspect ratios.
|
|
@@ -2442,10 +2548,13 @@ def resize_videos_by_height(video_paths: List[Union[str, os.PathLike]],
|
|
|
2442
2548
|
for i in video_paths:
|
|
2443
2549
|
video_heights.append(get_video_meta_data(video_path=i)["height"])
|
|
2444
2550
|
height = video_heights[int(height)]
|
|
2551
|
+
if codec is not None: check_valid_codec(codec=codec, raise_error=True, source=resize_videos_by_height.__name__)
|
|
2552
|
+
|
|
2445
2553
|
for cnt, video_path in enumerate(video_paths):
|
|
2446
2554
|
dir_name, video_name, ext = get_fn_ext(video_path)
|
|
2555
|
+
video_codec = get_ffmpeg_codec(file_name=video_path) if codec is None else codec
|
|
2447
2556
|
if verbose:
|
|
2448
|
-
|
|
2557
|
+
stdout_information(msg=f"Resizing height video {video_name} (Video {cnt+1}/{len(video_paths)})...", source=resize_videos_by_height.__name__)
|
|
2449
2558
|
if overwrite:
|
|
2450
2559
|
dt = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
2451
2560
|
save_path = os.path.join(dir_name, f"{video_name}_{dt}.mp4")
|
|
@@ -2459,7 +2568,7 @@ def resize_videos_by_height(video_paths: List[Union[str, os.PathLike]],
|
|
|
2459
2568
|
if gpu:
|
|
2460
2569
|
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
2570
|
else:
|
|
2462
|
-
cmd = f'ffmpeg -y -i "{video_path}" -vf scale=-2:{height} -crf {quality} "{save_path}" -hide_banner -loglevel error -stats -y'
|
|
2571
|
+
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
2572
|
subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
|
|
2464
2573
|
if overwrite:
|
|
2465
2574
|
shutil.copy(save_path, video_path)
|
|
@@ -2477,6 +2586,7 @@ def resize_videos_by_width(video_paths: List[Union[str, os.PathLike]],
|
|
|
2477
2586
|
overwrite: Optional[bool] = False,
|
|
2478
2587
|
save_dir: Optional[Union[str, os.PathLike]] = None,
|
|
2479
2588
|
gpu: Optional[bool] = False,
|
|
2589
|
+
codec: Optional[str] = None,
|
|
2480
2590
|
quality: Optional[int] = None,
|
|
2481
2591
|
suffix: Optional[str] = None,
|
|
2482
2592
|
verbose: Optional[bool] = True) -> Union[None, List[Union[None, str, os.PathLike]]]:
|
|
@@ -2527,12 +2637,12 @@ def resize_videos_by_width(video_paths: List[Union[str, os.PathLike]],
|
|
|
2527
2637
|
for i in video_paths:
|
|
2528
2638
|
video_widths.append(get_video_meta_data(video_path=i)["width"])
|
|
2529
2639
|
width = video_widths[int(width)]
|
|
2640
|
+
if codec is not None: check_valid_codec(codec=codec, raise_error=True, source=resize_videos_by_width.__name__)
|
|
2530
2641
|
for cnt, video_path in enumerate(video_paths):
|
|
2531
2642
|
dir_name, video_name, ext = get_fn_ext(video_path)
|
|
2643
|
+
video_codec = get_ffmpeg_codec(file_name=video_path) if codec is None else codec
|
|
2532
2644
|
if verbose:
|
|
2533
|
-
|
|
2534
|
-
f"Resizing width video {video_name} (Video {cnt+1}/{len(video_paths)})..."
|
|
2535
|
-
)
|
|
2645
|
+
stdout_information(msg=f"Resizing width video {video_name} (Video {cnt+1}/{len(video_paths)})...", source=resize_videos_by_height.__name__)
|
|
2536
2646
|
if overwrite:
|
|
2537
2647
|
dt = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
2538
2648
|
save_path = os.path.join(dir_name, f"{video_name}_{dt}.mp4")
|
|
@@ -2546,7 +2656,7 @@ def resize_videos_by_width(video_paths: List[Union[str, os.PathLike]],
|
|
|
2546
2656
|
if gpu:
|
|
2547
2657
|
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
2658
|
else:
|
|
2549
|
-
cmd = f'ffmpeg -y -i "{video_path}" -vf scale={width}:-2 -crf {quality} "{save_path}" -hide_banner -loglevel error -stats -y'
|
|
2659
|
+
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
2660
|
subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
|
|
2551
2661
|
if overwrite:
|
|
2552
2662
|
shutil.copy(save_path, video_path)
|
|
@@ -2685,7 +2795,6 @@ def vertical_video_concatenator(video_paths: List[Union[str, os.PathLike]],
|
|
|
2685
2795
|
:width: 300
|
|
2686
2796
|
:align: center
|
|
2687
2797
|
|
|
2688
|
-
|
|
2689
2798
|
.. seealso::
|
|
2690
2799
|
:func:`simba.video_processors.video_processing.horizontal_video_concatenator`
|
|
2691
2800
|
|
|
@@ -2991,6 +3100,7 @@ def clip_videos_by_frame_ids(file_paths: List[Union[str, os.PathLike]],
|
|
|
2991
3100
|
frm_ids: List[List[int]],
|
|
2992
3101
|
save_dir: Optional[Union[str, os.PathLike]] = None,
|
|
2993
3102
|
gpu: Optional[bool] = False,
|
|
3103
|
+
codec: Optional[str] = None,
|
|
2994
3104
|
quality: Optional[int] = None):
|
|
2995
3105
|
|
|
2996
3106
|
"""
|
|
@@ -3033,13 +3143,14 @@ def clip_videos_by_frame_ids(file_paths: List[Union[str, os.PathLike]],
|
|
|
3033
3143
|
video_timer = SimbaTimer(start=True)
|
|
3034
3144
|
dir, video_name, ext = get_fn_ext(filepath=file_path)
|
|
3035
3145
|
s_f, e_f = frm_ids[cnt][0], frm_ids[cnt][1]
|
|
3036
|
-
|
|
3146
|
+
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__)
|
|
3147
|
+
video_codec = get_ffmpeg_codec(file_name=file_path) if codec is None else codec
|
|
3037
3148
|
if save_dir is not None:
|
|
3038
3149
|
out_path = os.path.join(save_dir, os.path.basename(file_path))
|
|
3039
3150
|
else:
|
|
3040
3151
|
out_path = os.path.join(dir, f"{video_name}_{s_f}_{e_f}{ext}")
|
|
3041
3152
|
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'
|
|
3153
|
+
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
3154
|
else:
|
|
3044
3155
|
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
3156
|
subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
|
|
@@ -3309,6 +3420,7 @@ def convert_to_mov(path: Union[str, os.PathLike],
|
|
|
3309
3420
|
def superimpose_video_progressbar(video_path: Union[str, os.PathLike],
|
|
3310
3421
|
bar_height: Optional[int] = 10,
|
|
3311
3422
|
color: Optional[str] = 'red',
|
|
3423
|
+
codec: Optional[str] = None,
|
|
3312
3424
|
position: Optional[Literal['top', 'bottom']] = 'bottom',
|
|
3313
3425
|
save_dir: Optional[Union[str, os.PathLike]] = None,
|
|
3314
3426
|
gpu: Optional[bool] = False) -> None:
|
|
@@ -3345,28 +3457,30 @@ def superimpose_video_progressbar(video_path: Union[str, os.PathLike],
|
|
|
3345
3457
|
video_path = find_all_videos_in_directory(directory=video_path, as_dict=True, raise_error=True)
|
|
3346
3458
|
video_paths = list(video_path.values())
|
|
3347
3459
|
else:
|
|
3348
|
-
raise InvalidInputError(msg='{} is not a valid file path or directory path.', source=superimpose_video_progressbar.__name__)
|
|
3460
|
+
raise InvalidInputError(msg=f'{video_path} is not a valid file path or directory path.', source=superimpose_video_progressbar.__name__)
|
|
3349
3461
|
if save_dir is not None:
|
|
3350
3462
|
check_if_dir_exists(in_dir=save_dir)
|
|
3351
3463
|
else:
|
|
3352
3464
|
save_dir, _, _ = get_fn_ext(filepath=video_paths[0])
|
|
3465
|
+
if codec is not None: check_valid_codec(codec=codec, raise_error=True, source=superimpose_video_progressbar.__name__)
|
|
3353
3466
|
for cnt, video_path in enumerate(video_paths):
|
|
3354
3467
|
video_meta_data = get_video_meta_data(video_path=video_path)
|
|
3355
3468
|
video_length = video_meta_data['video_length_s']
|
|
3356
3469
|
width, height = video_meta_data['width'], video_meta_data['height']
|
|
3357
3470
|
bar_height = int(height * (bar_height/100))
|
|
3358
3471
|
_, video_name, ext = get_fn_ext(filepath=video_path)
|
|
3359
|
-
|
|
3472
|
+
video_codec = get_ffmpeg_codec(file_name=video_path) if codec is None else codec
|
|
3473
|
+
stdout_information(msg=f'Inserting progress bar on video {video_name}...', source=superimpose_video_progressbar.__name__)
|
|
3360
3474
|
save_path = os.path.join(save_dir, f'{video_name}_progress_bar{ext}')
|
|
3361
3475
|
check_int(name=f'{superimpose_video_progressbar} height', value=bar_height, max_value=height, min_value=1)
|
|
3362
3476
|
if position == 'bottom':
|
|
3363
3477
|
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'
|
|
3478
|
+
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
3479
|
else:
|
|
3366
3480
|
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
3481
|
else:
|
|
3368
3482
|
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'
|
|
3483
|
+
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
3484
|
else:
|
|
3371
3485
|
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
3486
|
subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
|
|
@@ -3757,7 +3871,7 @@ def superimpose_elapsed_time(video_path: Union[str, os.PathLike],
|
|
|
3757
3871
|
|
|
3758
3872
|
def reverse_videos(path: Union[str, os.PathLike],
|
|
3759
3873
|
save_dir: Optional[Union[str, os.PathLike]] = None,
|
|
3760
|
-
codec:
|
|
3874
|
+
codec: Optional[str] = None,
|
|
3761
3875
|
quality: Optional[int] = 60,
|
|
3762
3876
|
gpu: Optional[bool] = False) -> None:
|
|
3763
3877
|
|
|
@@ -3803,13 +3917,15 @@ def reverse_videos(path: Union[str, os.PathLike],
|
|
|
3803
3917
|
os.makedirs(save_dir)
|
|
3804
3918
|
else:
|
|
3805
3919
|
raise InvalidInputError(msg=f'Path is not a valid file or directory path.', source=reverse_videos.__name__)
|
|
3920
|
+
if codec is not None: check_valid_codec(codec=codec, raise_error=True, source=reverse_videos.__name__)
|
|
3806
3921
|
for file_cnt, file_path in enumerate(file_paths):
|
|
3807
3922
|
_, video_name, ext = get_fn_ext(filepath=file_path)
|
|
3808
|
-
|
|
3923
|
+
stdout_information(msg=f'Reversing video {video_name} (Video {file_cnt+1}/{len(file_paths)})...', source=reverse_videos.__name__)
|
|
3924
|
+
video_codec = get_ffmpeg_codec(file_name=file_path) if codec is None else codec
|
|
3809
3925
|
_ = get_video_meta_data(video_path=file_path)
|
|
3810
3926
|
out_path = os.path.join(save_dir, f'{video_name}{ext}')
|
|
3811
3927
|
if not gpu:
|
|
3812
|
-
cmd = f'ffmpeg -i "{file_path}" -vf reverse -af areverse -c:v {
|
|
3928
|
+
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
3929
|
else:
|
|
3814
3930
|
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
3931
|
subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
|
|
@@ -5080,7 +5196,7 @@ def change_playback_speed(video_path: Union[str, os.PathLike],
|
|
|
5080
5196
|
quality: int = 60,
|
|
5081
5197
|
gpu: bool = False,
|
|
5082
5198
|
verbose: bool = True,
|
|
5083
|
-
codec: str =
|
|
5199
|
+
codec: Optional[str] = None):
|
|
5084
5200
|
"""
|
|
5085
5201
|
Change the playback speed of a video file. Speed > 1.0 makes the video faster, speed < 1.0 makes it slower.
|
|
5086
5202
|
|
|
@@ -5123,6 +5239,10 @@ def change_playback_speed(video_path: Union[str, os.PathLike],
|
|
|
5123
5239
|
else:
|
|
5124
5240
|
save_path = os.path.join(dir, f'{video_name}_playback_speed_{speed}{ext}')
|
|
5125
5241
|
video_pts = 1.0 / speed
|
|
5242
|
+
if codec is None:
|
|
5243
|
+
codec = get_ffmpeg_codec(file_name=video_path)
|
|
5244
|
+
else:
|
|
5245
|
+
check_valid_codec(codec=codec, raise_error=True, source=change_playback_speed.__name__)
|
|
5126
5246
|
if gpu:
|
|
5127
5247
|
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
5248
|
else:
|
|
@@ -5140,7 +5260,7 @@ def change_playback_speed_dir(data_dir: Union[str, os.PathLike],
|
|
|
5140
5260
|
quality: int = 60,
|
|
5141
5261
|
gpu: bool = False,
|
|
5142
5262
|
verbose: bool = True,
|
|
5143
|
-
codec: str =
|
|
5263
|
+
codec: Optional[str] = None):
|
|
5144
5264
|
"""
|
|
5145
5265
|
Change the playback speed of all video files in a directory. Speed > 1.0 makes videos faster, speed < 1.0 makes them slower.
|
|
5146
5266
|
|
|
@@ -5174,7 +5294,7 @@ def change_playback_speed_dir(data_dir: Union[str, os.PathLike],
|
|
|
5174
5294
|
check_float(name=f'{change_playback_speed.__name__} speed', value=speed, min_value=0.000001, max_value=100, raise_error=True)
|
|
5175
5295
|
check_int(name=f'{change_playback_speed.__name__} quality', value=quality, min_value=1, max_value=100, raise_error=True)
|
|
5176
5296
|
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__)
|
|
5297
|
+
if codec is not None: check_valid_codec(codec=codec, raise_error=True, source=change_playback_speed_dir.__name__)
|
|
5178
5298
|
video_dict = find_all_videos_in_directory(directory=data_dir, as_dict=True, raise_error=True, sort_alphabetically=True)
|
|
5179
5299
|
total_video_cnt = len(list(video_dict.keys()))
|
|
5180
5300
|
if save_dir is not None:
|
|
@@ -5186,9 +5306,10 @@ def change_playback_speed_dir(data_dir: Union[str, os.PathLike],
|
|
|
5186
5306
|
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
5307
|
for video_cnt, (video_name, video_path) in enumerate(video_dict.items()):
|
|
5188
5308
|
_, _, ext = get_fn_ext(filepath=video_path)
|
|
5309
|
+
video_codec = get_ffmpeg_codec(file_name=video_path) if codec is None else codec
|
|
5189
5310
|
save_path = os.path.join(save_dir, f'{video_name}{ext}')
|
|
5190
5311
|
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=
|
|
5312
|
+
change_playback_speed(video_path=video_path, speed=speed, save_path=save_path, quality=quality, codec=video_codec, verbose=False)
|
|
5192
5313
|
|
|
5193
5314
|
timer.stop_timer()
|
|
5194
5315
|
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.5
|
|
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
|