Значимые миниатюры для видео с использованием FFmpeg

FFmpeg может захватывать изображения из видео, которые можно использовать в качестве эскизов для представления видео. Наиболее распространенные способы сделать это описаны в FFmpeg Wiki.

Но я не хочу выбирать случайные кадры через определенные промежутки времени. Я нашел несколько вариантов использования фильтров в FFmpeg для захвата изменений сцены:

Фильтр thumbnail пытается найти наиболее представительные кадры в видео:

ffmpeg -i input.mp4 -vf  "thumbnail,scale=640:360" -frames:v 1 thumb.png

и следующая команда выбирает только кадры, которые имеют более 40% изменений по сравнению с предыдущими (и, вероятно, являются изменениями сцены), и генерирует последовательность из 5 PNG.

ffmpeg -i input.mp4 -vf  "select=gt(scene\,0.4),scale=640:360" -frames:v 5 thumb%03d.png

Информационный кредит для вышеуказанных команд Фабио Соннати. Второй показался мне лучше, так как я мог получить n изображений и выбрать лучшее. Я попробовал это, и это произвело то же самое изображение 5 раз.

Еще одно расследование привело меня к:

ffmpeg -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr  out%02d.png

-vsync vfr гарантирует, что вы получите разные изображения. Это по-прежнему всегда выбирает первый кадр видео, в большинстве случаев первый кадр является титрами / логотипом и не имеет смысла, поэтому я добавил -ss 3, чтобы отменить первые 3 секунды видео.

Моя последняя команда выглядит так:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr out%02d.jpg

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

Я хотел бы выбрать ваши мозги для любых других лучших вариантов.

8 ответов

Решение

Как насчет поиска, в идеале, первого>40% сменного кадра в каждом из 5 временных интервалов, где временные интервалы составляют 1, 2, 3, 4 и 5–20% видео.

Вы также можете разделить его на 6 временных периодов и игнорировать 1-й, чтобы избежать кредитов.

На практике это будет означать установку низкого значения fps при применении проверки смены сцены и вашего аргумента для удаления первого бита видео.

...что-то вроде:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.4)" -frames:v 5 -vsync vfr -vf fps=fps=1/600 out%02d.jpg

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

Псевдо-код

for X in 1..N
  T = integer( (X - 0.5) * D / N )  
  run `ffmpeg -ss <T> -i <movie>
              -vf select="eq(pict_type\,I)" -vframes 1 image<X>.jpg`

Куда:

  • D - продолжительность видео, прочитанная с ffmpeg -i <movie> один или ffprobe который имеет хороший выходной JSON писатель кстати
  • N - общее количество миниатюр, которые вы хотите
  • X - номер миниатюры от 1 до N
  • T - момент времени для миниатюры

Просто вышеупомянутое записывает центральный ключевой кадр каждого раздела фильма. Например, если фильм имеет длину 300 с и вам нужно 3 миниатюры, тогда он занимает один ключевой кадр после 50 с, 150 с и 250 с. Для 5 миниатюр это будет 30, 90, 150, 210, 270. Вы можете настроить N в зависимости от продолжительности видеоролика D, например, в 5-минутном фильме будет 3 миниатюры, а в течение 1 часа - 20 миниатюр.

Спектакль

Каждый вызов выше ffmpeg Команда занимает долю секунды (!) для ~1 ГБ H.264. Это потому, что он мгновенно переходит к <time> положение (ум -ss до -i) и берет первый ключевой кадр, который является практически полным JPEG. Нет времени, потраченного на рендеринг фильма в соответствии с точным положением времени.

Постобработка

Вы можете смешать выше с scale или любой другой метод изменения размера. Вы также можете удалить сплошные цветные рамки или попробовать смешать их с другими фильтрами, такими как thumbnail,

Попробуй это

 ffmpeg -i input.mp4 -vf fps= no_of_thumbs_req/total_video_time out%d.png

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

Это действительно трудная задача. Самое первое, что нужно сделать, это определить «значимый» в вашем контексте: вы ищете наиболее единообразный образ? изображение, которое описывает ваше видео, но не портит его?

В большинстве случаев лучшая миниатюра — это та, которая содержит:

  • Отличная яркость.
  • Не имеет высокой однородности интенсивности пикселей.
  • Хороший контраст.
  • Отличная резкость.

Использование ffmpeg означает удаление черных рамок и последующее получение миниатюр (у ffmpeg есть хороший фильтр для получения миниатюр). Сначала получим список черных рамок:

      ffprobe -f lavfi -i "movie=./input.mp4,blackdetect[out0]" -show_entries tags=lavfi.black_start,lavfi.black_end -of default=nw=1 -v quiet

затем удалите их из видео и получите миниатюру:

      ffmpeg -i ./input.mp4 -filter_complex "[0:v]trim=start=3.23657:end=32.9329,setpts=PTS-STARTPTS[v1];[0:a]atrim=start=3.23657:end=32.9329,asetpts=PTS-STARTPTS[a1]" -map [v1] -map [a1] output.mp4

и

      ffmpeg -i output.mp4 -vf "thumbnail" -frames:v 1 thumb2.png

Вы заметите, что результат не самый лучший, который вы можете получить: этот метод выбирает только кадр, имеющий некоторый «контент», и не учитывает факторы, которые я описал ранее.

Мы написали сообщение в блоге, описывающее ту же самую проблему , которая может оказаться полезной.

Отказ от ответственности: я являюсь одним из соучредителей https://mediamachine.io .

Однажды я сделал нечто подобное, но я экспортировал все кадры видео (с частотой 1 кадр / с) и сравнил их с обнаруженной мной утилитой perl, которая вычисляет разницу между изображениями. Я сравнил каждый кадр с предыдущими миниатюрами, и если он отличался от всех миниатюр, я добавил его в коллекцию миниатюр. Преимущество здесь в том, что если ваше видео перемещается со сцены A на B, а оно возвращается на A, ffmpeg будет экспортировать 2 кадра A.

Вот что я делаю, чтобы генерировать периодические миниатюры для живых потоков m3u8 для использования в качестве постера. Я обнаружил, что выполнение непрерывной задачи ffmpeg только для генерации миниатюр приводит к израсходованию всего моего процессора, поэтому вместо этого я запускаю cronjob каждые 60 секунд, который генерирует все миниатюры для моих потоков.

#!/usr/bin/env bash

## this is slow but gives better thumbnails (takes about 1+ minutes for 20 files)
#-vf thumbnail,scale=640:360 -frames:v 1 poster.png

## this is faster but thumbnails are random (takes about 8 seconds for 20 files)
#-vf scale=640:360 -frames:v 1 poster.png
cd ~/path/to/streams

find . -type f \
  -name "*.m3u8" \
  -execdir sh -c 'ffmpeg -y -i "$0" -vf scale=640:360 -frames:v 1 "poster.png"' \
  {} \;

Если вам нужно делать чаще, чем 60 секунд (ограничение cronjob), то вы можете сделать while цикл в вашем сценарии, и он будет выполняться вечно. Просто добавьте sleep 30 изменить частоту до 30 секунд. Я не рекомендую делать это с большим количеством видео, поскольку предыдущий запуск может не завершиться до начала следующего.

В идеале с cronjob я просто запускаю его каждые 5 минут.

Я пока не могу добавлять комментарии, поэтому, что касается решения @jaygooby, вызывающего синтаксическую ошибку. Значимые миниатюры для видео с использованием FFmpeg , проблема в том, что $N (количество снимков экрана) не было объявлено. Исправление состоит в том, чтобы добавить его в начало команды, например:

N=30; D=180; for X in $(seq 1 $N); do echo $X; T=$(bc <<< "($X-0.5)*$D/$N"); ffmpeg -y -hide_banner -loglevel panic -ss $T -i in.mp4 -vf select="eq(pict_type\,I)" -vframes 1 $X.jpg; done

Более того, решение @jaygooby работает хорошо и очень быстро (из-за того, что в команде ffmpeg перед -i ставится -ss). Я объединил оба его предложения (тот, что с медиаинформацией) и мое исправление:

N=60; INPUT="path/to/filename"; D=$(bc <<< $(mediainfo --Inform="Video;%Duration%" "$INPUT")/1000) ; echo "Duration: $D seconds"; for X in $(seq 1 $N); do echo $X; T=$(bc <<< "($X-0.5)*$D/$N"); ffmpeg -y -hide_banner -loglevel panic -ss $T -i $INPUT -vf select="eq(pict_type\,I)" -vframes 1 $X.jpg; done

где:

      N = number of desired screenshots
INPUT = path to the filename

Измените эти две переменные, и все готово.

https://github.com/romanwarlock/thumbnails/blob/master/thumbgen.sh

Использование:

      . thumbgen.sh COLUMNS ROWS SIZE INPUT

COLUMNS означает количество столбцов; ROWS означает количество строк; РАЗМЕР — это длина более длинной стороны вывода, например, 1920, если вы хотите получить выходное изображение 1920x1080; INPUT — путь к входному файлу;

Пример:

      . thumbgen.sh 3 4 1920 video.mp4



. <path-to-file>/thumbgen.sh 3 4 1920 video.mp4

пример вывода: https://github.com/romanwarlock/thumbnails/blob/master/th7138.jpg

в скрипте все скриншоты хранятся вTMPDIR=/tmp/thumbnails-${RANDOM}/но папку можно сбросить конечно.

Другие вопросы по тегам