ffmpeg - удалить последовательно дублирующиеся кадры
Есть ли способ обнаружить дубликаты кадров в видео, используя ffmpeg
,
Я старался -vf
флаг с select=gt(scene\,0.xxx)
для смены сцены. Но это не сработало для моего случая.
4 ответа
Используйте фильтр mpdecimate, цель которого - "отбросить кадры, которые не сильно отличаются от предыдущего кадра, чтобы уменьшить частоту кадров".
Это сгенерирует консольное показание, показывающее, какие кадры фильтр считает дубликатами.
ffmpeg -i input.mp4 -vf mpdecimate -loglevel debug -f null -
Создать видео с удаленными дубликатами
ffmpeg -i input.mp4 -vf mpdecimate,setpts=N/FRAME_RATE/TB out.mp4
Выражение фильтра th setpts создает плавные временные метки для видео со скоростью FRAME_RATE fps. См. Объяснение временных меток в разделе Что такое временная шкала видео, временная база или временная метка в ffmpeg?
У меня также была эта проблема, и превосходный ответ Джиана, приведенный выше, заставил меня начать, но в результате я получил десинхронизированное аудио, поэтому мне пришлось изучить больше вариантов:
mpdecimate против десятичных фильтров
mpdecimate
это стандартная рекомендация, которую я нашел по всему SO и в Интернете, но я не думаю, что это должно быть первым выбором- он использует эвристику, поэтому он может и пропустит некоторые дубликаты кадров
- вы можете настроить обнаружение с
frac
параметр, но это дополнительная работа, которую вы можете избежать, если можете - на самом деле не нужно работать с
mp4
контейнер ( источник), но я использовалmkv
так что это ограничение не распространялось на мой случай, но приятно знать о нем
decimate
удаляет кадры точно, но это полезно только для периодически возникающих дубликатов
обнаружено против фактической частоты кадров
- поэтому у вас есть мультимедийный файл с дублирующимися кадрами, поэтому рекомендуется убедиться, что обнаруженная частота кадров соответствует фактической
ffprobe in.mkv
выведет обнаруженный FPS; это может выглядеть такStream #0:0: Video: h264 (Main), yuvj420p(pc, bt709, progressive), 1920x1080, SAR 1:1 DAR 16:9, 25 fps, 25 tbr, 1k tbn, 50 tbc (default)
фактическую частоту кадров можно узнать, если открыть медиа
in.mkv
в медиаплеере, который позволяет вам шагать по одному кадру за раз; затем посчитайте шаги, необходимые для увеличения времени воспроизведения на 1 секунду, в моем случае это было 30 кадров в секунду- Для меня это не стало большим сюрпризом, потому что каждый 6-й кадр был дубликатом (5 хороших кадров и 1 дубликат), поэтому после 25 хороших кадров было также 5 дубликатов.
что такое N/FRAME_RATE/TB
кроме использования
FRAME_RATE
переменнаяN/FRAME_RATE/TB
соответствует приведенному ниже примеру из документации ffmpeg ( источник)Установите фиксированную скорость 25 кадров в секунду:
setpts=N/(25*TB)
Математика, стоящая за этим, прекрасно объясняется в разделе Что такое временная шкала видео, временная база или временная метка в ffmpeg?
- он в основном вычисляет временную метку для каждого кадра и умножает ее на временную базу
TB
повысить точность
- он в основном вычисляет временную метку для каждого кадра и умножает ее на временную базу
FRAME_RATE
переменная против буквального значения FPS (например, 25)
- Вот почему важно знать ваш обнаруженный и фактический FPS
- если обнаруженный FPS соответствует вашему фактическому FPS (например, оба - 30 кадров в секунду), вы можете с радостью использовать
FRAME_RATE
переменная вN/FRAME_RATE/TB
- но если обнаруженный FPS отличается, чем вы должны рассчитать
FRAME_RATE
самостоятельно- в моем случае мой фактический FPS был 30 кадров в секунду, и я удалял каждый 6-й кадр, поэтому целевой FPS равен 25, что приводит к
N/25/TB
- если бы я использовал
FRAME_RATE
(и я действительно пытался это сделать) он будет принимать неправильно обнаруженные кадры в секунду 25 кадров, т.е.FRAME_RATE=25
пропустите это черезmpdecimate
фильтр, который будет удалять каждый 6-й кадр, и он будет обновляться доFRAME_RATE=20.833
такN/FRAME_RATE/TB
будет на самом делеN/20.833/TB
что совершенно неправильно
- если бы я использовал
- в моем случае мой фактический FPS был 30 кадров в секунду, и я удалял каждый 6-й кадр, поэтому целевой FPS равен 25, что приводит к
использовать или не использовать setpts
- поэтому фильтр setpts уже стал довольно сложным, особенно из-за беспорядка FPS, который могут создавать дублирующиеся кадры
- хорошая новость в том, что на самом деле вам вообще может не понадобиться фильтр setpts
вот что я использовал с хорошими результатами
ffmpeg -i in.mkv -vf mpdecimate out.mkv
ffmpeg -i in.mkv -vf decimate=cycle=6, setpts=N/25/TB out.mkv
но следующее дало мне десинхронизированный звук
ffmpeg -i in.mkv -vf mpdecimate, setpts=N/FRAME_RATE/TB out.mkv
ffmpeg -i in.mkv -vf mpdecimate, setpts=N/25/TB out.mkv
как вы видите
- mpdecimate и decimate не работают одинаково
- mpdecimate работал лучше для меня без фильтра setpts
- в то время как decimate требуется фильтр setpts и, кроме того, мне нужно избегать
FRAME_RATE
переменная и использоватьN/25/TB
вместо этого, потому что фактический FPS не был обнаружен должным образом
обратите внимание на asetpts
- он выполняет ту же работу, что и setpts, но для аудио
- это действительно не исправило рассинхронизацию звука для меня, но вы хотите использовать это как-то так
-af asetpts=N/SAMPLE_RATE/TB
- может быть, вы должны настроить
SAMPLE_RATE
в соответствии с соотношением удаленных повторяющихся кадров, но мне кажется, что это лишняя ненужная работа, особенно когда мое видео синхронизировало звук в начале, поэтому лучше использовать команды, которые сохранят его таким, а не исправлять позже.
ТЛ; др
Если обычно рекомендуемая команда ffmpeg -i in.mkv -vf mpdecimate, setpts=N/FRAME_RATE/TB out.mkv
не работает у вас попробуйте это:
ffmpeg -i in.mkv -vf mpdecimate out.mkv
или же
ffmpeg -i in.mkv -vf decimate=cycle=6, setpts=N/25/TB out.mkv
( cycle=6
потому что каждый 6-й кадр дублируется и N/25/TB
потому что после удаления дубликатов видео будет иметь 25 кадров в секунду (избегайте FRAME_RATE
переменная); настроить для вашего случая использования)
Я попробовал решение здесь, и ни один из них, похоже, не работает, когда вам нужно обрезать видео и сохранить звук. Есть репозиторий mpdecimate_trim , который хорошо работает. Он в основном перечисляет все кадры, которые нужно отбросить (используя), а затем создает сложный фильтр для обрезки всех этих кадров (и соответствующего звука) из видео путем разделения видео и включения только части без повторяющихся кадров.
Однако мне пришлось настроить несколько параметров в коде. Например, в
mpdecimate_trim.py
, Мне пришлось изменить эту строку:
dframes2 = get_dframes(ffmpeg(True, "-vf", "mpdecimate=hi=576", "-loglevel", "debug", "-f", "null", "-").stderr)
Мне пришлось обнаруживать дубликаты более агрессивно, поэтому я изменил
mpdecimate
возможность
mpdecimate=hi=64*32:lo=64*24:frac=0.05
Если вы получаете дубликаты стоп-кадров после обрезки, возможно, вы неправильно вводите строку cmd. Я неправильно вводил порядок параметров, из-за чего первые несколько секунд моего видео зависали. Вот исправление, если это так. Надеюсь, это поможет вам полностью избежать стоп-кадров.
ffmpeg -ss {start_time} -i {input_path} -vcodec copy -acodec copy -to {end_time} {output_path}