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

80

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гарантирует, что вы получите разные изображения. Это по-прежнему всегда выбирает первый кадр видео, в большинстве случаев первый кадр имеет титры / логотип и не имеет смысла, поэтому я добавил -ss3, чтобы отбросить первые 3 секунды видео.

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

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

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

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

d33pika
источник
Хорошие примеры команд. FWIW, я не сталкивался с какими-либо проблемами с изображениями JPEG, созданными FFmpeg на OS X (10.8, FFmpeg 1.1 и ниже). Ваша вторая до последней команды отлично работает - как и последняя - и ни одна из этих команд не приводит к пустым файлам JPG. Я скомпилировал с libopenjpeg.. не уверен, если это имеет значение.
Slhck
Спасибо, slhck. Отредактировал вопрос с помощью конфигурации / версии ffmpeg. Я не обновился до 1.1 на этой машине. Я сделаю это и посмотрю, изменит ли это какие-либо результаты.
d33pika
1
Так ты на Ubuntu? Можете ли вы попробовать последнюю версию Git Master из статической сборки или скомпилировать и запустить снова? Или последняя стабильная. Я только что проверил, он также использует mjpegкодировщик для меня, и я также проверил, jpegoptimи exiv2оба они отлично работают для меня со всеми результатами JPG из ваших примеров команд.
Slhck
1
Я обновился, и теперь это работает! Я думаю, в предыдущей версии были некоторые ошибки.
d33pika
Можете ли вы опубликовать решение - новую версию, желательно со ссылкой на журнал изменений, показывающий ошибку, с которой вы столкнулись, и впоследствии исправленную в новой версии?
Лизз

Ответы:

29

Как насчет поиска, в идеале, первого> 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
AM
источник
1
Получаются полностью черные или белые изображения. Как их избежать?
d33pika
1
Существуют фильтры для обнаружения черных рамок и их последовательности ( ffmpeg.org/ffmpeg-filters.html#blackframe или ffmpeg.org/ffmpeg-filters.html#blackdetect ). Вы не сможете использовать ни одну из них в своей растущей однострочнике, но вы определенно должны быть в состоянии вырезать черные рамки (отдельный шаг) и извлечь миниатюры из полученного видео.
AM
5
Что касается белых рамок, то теперь все становится сложнее, но все равно выглядит, что есть способ: 1. вырезать черные рамки 2. отменить (белые превращаются в черные) 3. убрать белые рамки 4. отменить снова 5. извлечь миниатюры (Если вам удастся удалить черные или белые рамки, особенно в одну строку, вы можете разместить их здесь? Я могу добавить их в ответ для будущих читателей ... ... или на самом деле вы можете создать новый вопрос и ответ это. Я определенно приветствовал бы это.)
AM
3
Чтобы избежать скучных изображений, таких как черно-белое изображение: создайте больше миниатюр, чем вам нужно, сожмите их с помощью JPEG и выберите изображения с большим размером файла. Это работает на удивление хорошо само по себе, чтобы получить приличные эскизы.
Сэм Уоткинс
2
Эта команда не работает, я получил ошибку Unable to find a suitable output format for 'fps=fps=1/600'. Решение состоит в том, чтобы добавить -vfперед этим аргументом (см. Stackoverflow.com/questions/28519403/ffmpeg-command-issue )
Джон Уайзман
7

Трудно определить значимые значения, но если вы хотите, чтобы 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.

gertas
источник
2
Ничего себе, переходя -ss Nдо того -i, это удивительный совет. благодарю вас!
apinstein
2

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

Эран Бен-Натан
источник
Какие факторы вы использовали для сравнения 2 изображений?
d33pika
К сожалению, я не помню, это было довольно давно. Вам нужно сначала решить, какой метод сравнения использовать, а затем выполнить несколько тестов, чтобы найти правильный коэффициент. Это не должно занять много времени.
Эран Бен-Натан
Я просто думаю здесь вслух, но разве это не хуже? Когда вы сравниваете 2 экспортированных изображения из видео, вы уже потеряли некоторую информацию из него. Я имею в виду, что легче вычислить сходство по информации кодека, чем по 2 рисункам, не так ли? Недавно я изучил некоторые алгоритмы / библиотеки для сравнения изображений, и они не работают так, как того может потребоваться.
Самуил
Что ж, с помощью ffmpeg вы можете извлекать кадры без потери качества, используя флаг same_quality. Если вы используете информацию о кодеках, вам нужно убедиться, что вы не просто получаете Iframes. Во всяком случае, эта утилита perl прекрасно работала для меня и очень настраиваема. Смотрите здесь: search.cpan.org/~avif/Image-Compare-0.3/Compare.pm
Эран Бен-Натан,
2

Попробуй это

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

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

LostPuppy
источник
Не правильная формула бы fpsбыть (no_of_frames_req * fps_of_vid) / total_video_frames?
flolilo
Я ищу противоположное решение: выбрать больше кадров из периодов, когда камера более стабильна и не движется? (где разница между последовательными кадрами меньше, а не выше). Есть ли способ сделать это?
Тина Дж
1

Вот что я делаю для создания периодической миниатюры для живых потоков 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 минут.

chovy
источник