Значимые миниатюры для видео с использованием 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}/
но папку можно сбросить конечно.