ImageMagick / GraphicsMagick: Como mesclar / compor várias imagens (3+) sem arquivos temporários em uma única etapa?

1

Estou tentando criar várias (duas ou mais) imagens pequenas com textos / fontes / tamanhos diferentes e, em seguida, sobrepô-las a uma única imagem de origem em diferentes posições em uma única etapa (sem criar arquivos nomeados temporários por mim). / p>

Isso é o que eu tentei até agora:

  • Crie um único texto e mescle-o em uma posição usando um pipe e um fluxo miff (funciona, mas eu preciso de várias imagens) :

    gm convert \
      -background transparent \
      -fill black \
      -font Calibri \
      -size 300x100 \
      -pointsize 36 \
      -gravity SouthEast \
      label:'large text' \
      miff:- \
    | gm composite -geometry +10+10 miff:- source.tif out.tif
    
  • Crie e mescle a primeira imagem, armazene o arquivo temporário de saída, carregue o arquivo temporário e mescle-o na origem (funciona, mas duas chamadas para gm necessárias, arquivo temporário devem ser gravadas e devem ser excluídas manualmente) :

    gm convert \
      -background transparent \
      -fill black \
      -font Calibri \
      -size 300x100 \
      -pointsize 36 \
      -gravity SouthEast \
      label:'large text' \
      miff:- \
    | gm composite -geometry +10+10 miff:- source.tif tmp.tif ; \
    gm convert \
      -background transparent \
      -fill grey \
      -font Calibri \
      -size 200x50 \
      -pointsize 12 \
      -gravity SouthEast \
      label:'small text' \
      miff:- \
    | gm composite -geometry +300+100 miff:- tmp.tif out.tif
    
  • Crie vários textos e mescle-os em uma posição, onde apenas a última posição é contada e aplicada a todos os arquivos (não funciona, somente o primeiro texto é sobreposto) :

    { gm convert \
      -background transparent \
      -fill black \
      -font Calibri \
      -size 300x100 \
      -pointsize 36 \
      -gravity SouthEast \
      label:'large text' \
      miff:- ; \
      gm convert \
      -background transparent \
      -fill grey \
      -font Calibri \
      -size 200x50 \
      -pointsize 12 \
      -gravity SouthEast \
      label:'small text' \
      miff:- ; } \
    | gm composite miff:- -geometry +10+10 miff:- -geometry +300+100 source.tif -geometry +0+0 out.tif
    
  • Use a sintaxe ( ... ) , substituindo "text1.tif" e "text2.tif" pelos comandos gm convert das chaves (não funcionou em nenhum caso, talvez eu esteja usando errado? )

  • Use flatten em vez de composite (funcionou, mas todos os arquivos colocados na mesma posição em vez de em diferentes, também destruiriam imagens de várias páginas)
  • Use append com vários arquivos por meio de um único canal (preciso deles sobrepostos, não lado a lado)

É possível combinar três ou mais imagens umas sobre as outras, de modo que cada imagem tenha uma posição diferente na imagem de saída, sem recorrer a salvar e carregar arquivos temporários? Como alternativa, como posso definir a ordem z das imagens sobrepostas antes de escrevê-las? Se possível, em gm 1.3.25 e bash 4.3 , mas estou aberto a alternativas que funcionem.

    
por user121391 05.12.2016 / 13:54

2 respostas

3

A partir da solicitação inicial, eu entendo que você precisa mesclar 3 imagens (1 do arquivo e 2 geradas) em uma foto, considerando que você só pode trabalhar com 2 na ocasião.

Eu também entendo que você não quer dados temporários armazenados em disco. A solução mais rápida seria usar "tmpfs" como sugerido na resposta anterior por @ nominal-animal.

Mas dando um passo adiante, é possível evitar a criação de arquivos temporários e, basicamente, fazer todo o processamento em um único disparo. A solução que encontrei é usar "pipes nomeados", são arquivos especiais que não armazenam dados no disco e colocam até mesmo qualquer transferência nos canais até que a parte receptora também esteja pronta. Eu não usarei comandos completos como você forneceu (apenas o comando, entradas e saída), sinta-se livre para preencher os argumentos que você precisa.

# First it's necessary to have the pipes created, one time operation and they can be reused over and over.
mknod /tmp/pipe1 p
mknod /tmp/pipe2 p
mknod /tmp/pipe3 p

# generate the 2 images and send each to a pipe 
gm convert ... miff:- > pipe1 &
gm convert ... miff:- > pipe2 &
# merge the source image to the 1st generated image
gm composite ... source.tif pipe1 miff:- > pipe3 &
# finally merge the result to the 2nd generated image
gm composite ... pipe3 pipe2 out.tif

Parece que a operação é feita em 4 etapas, mas, na verdade, tudo será feito de uma só vez sem que nada seja gravado no disco.

Veja como funciona e por que os comandos estão escritos como estão:

  • o comando para gerar a 1ª imagem começará a trabalhar na solicitação, mas a saída deve ir para o canal nomeado. Porque neste momento nada está lendo do tubo a saída vai parar esperando por um recebido. O processo não vai sair ou exibir nenhum erro, então, para continuar, ele é colocado em segundo plano.
  • segunda imagem mesma coisa apenas pipe diferente.
  • a fusão da imagem de origem e os dados do primeiro canal farão com que ele leia de pipe1 e libere o primeiro comando de geração, mas como sua saída vai para outro canal, ele terá que esperar pela etapa final.
  • A fusão dos dados do pipe3 e pipe2 finalizará a operação e gerará o arquivo de saída desejado e liberará os dois comandos anteriores.

Você pode considerar os canais como os arquivos temporários que não são arquivos, lembre-se de colocar algumas coisas em segundo plano, caso contrário, o script aguardará sua CTRL + C. Cada comando deve ser colocado em segundo plano separadamente! Não há nenhum problema se você colocá-los em um script como este, uma vez que a saída é gerada para os comandos anteriores.

Um recurso interessante é que você pode iniciar qualquer comando 3 primeiro em segundo plano e o último normalmente, já que todos os 4 dependem de dados de pipes. Também é fácil estender a vários comandos apenas adicionando mais pipes.

Há uma limitação que vejo, você não pode usar os mesmos canais em paralelo, se você precisar usar diferentes conjuntos de canais para cada instância em execução em paralelo, caso contrário, haverá surpresas :) O processamento sequencial não tem esse problema.

Do ponto de vista do desempenho, não sei se isso é mais rápido do que criar arquivos temporários em um "tmpfs", portanto, você precisará descobrir testando-o no cenário real. Para que isso funcione, você deve ter memória suficiente para acomodar todos os 4 processos com seus dados na memória. (depende muito do tamanho das imagens que você processa / gera).

    
por 20.12.2016 / 00:49
1

Esta não é uma resposta à questão declarada, mas apenas um aparte mostrando que a premissa da pergunta original, que "arquivo temporário ... deve ser removido manualmente" , está completamente incorreta.

(Eu postei esta "resposta" no caso provável de perceber que essa premissa é falsa, permite que o OP e outros se sintam livres para usar arquivos temporários. Em muitos casos, o uso de arquivos temporários torna os scripts mais robustos e fáceis de manter .)

Se um script Bash precisar usar um ou mais arquivos temporários, sempre uso a seguinte abordagem:

#!/bin/bash
work="$(mktemp -d)" || exit 1
trap "cd / ; rm -rf '$work'" EXIT

Isso usa o mktemp helper para criar um diretório de trabalho temporário (em /tmp/ ). Se mktemp não estiver disponível ou o diretório não puder ser criado, o script será cancelado.

Para remover automaticamente o diretório de trabalho e quaisquer arquivos que ele possa conter, definimos um EXIT trap. Essa armadilha é acionada - isto é, o bash executa o comando especificado - sempre que o shell sai; seja uma saída normal ou devido a um erro.

O comando EXIT trap altera para o diretório raiz (apenas para garantir que o diretório de trabalho atual não esteja dentro do diretório temporário) e, em seguida, remove a subárvore inteira. Observe que, como o comando está entre aspas duplas, $work é avaliado quando o trap é definido; isso significa que, mesmo se você alterasse o valor da variável work posteriormente no script, a interceptação ainda removeria o diretório temporário original.

Embora a interceptação altere o diretório de trabalho atual, ele não terá nenhum efeito fora da interceptação - seja no script em outro lugar ou em outros processos -, porque o diretório de trabalho atual é específico para cada processo, e o atual O processo bash sairá logo após o comando trap ser executado.

Portanto, todos os arquivos temporários criados com $work , por exemplo, "$work/1" , "$work/result" e assim por diante, serão automaticamente removidos, assim como o diretório temporário $work , quando o script for concluído.

    
por 14.12.2016 / 11:29