Fazer um panorama de imagens para carrossel de Instagram

Aqui um comando que parece meio mágico:

magick montage -background "#000000" -geometry +1 -resize "50%" -tile $(ls Feed-*.png | wc -l)x1 Feed-*.png panorama.png

Mas o que cada parte dele faz?

  • magick - Invoca o programa.
  • montage - Inicia o módulo do programa encarregado de fazer montagens.
  • -background '#000000' - Define o fundo como preto.
  • -geometry +1 - Determina um offset de 1 pixel. Mais no link da documentação.
  • -resize "50%" - Já que estamos juntando imagens de 1080 pixels de largura em um carrossel, os arquivos podem rapidamente ficar pesados demais para aprovação interna. Desta forma diminuímos as dimensões do arquivo pela metade para ficar mais leve.
  • -tile $(ls Feed-*.png | wc -l)x1 - Usamos uma combinação do parâmetro tile e um subshell para definir o arranjo das imagens. Do jeito que está escrito fará uma única fileira horizontal de imagens com o formato Feed-*.png como nome, formando um panorama. Por exemplo, Feed-1.png, Feed-2.png, Feed-3.png, e assim por diante. Caso o parâmetro tile seja omitido, imagemagick vai decidir sozinho as proporções do arranjo. Uma sequência de 6 imagens poderia ser arranjada, por exemplo, em um grid de 3x2. Mais informações aqui e na documentação oficial. Uma explicação mais detalhada pode ser encontrada abaixo.
  • Feed-*.png - Usamos o asterisco para selecionar todas imagens neste formato de nome.
  • panorama.png - Salvamos o arquivo como panorama.png

Sobre o comando de tile:

O parâmetro tile é responsável por definir o layout da nossa montagem de fotos. Ele aceita valores no formato larguraxaltura. Um conjunto de seis imagens, por exemplo, poderia ser arranjado em 2x3 ou 3x2. No nosso caso, um feed de seis imagens precisaria ser explicitado como 6x1. Carrosséis, porém, tem larguras que podem variar. A necessidade, portanto, é a de parametrizar o comando para que ele mesmo saiba quantas imagens devem ser processadas. Neste caso empregamos algo chamado subshell, que é basicamente uma maneira de rodar um comando dentro de outro. Foi usado porque queremos explicitar para o programa que queremos um panorama com exatamente o número de imagens que obedeçam a determinado requisito, neste caso ter o nome Feed-x.png, sendo x qualquer número. Assim temos:

$(ls Feed-*.png | wc -l)x1

Aonde $ abre o subshell, que virá entre parênteses. Pedimos para listar com ls todos os arquivos no formato Feed-*.png. Encaminhamos esse output com o operador pipe | para o comando wc. Normalmente wc faz a contagem de palavras (wordcount), porém aqui especificamos com -l que queremos uma contagem de linhas. Ou seja: liste todos os arquivos com nome especificado, pegue esse output e conte quantas linhas são printadas. Dessa maneira conseguimos o número de imagens para o carrossel.

Um método alternativo para panoramas

Neste usamos o método convert para selecionar todos os arquivos no formato Feed-x.png, com o parâmetro +append para colar todas as imagens lado a lado. Finalmente damos o -resize para diminuir o tamanho do arquivo, e determinamos o nome do arquivo de saída como output.png.

convert Feed-*.png +append -resize "50%" output.png

Este método é mais legível e consideravelmente mais simples, porém não cria as linhas de divisórias entre as imagens. Deve-se avaliar qual o melhor método para diferentes usos.

Modificando o método acima para criar um mosaico de imagens

Utilizando uma estrutura de comando parecida com a de fazer panoramas, podemos utilizar o comando abaixo para criar um mosaico de imagens. Essa função pode ser muito útil para criar um painel de referências, por exemplo.

magick montage -background '#ffffff' -geometry 128x128+1+1 *.jpg *.png *.jpeg mosaico.png

Já destrinchamos o que cada parte dele faz lá em cima lá em cima, portanto, veremos somente os comandos que tiveram mudanças.

  • -geometry 128x128+1+1 - O primeiro parâmetro 128x128 define o tamanho largura x altura do -tile do nosso mosaico. Nesse exemplo, definimos um tamanho de tile pequeno para não gerar uma imagem final muito grande. É possível, utilizando por exemplo 128x especificar somente a largura ou x128 para somente a altura. A segunda parte +1+1 define o espaçamento em pixels entre os tiles.
  • *.jpg *.png *.jpeg - Nessa seção, delimitamos as extensões das imagens que o imagemagick buscará para fazer o mosaico. No nosso exemplo utilizamos três, mas você pode utilizar quantas quiser.

Note que, diferentemente do exemplo do carrossel, aqui não especificamos um tile. Isso se dá porque o imagemagick é bem espertinho para gerar mosaicos. Mais informações neste link.

Converter múltiplos arquivos de uma pasta

Para converter múltiplos arquivos de uma pasta é possível usar o comando mogrify. O comando a seguir, por exemplo, converte para .png todos os arquivos .HEIC e, em seguida, todos os arquivos .heic.

Importante

O parâmetro -format é necessário para que os arquivos não sejam substituídos após conversão:

mogrify -format png *.HEIC *.heic

Remover o fundo branco de imagens

magick input.jpg -fuzz 20% -transparent white output.png

Onde o parâmetro -fuzz controla a precisão da seleção do branco. Quanto maior o valor mais “agressivo” será a remoção do branco e sua substituição pelo Alpha.

Trabalhando com GIFs

O comando básico para criar gifs a partir de imagens estáticas usando ImageMagick é o seguinte:

magick -delay 3 -loop 0 *.jpg output.gif

Onde:

  • -delay determina o tempo entre uma imagem e outra. É medido e centésimos de segundos. Um vídeo de 30fps teria um valor de delay de aproximadamente 3.
  • -loop determina quantas vezes o loop de imagens deve acontecer. Ao passar o número zero o loop acontece para sempre.
  • *.jpg wildcard pega todos os jpgs presentes na pasd

Ainda é possível unir diferentes gifs em um só:

magick image1.gif image2.gif output.gif

Um lembrete sobre otimização

No passado precisei juntar vídeos e imagens em um arquivo de gif só. O problema é que vídeos tem framerates altos, e uma sequência de imagens a 30fps, por exemplo, rapidamente geram um gif com tamanho imenso, normalmente na casa das dezenas de megabytes!

Porém, existem duas maneiras possíveis contornar o problema. Na primeira criamos diferentes gifs com diferentes framerates e juntamos todos com o comando citado acima. É um processo que funciona, mas fica rapidamente tedioso e que dificulta a alteração deles no futuro.

Na segunda lançamos mão de um fato interessante sobre a especificação do arquivo gif. Ao contrário de vídeos, que possuem framerates globais, no gif cada frame contém a informação de por quanto tempo deve ser visto. Assim, misturando imagens estáticas e vídeos, é possível usar uma função de otimização embutida no ImageMagick que remove frames idênticos.

magick input.gif -coalesce -layers RemoveDups output.gif

Isso permitiria um processo mais flexível, com inclusive a possibilidade de usar editores de vídeo. O processo então seria:

  1. Editar o vídeo da maneira que preferir.https://www.youtube.com/watch?v=4JgTHz41KkQ
  2. Exportar o vídeo como sequência de frames. Nota: Tenho usado o formato jpg com qualidade média. O formato gif só suporta 256 cores, portanto a imagem será altamente comprimida e não faz sentido usar um pngs, por exemplo.
  3. Fundir os frames em um único gif usando ImageMagick. Nota: este processo pode demorar algum tempinho e usar bastante RAM.
  4. Passar o gif pela otimização acima para remover frames duplicados.

Com esse processo já consegui uma redução de até 20x no tamanho do gif final.