Извлечение кадров из видео с помощью ffmpeg, непредсказуемое поведение

Вопрос или проблема

Я использую эту команду ffmpeg для создания набора снимков изображений из видео:

  ffmpegCommand([
    "-i",
    inputPath,
    "-vf",
    `select="not(mod(n,${frame_interval}))",setpts="N/(${fps}*TB)"`,
    "-s",
    `320x200`,
    "-f",
    "image2",
    outputPath,
  ]);

это наиболее точный метод по кадрам, согласно моим исследованиям в Google и Stack Overflow.

Это хорошо работает, когда видео около 30 кадров в секунду, с интервалом кадров около 250.

Но когда видео 5 кадров в секунду, интервал кадров очевидно должен быть меньше, около 70, потому что в видео меньше кадров. Но потом я получаю огромное количество изображений.

// пример для видео 30 кадров в секунду
ffmpeg -i test30.mp4 -vf select="not(mod(n,250))",setpts="N/(29.97*TB) .....

// пример для видео 5 кадров в секунду
ffmpeg -i test5.mp4 -vf select="not(mod(n,70))',setpts="N/(4.907*TB) ......

Что здесь может быть не так?

Ответ или решение

Ваша задача — извлекать кадры из видеофайлов с использованием командного инструмента ffmpeg. На первый взгляд, команда, описанная в задаче, кажется корректной: вы используете фильтр select для выбора кадров и фильтр setpts для того, чтобы задать временные метки кадров. Однако возникает проблема при работе с видео разной частоты кадров, в частности при уменьшении fps, что приводит к некорректному количеству извлекаемых кадров. Для глубокого анализа проблемы рассмотрим теорию, пример и способы её применения к вашей задаче.

Теория

1. Основы работы с кадрами в ffmpeg

ffmpeg — это мощный инструмент для работы с медиаконтентом, который позволяет выполнять множество операций с видео и аудиофайлами. В вашем случае используется фильтр select, который работает с выражением not(mod(n, frame_interval)). Это выражение оставляет каждый frame_interval-ый кадр.

  • n: Номер текущего кадра (отсчет начинается с нуля).
  • mod(n, frame_interval): Возвращает остаток от деления номера кадра на величину frame_interval.
  • select="not(mod(n, frame_interval))": Выбирает кадры, которые делятся нацело на frame_interval.

2. Ошибка в подходе

Когда вы задаете фиксированный frame_interval (скажем, 70), который работает на одном видео (например, с 30 fps), он может вести к неожиданным результатам на другом видео с нестандартной частотой кадров (например, 5 fps). Это связано с тем, что интервал выбора кадров не адаптирован к количеству кадросекундности (fps) видеофайла.

3. Введение фильтра fps

Вместо использования избирательного фильтра на основе номера кадра (n), можно использовать фильтр fps, который позволяет извлекать кадры с заданной частотой в видео. Он эффективен, когда необходимо поддерживать определенное количество кадров в секунду в выходных изображениях, адаптируя их к частоте кадров входного видео.

Пример

Для видео с различной fps можем использовать следующую команду:

ffmpeg -i input.mp4 -vf "fps=1" -s 320x200 frame_%04d.png

Эта команда извлечет один кадр в секунду из видео, независимо от исходной частоты кадров видеофайла. Это значит, что выходные данные будут стандартизированы на один кадр в секунду.

Применение

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

  2. Используйте адаптивные настройки fps. Включите использование фильтра fps для извлечения кадров с заданной частотой.

    ffmpeg -i input.mp4 -vf "fps=1,scale=320:200" frame_%04d.png

    Здесь fps=1 указывает на извлечение одного кадра в секунду с масштабированием до указанного размера.

  3. Тестирование и проверка. Протестируйте подобный подход на разных видео с разной частотой кадров с целью достижения стабильных результатов.

  4. Автоматизация. Если ситуация требует фильтра select, используйте скрипт на Python или Bash, который автоматизирует подсчёт подходящего frame_interval на основе реальной fps видео.

    import subprocess
    
    def get_fps(video_path):
       # Получаем информацию о видео
       result = subprocess.run(
           ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", 
            "stream=r_frame_rate", "-of", "default=noprint_wrappers=1:nokey=1", 
            video_path],
           stdout=subprocess.PIPE,
           stderr=subprocess.PIPE
       )
       # Возвращаем fps
       return eval(result.stdout.strip())
    
    # Пример использования
    video_fps = get_fps('test5.mp4')
    frame_interval = int(video_fps) # Пример автоматического расчёта
    
    # Применение в ffmpeg
    subprocess.run([
       "ffmpeg", "-i", "test5.mp4", "-vf", f"select='not(mod(n,{frame_interval}))'",
       "frame_%04d.png"
    ])

    Таким образом, вы можете автоматизировать процесс выбора интервала кадров в зависимости от частоты кадров видео.

Заключение

Непредсказуемое поведение при извлечении кадров с помощью ffmpeg может быть исправлено за счет правильной настройки параметров команды. Для обеспечения стабильности результатов рекомендовано использовать фильтр fps для извлечения кадров с фиксированной частотой или автоматизировать расчет frame_interval в зависимости от fps видео. Тестирование и адаптация ваших скриптов под различную входную частоту кадров позволит избежать столь нежелательных расхождений и создать гибкое, надежное решение для обработки видео.

Оцените материал
Добавить комментарий

Капча загружается...