Цифровые медиа, такие как видео, открыли множество возможностей для творчества, когда при обработке видео и аудио файлов можно создавать потрясающие эффекты. Многие программы и инструменты для обработки мультимедиа предлагают функцию реверса.
В этом уроке вы узнаете, как можно перевернуть видео на Python с помощью
Основная идея кода, который вы здесь увидите, заключается в том, что мы извлекаем все кадры из видео с помощью настраиваемого параметра fps, а затем монтируем эти кадры в обратном порядке.
Для начала установим необходимые библиотеки:
$ pip install tqdm moviepy numpy
Начнем с импорта наших модулей:
from moviepy.editor import VideoFileClip, ImageSequenceClip import numpy as np import os from datetime import timedelta, datetime from glob import glob from tqdm import tqdm import shutil
Затем, чтобы избежать избыточности, я привожу приведенный ниже код из этого руководства:
# то есть, если видео длительностью 30 секунд, сохраняется с частотой 10 кадров в секунду, # то всего сохраняется 300 кадров SAVING_FRAMES_PER_SECOND = 30 def format_timedelta(td): """Служебная функция для классного форматирования объектов timedelta (например, 00: 00: 20.05) исключая микросекунды и сохраняя миллисекунды""" result = str(td) try: result, ms = result.split(".") except ValueError: return result + ".00".replace(":", "-") ms = int(ms) ms = round(ms / 1e4) return f"{result}.{ms:02}".replace(":", "-") def extract_frames(video_file, verbose=1): # загрузить видеоклип video_clip = VideoFileClip(video_file) # создаем папку по названию видео файла filename, _ = os.path.splitext(video_file) if not os.path.isdir(filename): os.mkdir(filename) # если SAVING_FRAMES_PER_SECOND больше FPS видео, то установите минимум из них saving_frames_per_second = min(video_clip.fps, SAVING_FRAMES_PER_SECOND) # если SAVING_FRAMES_PER_SECOND установлен в 0, шаг равен 1/fps, иначе step = 1 / video_clip.fps if saving_frames_per_second == 0 else 1 / saving_frames_per_second iteration = np.arange(0, video_clip.duration, step) if verbose: iteration = tqdm(iteration, desc="\nИзвлечен видеокадр") # перебираем каждый возможный кадр for current_duration in iteration: # форматируем имя файла и сохраняем его frame_duration_formatted = format_timedelta(timedelta(seconds=current_duration)).replace(":", "-") frame_filename = os.path.join(filename, f"frame{frame_duration_formatted}.jpg") # сохраняем кадр с текущей длительностью video_clip.save_frame(frame_filename, current_duration) return filename, video_clip.fps
Вы узнаете много подробностей в записке об извлечению кадров. Однако вкратце, функция extract_frames()
принимает путь к видеофайлу в качестве параметра и извлекает кадры с соответствующей продолжительностью в папку, названную по имени исходного видеофайла. Наконец, она возвращает имя этой папки.
Теперь, создадим функцию, которая считывает эти кадры извлечения в обратном порядке и сохраняет их как перевернутое видео:
def reverse_video(frames_path, video_fps, remove_extracted_frames=True): frame_files = glob(os.path.join(frames_path, "*")) # сортируем по длительности в порядке возростания frame_files = sorted(frame_files, key=lambda d: datetime.strptime(d.split("frame")[1], "%H-%M-%S.%f.jpg"), reverse=True) # вычисляем FPS, получая минимум между исходным FPS и установленным нами параметром saving_frames_per_second = min(video_fps, SAVING_FRAMES_PER_SECOND) if saving_frames_per_second == 0: # если параметр установлен на 0, автоматически установить его на исходное fps видео saving_frames_per_second = video_fps print("Сохраненение видео с FPS:", saving_frames_per_second) # загружаем в клип кадры последовательности изображений (MoviePy) image_sequence_clip = ImageSequenceClip(frame_files, fps=saving_frames_per_second) # записываем видеофайл на диск output_filename = f"{frames_path}-inverted.mp4" image_sequence_clip.write_videofile(output_filename) if remove_extracted_frames: # если установлено значение True, то удалить папку, содержащую извлеченные кадры shutil.rmtree(frames_path)
Функция reverse_video()
ожидает в качестве аргумента имя папки с извлеченными предыдущей функцией видеокадрами (фреймами). Используем функцию glob()
из модуля
Затем сортируем эти файлы по длительности в порядке убывания. После этого, передаем эти кадры в обратном порядке объекту ImageSequenceClip()
из MoviePy и устанавливаем FPSએ на минимум между SAVING_FRAMES_PER_SECOND, который был использован в процессе извлечения кадров, и исходным FPSએ видео. Причина в том, что при установке более высокого FPS, чем в исходном видео, результирующее видео будет ускоренным. Затем используем метод write_videofile()
для сохранения перевернутого видео в видеофайл на диске.
Если вы установите для параметра remove_extracted_frames
значение True (по умолчанию), временная папка, в которой находятся извлеченные кадры, будет удалена вместе с её содержимым.
Наконец, воспользуемся этими функциями для решения нашей задачи:
if __name__ == "__main__": import sys video_file = sys.argv[1] import time # замерим время для раскадровки видеоролика begtime = time.perf_counter() frames_folder_path, video_fps = extract_frames(video_file) endtime = time.perf_counter() print(f"\nНа раскадровку потребовалось {endtime - begtime} с") # замерим время для сборки реверсивного ролика begtime = time.perf_counter() reverse_video(frames_folder_path, video_fps=video_fps) endtime = time.perf_counter() print(f"\nНа сборку видео потребовалось {endtime - begtime} с")
Готово, мастер! Попробуем с видео заставки
$ python reverse_video.py is42.mp4
В консоле можно увидеть что-то подобное:
И перевернутое видео в файле is42-inverted.mp4 появилось в текущем каталоге!
Заключение
Если ваше видео довольно длинное, обязательно уменьшите FPS (параметр SAVING_FRAMES_PER_SECOND
), я ууменьшал его до 10
, вы можете увеличить его, если чувствуете задержку в выходном видео, что увеличит размер видео, а также время выполнения программы по извлечению кадров и их загрузке в обратном порядке.
Очевидно, что в выходном видео не будет звука, вы можете использовать AudioClip()
, загруженный из аудио (возможно, аудио, извлеченное из исходного видео), и просто установить image_sequence_clip.audio
для этого вновь созданного объекта AudioClip()
, а затем продолжить тот-же процесс сохранения видео.
Полный код приведен ниже, а видео можете забраь с Yuotube:
from moviepy.editor import VideoFileClip, ImageSequenceClip import numpy as np import os from datetime import timedelta, datetime from glob import glob from tqdm import tqdm import shutil # то есть, если видео длительностью 30 секунд, сохраняется с частотой 10 кадров в секунду, # то всего сохраняется 300 кадров SAVING_FRAMES_PER_SECOND = 30 def format_timedelta(td): """Служебная функция для классного форматирования объектов timedelta (например, 00: 00: 20.05) исключая микросекунды и сохраняя миллисекунды""" result = str(td) try: result, ms = result.split(".") except ValueError: return result + ".00".replace(":", "-") ms = int(ms) ms = round(ms / 1e4) return f"{result}.{ms:02}".replace(":", "-") def extract_frames(video_file, verbose=1): # загрузить видеоклип video_clip = VideoFileClip(video_file) # создаем папку по названию видео файла filename, _ = os.path.splitext(video_file) if not os.path.isdir(filename): os.mkdir(filename) # если SAVING_FRAMES_PER_SECOND больше FPS видео, то установите минимум из них saving_frames_per_second = min(video_clip.fps, SAVING_FRAMES_PER_SECOND) # если SAVING_FRAMES_PER_SECOND установлен в 0, шаг равен 1/fps, иначе step = 1 / video_clip.fps if saving_frames_per_second == 0 else 1 / saving_frames_per_second iteration = np.arange(0, video_clip.duration, step) if verbose: iteration = tqdm(iteration, desc="\nИзвлечен видеокадр") # перебираем каждый возможный кадр for current_duration in iteration: # форматируем имя файла и сохраняем его frame_duration_formatted = format_timedelta(timedelta(seconds=current_duration)).replace(":", "-") frame_filename = os.path.join(filename, f"frame{frame_duration_formatted}.jpg") # сохраняем кадр с текущей длительностью video_clip.save_frame(frame_filename, current_duration) return filename, video_clip.fps def reverse_video(frames_path, video_fps, remove_extracted_frames=True): frame_files = glob(os.path.join(frames_path, "*")) # сортируем по длительности в порядке возростания frame_files = sorted(frame_files, key=lambda d: datetime.strptime(d.split("frame")[1], "%H-%M-%S.%f.jpg"), reverse=True) # вычисляем FPS, получая минимум между исходным FPS и установленным нами параметром saving_frames_per_second = min(video_fps, SAVING_FRAMES_PER_SECOND) if saving_frames_per_second == 0: # если параметр установлен на 0, автоматически установить его на исходное fps видео saving_frames_per_second = video_fps print("Сохраненение видео с FPS:", saving_frames_per_second) # загружаем в клип кадры последовательности изображений (MoviePy) image_sequence_clip = ImageSequenceClip(frame_files, fps=saving_frames_per_second) # записываем видеофайл на диск output_filename = f"{frames_path}-inverted.mp4" image_sequence_clip.write_videofile(output_filename) if remove_extracted_frames: # если установлено значение True, то удалить папку, содержащую извлеченные кадры shutil.rmtree(frames_path) if __name__ == "__main__": import sys video_file = sys.argv[1] import time # замерим время для раскадровки видеоролика begtime = time.perf_counter() frames_folder_path, video_fps = extract_frames(video_file) endtime = time.perf_counter() print(f"\nНа раскадровку потребовалось {endtime - begtime} с") # замерим время для сборки реверсивного ролика begtime = time.perf_counter() reverse_video(frames_folder_path, video_fps=video_fps) endtime = time.perf_counter() print(f"\nНа сборку видео потребовалось {endtime - begtime} с")
По мотивам
Как с помощью Python перевернуть видео, опубликовано К ВВ, лицензия — Creative Commons Attribution-NonCommercial 4.0 International.
Респект и уважуха