Цифровые медиа, такие как видео, открыли множество возможностей для творчества, когда при обработке видео и аудио файлов можно создавать потрясающие эффекты. Многие программы и инструменты для обработки мультимедиа предлагают функцию реверса.
В этом уроке вы узнаете, как можно перевернуть видео на 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.
Респект и уважуха

