Вопрос или проблема
Я использую эту команду 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
Эта команда извлечет один кадр в секунду из видео, независимо от исходной частоты кадров видеофайла. Это значит, что выходные данные будут стандартизированы на один кадр в секунду.
Применение
-
Определите основную цель. Необходимо понимать, с какой целью извлекаются кадры. Например, если это статистический анализ, может быть оправдано извлечение фиксированного количества кадров в секунду по всему видео.
-
Используйте адаптивные настройки fps. Включите использование фильтра
fps
для извлечения кадров с заданной частотой.ffmpeg -i input.mp4 -vf "fps=1,scale=320:200" frame_%04d.png
Здесь
fps=1
указывает на извлечение одного кадра в секунду с масштабированием до указанного размера. -
Тестирование и проверка. Протестируйте подобный подход на разных видео с разной частотой кадров с целью достижения стабильных результатов.
-
Автоматизация. Если ситуация требует фильтра
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 видео. Тестирование и адаптация ваших скриптов под различную входную частоту кадров позволит избежать столь нежелательных расхождений и создать гибкое, надежное решение для обработки видео.