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 что совершенно неправильно

использовать или не использовать 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

    ffmpeg -i in.mkv -vf decimate=cycle=6 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}
Другие вопросы по тегам