Конвертер MP4 видео в GIF анимацию на Python

Конвертер MP4 видео в GIF анимацию на Python

С помощью Python можно конвертировать видео в формате MP4 в анимированные GIF изображения. Разумеется, с этой задачей прекрасно справляются готовые приложения. Однако создание собственного конвертера средствами Python – интересный и полезный опыт.

В этом руководстве мы рассмотрим следующие задачи:

  1. Извлечение кадров из MP4 видео.
  2. Конвертацию кадров в GIF анимацию.
  3. Создание пользовательского интерфейса для программы-конвертера.

Приступим.

Что нам потребуется

Для распознавания MP4 видео, извлечения и конвертации кадров в формат JPG в Python используется библиотека OpenCV. Установим ее с помощью pip, системы управления пакетами:

python3 -m pip install opencv-python

Кроме того, для создания GIF-анимации из кадров, сохраненных в формате JPG, нам также понадобится библиотека Pillow. Ее тоже устанавливают с помощью pip:

python3 -m pip install Pillow

Для создания графического пользовательского интерфейса мы воспользуемся пакетом PySimpleGUI. Установим его с помощью следующей команды:

python3 -m pip install PySimpleGUI

Пользователям среды разработки Anaconda устанавливать OpenCV и Pillow не нужно – они уже включены в набор компонентов. Для Anaconda надо дополнительно установить лишь PySimpleGUI.

Извлечение кадров из MP4 видео

Первый шаг к созданию GIF анимации – выбор видео, из которого будут извлечены нужные кадры. В нашем случае мы воспользуемся видеороликом, который демонстрирует процесс установки фреймворка Flask, который используется для веб-разработки в Python.

Для извлечения отдельных кадров нужно написать специальную функцию. Создайте новый файл, назовите его mp4_converter.py и вставьте в него следующий код:

import cv2


def convert_mp4_to_jpgs(path):
    video_capture = cv2.VideoCapture(path)
    still_reading, image = video_capture.read()
    frame_count = 0
    while still_reading:
        cv2.imwrite(f"output/frame_{frame_count:03d}.jpg", image)
        
        # read next image
        still_reading, image = video_capture.read()
        frame_count += 1


if __name__ == "__main__":
    convert_mp4_to_jpgs("flask_demo.mp4")

Данная функция принимает путь к MP4 файлу. Затем она открывает видео, используя метод cv2.VideoCapture(path). Этот метод при желании можно использовать для покадрового сохранения всего видеоролика. Для сохранения извлеченных кадров применяем метод cv2.imwrite().

После выполнения кода вы увидите, что из видео продолжительностью в 7 секунд получилось целых 235 кадров. После того, как кадры сохранены, можно приступать к созданию анимированного GIF изображения.

Создание GIF анимации из отдельных изображений

На этом этапе мы напишем код для создания GIF изображения на основе кадров, которые мы извлекли из MP4 видео с помощью OpenCV.

Здесь нам и пригодится библиотека Pillow – с ее помощью можно превратить в GIF анимацию набор изображений, находящихся в указанной директории. Создайте новый файл, назовите его gif_maker.py, сохраните в нем приведенный ниже код:

import glob

from PIL import Image


def make_gif(frame_folder):
    images = glob.glob(f"{frame_folder}/*.jpg")
    images.sort()
    frames = [Image.open(image) for image in images]
    frame_one = frames[0]
    frame_one.save("flask_demo.gif", format="GIF", append_images=frames,
                   save_all=True, duration=50, loop=0)
    

if __name__ == "__main__":
    make_gif("output")

В этом фрагменте кода Python для поиска JPG файлов использует модуль glob. После этого кадры сортируются в нужном порядке, а на заключительном этапе JPG изображения сохраняются в формате GIF.

Теперь мы готовы приступить к созданию пользовательского интерфейса для нашего конвертера.

Интерфейс для приложения, конвертирующего MP4 в GIF

PySimpleGUI – кроссплатформенный фреймворк: работает на Linux, Mac OS и Windows. В состав данного пакета входят несколько библиотек для разработки пользовательского интерфейса, в том числе Tkinter, wxPython и PyQt. Таким образом, когда вы на первом этапе установили PySimpleGUI, в вашем распоряжении по умолчанию оказался Tkinter.

Создайте новый файл в среде разработки, назовите его mp4_converter_gui.py, сохраните в нем приведенный ниже код:

# mp4_converter_gui.py

import cv2
import glob
import os
import shutil
import PySimpleGUI as sg

from PIL import Image

file_types = [("MP4 (*.mp4)", "*.mp4"), ("All files (*.*)", "*.*")]


def convert_mp4_to_jpgs(path):
    video_capture = cv2.VideoCapture(path)
    still_reading, image = video_capture.read()
    frame_count = 0
    if os.path.exists("output"):
        # remove previous GIF frame files
        shutil.rmtree("output")
    try:
        os.mkdir("output")
    except IOError:
        sg.popup("Error occurred creating output folder")
        return
    
    while still_reading:
        cv2.imwrite(f"output/frame_{frame_count:05d}.jpg", image)
        
        # read next image
        still_reading, image = video_capture.read()
        frame_count += 1


def make_gif(gif_path, frame_folder="output"):
    images = glob.glob(f"{frame_folder}/*.jpg")
    images.sort()
    frames = [Image.open(image) for image in images]
    frame_one = frames[0]
    frame_one.save(gif_path, format="GIF", append_images=frames,
                   save_all=True, duration=50, loop=0)


def main():
    layout = [
        [
            sg.Text("MP4 File"),
            sg.Input(size=(25, 1), key="-FILENAME-", disabled=True),
            sg.FileBrowse(file_types=file_types),
        ],
        [
            sg.Text("GIF File Save Location"),
            sg.Input(size=(25, 1), key="-OUTPUTFILE-", disabled=True),
            sg.SaveAs(file_types=file_types),
            
        ],
        [sg.Button("Convert to GIF")],
    ]

    window = sg.Window("MP4 to GIF Converter", layout)

    while True:
        event, values = window.read()
        mp4_path = values["-FILENAME-"]
        gif_path = values["-OUTPUTFILE-"]
        if event == "Exit" or event == sg.WIN_CLOSED:
            break
        if event in ["Convert to GIF"]:
            if mp4_path and gif_path:
                convert_mp4_to_jpgs(mp4_path)
                make_gif(gif_path)
                sg.popup(f"GIF created: {gif_path}")

    window.close()


if __name__ == "__main__":
    main()

Изучаем классы в Python: построение классов и определение объектов в Python 3

Код получился довольно длинный. Для упрощения восприятия, давайте рассмотрим каждый этап по отдельности.

В самом начале находится раздел импорта модулей:

# mp4_converter_gui.py

import cv2
import glob
import os
import shutil
import PySimpleGUI as sg

from PIL import Image

file_types = [("MP4 (*.mp4)", "*.mp4"), ("All files (*.*)", "*.*")]

В этом фрагменте в приложение импортируются все модули и пакеты, необходимые для создания GUI, графического интерфейса пользователя. В этом процессе участвуют пакеты OpenCV (cv2), Pillow Image class и PySimpleGUI, а также несколько модулей из стандартного набора Python. Также на этом этапе объявляется переменная, которой в списке кортежей передаются все допустимые форматы файлов.

Переходим к первой функции программы:

def convert_mp4_to_jpgs(path):
    video_capture = cv2.VideoCapture(path)
    still_reading, image = video_capture.read()
    frame_count = 0
    if os.path.exists("output"):
        # remove previous GIF frame files
        shutil.rmtree("output")
    try:
        os.mkdir("output")
    except IOError:
        sg.popup("Error occurred creating output folder")
        return
    
    while still_reading:
        cv2.imwrite(f"output/frame_{frame_count:05d}.jpg", image)
        
        # read next image
        still_reading, image = video_capture.read()
        frame_count += 1

Это слегка модифицированная версия функции, которую мы создали на первом этапе. Как и в первоначальном варианте, здесь использован метод VideoCapture() для захвата кадров из MP4 видео и их последующего сохранения в виде отдельных изображений.

Однако на этот раз мы добавили проверку существования и удаления выходной папки output – для того, чтобы избежать случайного сохранения кадров из двух MP4 файлов в одной и той же директории: из такого смешанного набора получится очень странная гифка.

Последующий фрагмент кода создает выходную папку output после удаления существующей, или выдает сообщение об ошибке в случае неполадки. Остальная часть кода осталась без изменений.

Перейдем к следующей функции:

def make_gif(gif_path, frame_folder="output"):
    images = glob.glob(f"{frame_folder}/*.jpg")
    images.sort()
    frames = [Image.open(image) for image in images]
    frame_one = frames[0]
    frame_one.save(gif_path, format="GIF", append_images=frames,
                   save_all=True, duration=50, loop=0)

Модуль re Python – осваиваем регулярные выражения Python

Здесь для создания GIF файла используется метод make_gif(). Код практически такой же, как и в первоначальном варианте, за исключением того, что здесь мы передаем путь к GIF файлу, чтобы каждая анимация сохранялась с новым названием.

В заключительной части кода описаны параметры окна и интерфейс приложения:

def main():
    layout = [
        [
            sg.Text("MP4 File"),
            sg.Input(size=(25, 1), key="-FILENAME-", disabled=True),
            sg.FileBrowse(file_types=file_types),
        ],
        [
            sg.Text("GIF File Save Location"),
            sg.Input(size=(25, 1), key="-OUTPUTFILE-", disabled=True),
            sg.SaveAs(file_types=file_types),
            
        ],
        [sg.Button("Convert to GIF")],
    ]

    window = sg.Window("MP4 to GIF Converter", layout)

    while True:
        event, values = window.read()
        mp4_path = values["-FILENAME-"]
        gif_path = values["-OUTPUTFILE-"]
        if event == "Exit" or event == sg.WIN_CLOSED:
            break
        if event in ["Convert to GIF"]:
            if mp4_path and gif_path:
                convert_mp4_to_jpgs(mp4_path)
                make_gif(gif_path)
                sg.popup(f"GIF created: {gif_path}")

    window.close()


if __name__ == "__main__":
    main()

При использовании фреймворка PySimpleGUI элементы интерфейса Elements включаются в список layout(«расположение», «раскладка»). К примеру, в нашем проекте в список Elements входят следующие элементы интерфейса:

  1. Text – два объекта, которые используются в качестве названий текстовых полей ввода.
  2. Input – также в двойном экземпляре. В одно поле ввода вводится путь к MP4 файлу, другое отображает путь к сохраненному GIF-файлу.
  3. FileBrowse – кнопка для открытия диалогового окна «Найти файл».
  4. SaveAs – кнопка «Сохранить файл» для записи GIF файла с нужным названием.
  5. Button – кнопка для запуска процесса конвертации.

На следующем этапе список элементов интерфейса передается объекту sg.Window, который отвечает за отображение окна приложения. Окно имеет название, кнопки для сворачивания, разворачивания и для выхода из программы.

Для обработки событий в окне приложения мы создаем цикл while и считываем события объекта Window. Таким образом мы получаем значения из двух объектов sg.Input(), которые содержат пути к MP4 и GIF файлам.

Когда пользователь нажимает кнопку «Convert to GIF» («Конвертировать в GIF»), программа перехватывает событие и вызывает сначала функцию convert_mp4_to_jpgs(), а затем – make_gif(). Если процесс конвертации завершается успешно, всплывающее окно уведомит пользователя о том, что GIF файл создан, и продемонстрирует путь к нему.

Запустите выполнение кода. Вы увидите вот такое окно:

Конвертер MP4 видео в GIF анимацию на Python

Неплохо получилось, верно?

Подведем итоги

Теперь у вас есть весь необходимый код для конвертирования MP4 видеофайлов в GIF- анимацию. Кое-что в нашей программе можно улучшить – попробуйте, например, добавить обработку ошибок в функциях, чтобы случайно не перезаписать старые GIF файлы новыми.

Еще можно предусмотреть уменьшение размера кадров, это уменьшает итоговый размер GIF анимации. Другой способ уменьшения GIF файлов – сжатие JPG кадров перед созданием анимации.

Есть и другие способы улучшить и дополнить код, рассмотренный в этом руководстве. Подумайте, какой еще функциональности не хватает этому приложению, и добавьте ее.

Источник: www.internet-technologies.ru