Como gerar rapidamente GIFs animados de baixa qualidade com ffmpeg?

3

Geramos muitos GIFs em miniatura cuja qualidade não importa quase o tempo necessário para gerá-los. Gerar GIFs de alta qualidade com o ffmpeg é muito bem coberto, mas não estou tendo muita sorte em descobrir como gerar os de baixa qualidade o mais rápido possível.

O cálculo da paleta ocupa a maior parte do tempo de execução com o seguinte comando (extraído da resposta do filtro de várias cadeias aqui: Como criar com eficiência um gif de melhor paleta a partir de uma parte de vídeo diretamente da Web ):

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

O tempo de execução desse comando com 1000 quadros é de cerca de 72 segundos. Cerca de 67 segundos são a passagem da paleta e, em seguida, brilha na geração GIF real em cerca de 5 segundos. Eu gostaria de reduzir o tempo todo de execução o máximo possível e disposto a sacrificar muita qualidade de imagem por velocidade.

Rjak
fonte
1
Seu comando está sintaticamente incorreto; está faltando um [b]rótulo. De qualquer forma, o uso da paleta é cerca de 200⨉ mais lento a julgar por um teste rápido. Não usá-lo não é uma opção para você?
slhck 8/08/19
@slhck Estou achando os documentos do ffmpeg bastante difíceis de entender, mas criei isso para eliminar a paleta: ffmpeg -y -r 24 -f image2 -start_number 1 -i "frames.%04d.jpg" -filter:v "scale=150:-1:flags=fast_bilinear" output.gifOs dois problemas com isso: 1) Ele economizou apenas 13 segundos (59 segundos em vez de 72) e 2 ) Não tenho 100% de certeza de que este comando elimina completamente o cálculo da paleta (apenas que o cálculo da paleta que eu estava especificando antes não está incluído).
Rjak
1
Sim, é basicamente isso que eu quis dizer. Desculpe por não ser tão explícito, achei que você sabia o que estava fazendo o comando de filtro. Nos meus testes, deixar de fora a paleta realmente acelerou o comando significativamente . O que levou você à conclusão original de que a codificação real levou apenas 5 segundos?
Slhck 9/08/19
Observação do resultado, o que provavelmente não é um ótimo indicador. Quando executo o comando sem geração de paleta, o indicador "quadro atual" acende imediatamente e começamos a processar os quadros. Quando o executo com a geração de paleta, o quadro atual fica em 0 até cerca de 5 segundos antes do final do processamento, depois gira rapidamente pelos quadros.
Rjak
1
Sim, é também isso que estou observando, mas é muito mais rápido. Mas acho que depende da duração do vídeo. Para ser sincero, não sei se existe uma maneira mais rápida de gerar o GIF do que apenas fazer ffmpeg -i <input> <scale> <output.gif>.
Slhck 10/08/19

Respostas:

2

Seu uso dos palettegen/ paletteusefiltros está tornando o comando mais lento. A maneira simples de obter um GIF de qualidade inferior seria:

ffmpeg -f image2 -i "frames.%04d.jpg" output.gif

Com escala adicional:

ffmpeg -f image2 -i "frames.%04d.jpg" -vf scale=150:-1 output.gif

Você também pode soltar quadros no GIF de saída, ou seja, provar os quadros, para que nem todos sejam processados. Por exemplo, para ter apenas 1 saída de FPS, usando um fpsfiltro:

ffmpeg -i "frames.%04d.jpg" -vf "fps=fps=1,scale=150:-1" output.gif
slhck
fonte
Eu acho que há um erro de digitação nesse último exemplo ... deve ser `-vf" fps = 1, scale = 150: -1 ". O tempo de execução do segundo exemplo é 68,48s. A especificação de fps = 1 resulta em um GIF de 1 FPS, mas a origem é de 120 fps, portanto, o resultado é reproduzido muito lentamente e o tempo de execução não é aprimorado o suficiente (59,44s). Estou experimentando usar o carregador concat e ter o ffmpeg lido todos os enésimos quadros. Fazendo isso, se eu provar cada quinto quadro usando o redimensionamento de Lanczos e a geração de paletas por quadro, a qualidade temporal parece ótima, a qualidade da cor parece ótima e o comando é executado em menos de 8 segundos.
Rjak
Vou compartilhar a coisa toda em um comentário aqui, uma vez que eu testei completamente e tenha tempos de execução.
Rjak
@Rjak A primeira opção do fpsfiltro é nomeada fps, portanto são equivalentes. Se sua fonte é 120fps, você precisa especificar -framerate 120 -i "frames…", porque o padrão para a entrada é 24.
slhck
1

Fui encarregado de reduzir o tempo necessário para gerar um GIF animado o mais próximo possível de 30 quadros de comprimento a 150 pixels de largura. A maioria das seqüências que geramos tem menos de 1000 quadros. Tivemos uma sequência de 15.000 quadros e nossos nós de renderização estavam demorando 17 minutos para produzir esse GIF de 30 quadros, o que é inaceitavelmente lento.

Estávamos usando o ffmpeg como um desmuxador e canalizando para a imagemagick. Através de várias horas de experimentação, cheguei às seguintes conclusões:

  • O número de quadros de entrada que você solicita ao ffmpeg para processar é DE longe a entrada mais impactante em termos de velocidade de execução. Se usar o desmuxer concat para pular os quadros de entrada é uma opção, isso fará a maior diferença de desempenho. Ao tirar todos os quintos quadros, consegui reduzir o tempo total de computação para 1 minuto e 45 segundos com o escalonamento de lanczos de alta qualidade e o cálculo por paleta por quadro. A geração da miniatura de visualização de 30 quadros agora leva menos de 1 segundo .

  • O algoritmo de redimensionamento foi o próximo maior impactador de desempenho (mas um segundo distante). Usar fast_bilinear em vez de lanczos economizou 150 segundos de tempo de computação em todos os 15.000 quadros.

  • A variável menos impactante foi a computação em paleta, e isso variou com o algoritmo de redimensionamento. Mais de 15.000 quadros usando lanczos, economizamos cerca de 17 segundos de tempo de execução se eliminássemos o cálculo da paleta. Usando fast_bilinear, economizamos cerca de 75 segundos de tempo de execução.

Como o algoritmo de redimensionamento e a computação da paleta eram insignificantes, acabamos mantendo-os com a mais alta qualidade. Reduzimos nosso tempo de computação de 17 minutos para menos de 1 segundo, dizendo ao ffmpeg para pular a leitura dos arquivos de entrada.

RESUMO PRINCIPAL: SALTAR QUADROS DE ENTRADA vs SALTAR QUADROS DE SAÍDA

A razão pela qual nosso processo estava demorando tanto é que a queda de quadros não ajuda no tempo de execução ao usar o desmuxador image2. Se você mexer com a -rbandeira e o fpsfiltro, afetará o número de quadros que aparecem no GIF final, mas o ffmpeg parece ainda fazer algo com todos os 15.000 quadros de entrada.

A única maneira de encontrar o ffmpeg skip frames de entrada é usando o concatdesmuxer.

Aqui está como agora eu gero miniaturas GIF animadas de alta qualidade em minha máquina de desenvolvimento em menos de 1 segundo, ignorando os quadros de entrada:

# 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
Rjak
fonte