![Как быстро создать анимированные GIF-файлы низкого качества с помощью ffmpeg?](https://rvso.com/image/1567475/%D0%9A%D0%B0%D0%BA%20%D0%B1%D1%8B%D1%81%D1%82%D1%80%D0%BE%20%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C%20%D0%B0%D0%BD%D0%B8%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5%20GIF-%D1%84%D0%B0%D0%B9%D0%BB%D1%8B%20%D0%BD%D0%B8%D0%B7%D0%BA%D0%BE%D0%B3%D0%BE%20%D0%BA%D0%B0%D1%87%D0%B5%D1%81%D1%82%D0%B2%D0%B0%20%D1%81%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E%20ffmpeg%3F.png)
Мы генерируем множество миниатюр GIF, качество которых не так важно, как время, необходимое для их генерации. Генерация высококачественных GIF с помощью ffmpeg очень хорошо освещена, но мне не очень везет в том, чтобы выяснить, как генерировать низкокачественные GIF как можно быстрее.
Вычисление палитры занимает большую часть времени выполнения следующей команды (взято из ответа multi-chain filtergraph здесь):Как эффективно создать gif-анимацию с лучшей палитрой из фрагмента видео прямо из Интернета):
ffmpeg -y -threads 8 -r 24 -f image2 -start_number 1 -i "frames.%04d.jpg" -filter_complex "fps=24,scale=150:-1:flags=fast_bilinear,split=2 [a][b]; [a] palettegen [pal] fifo [b]; [b] [pal] paletteuse" output.gif
Время выполнения этой команды с 1000 кадрами составляет около 72 секунд. Около 67 секунд из них — проход палитры, а затем он проносится через фактическую генерацию GIF примерно за 5 секунд. Я хотел бы максимально сократить общее время выполнения и готов пожертвовать большим количеством качества изображения ради скорости.
решение1
Использование вами фильтров palettegen
/ paletteuse
замедляет выполнение команды. Простой способ получить GIF-файл более низкого качества:
ffmpeg -f image2 -i "frames.%04d.jpg" output.gif
С дополнительным масштабированием:
ffmpeg -f image2 -i "frames.%04d.jpg" -vf scale=150:-1 output.gif
Вы также можете пропустить кадры в выходном GIF, т.е. сделать выборку кадров, чтобы не все они обрабатывались. Например, чтобы иметь только 1 FPS на выходе, используя фильтр fps
:
ffmpeg -i "frames.%04d.jpg" -vf "fps=fps=1,scale=150:-1" output.gif
решение2
Мне было поручено сократить время, необходимое для создания анимированного GIF-анимации, максимально приблизившись к 30 кадрам в длину при ширине 150 пикселей. Большинство последовательностей, которые мы генерируем, имеют длину менее 1000 кадров. У нас была последовательность из 15 000 кадров, и наши узлы рендеринга занимали17 минутдля создания этого GIF-файла из ~30 кадров, что неприемлемо медленно.
Мы использовали ffmpeg в качестве демультиплексора и передавали поток в imagemagick. После нескольких часов экспериментов я пришел к следующим выводам:
Количество входных кадров, которые вы просите обработать ffmpeg, равноДАЛЕКОнаиболее эффективный ввод с точки зрения скорости выполнения. Если использование concat demuxer для пропуска кадров ввода является опцией, это даст наибольшую разницу в производительности. Взяв каждый 5-й кадр, я смог сократить общее время вычислений до1 минута 45 секундс высококачественным масштабированием Ланцоша и покадровым вычислением палитры.Создание миниатюры предварительного просмотра из 30 кадров теперь занимает менее 1 секунды..
Алгоритм масштабирования был следующим по величине фактором, влияющим на производительность (но далеко вторым). Использование fast_bilinear вместо lanczos сэкономило 150 секунд вычислительного времени для всех 15 000 кадров.
Наименее влиятельной переменной было вычисление палитры, и оно менялось в зависимости от алгоритма изменения масштаба. Более 15 000 кадров с использованием lanczos, мы сэкономили около 17 секунд времени выполнения, если исключили вычисление палитры. Используя fast_bilinear, мы сэкономили около 75 секунд времени выполнения.
Поскольку алгоритм масштабирования и вычисление палитры были незначительными, мы в итоге сохранили их на самом высоком уровне качества. Мы сократили время вычислений с 17 минут до менее 1 секунды, в основном за счет того, что сказали ffmpeg пропустить чтение входных файлов.
КЛЮЧЕВОЙ ВЫВОД: ПРОПУСК ВХОДНЫХ КАДРОВ против ПРОПУСКА ВЫХОДНЫХ КАДРОВ
Причина, по которой наш процесс занял так много времени, заключается в том, что пропуск кадров не помогает сократить время выполнения при использовании демультиплексора image2. Если вы накосячите с флагом -r
и fps
фильтром, вы повлияете на количество кадров, которые появятся в конечном GIF, но ffmpeg, похоже, все равно что-то делает со всеми 15 000 входных кадров.
Единственный способ, который я смог найти, чтобы ffmpeg пропускал входные кадры, — это использование демультиплексора concat
.
Вот как я теперь создаю высококачественные анимированные миниатюры GIF на своей машине разработчика менее чем за 1 секунду, пропуская входные кадры:
# create text file which describes the ~30 input frames we want ffmpeg to process
seq -f "file 'left_frames.%04g.jpg'" 10000 500 25000 > tmp.txt
# generate the animated gif using ffmpeg only
ffmpeg -f concat -i tmp.txt -filter_complex "scale=150:-1:flags=lanczos,split=2 [a][b]; [a] palettegen [pal]; [b] fifo [b]; [b] [pal] paletteuse" output.gif