⚠️ Este é um site de tradução não oficial, sem relação com a ImageMagick Studio LLC. Para informações oficiais, consulte a página original (https://imagemagick.org/fx/).

A Anatomia de uma Expressão FX

O Operador de Imagem de Efeitos Especiais FX • A Anatomia de uma Expressão FX

O operador de imagem de efeitos especiais FX aplica uma expressão matemática a cada canal de pixel de uma imagem. A linguagem de expressões FX oferece uma maneira poderosa e flexível de manipular imagens, permitindo realizar uma ampla variedade de operações e transformações em suas imagens. Use o FX para:

  • criar telas, gradientes e mapas de cores matemáticos
  • mover valores de cor entre imagens e canais
  • transladar, inverter, espelhar, rotacionar, escalar, cisalhar e, de modo geral, distorcer imagens
  • mesclar ou compor várias imagens
  • convolver ou mesclar pixels vizinhos
  • gerar métricas ou "impressões digitais" de imagens

O operador percorre todos os pixels de uma imagem e todos os canais de cada pixel e retorna uma nova imagem com os resultados. A expressão pode referenciar qualquer imagem na sequência de imagens, mas somente uma cópia da primeira imagem é retornada com as atualizações apropriadas conforme sua expressão.

A expressão pode ser simples:

magick -size 64x64 canvas:black -channel blue -fx "1/2" fx_navy.png

Aqui, convertemos uma imagem preta em uma imagem azul-marinho:

black ==> navy

Ou a expressão pode ser complexa:

magick rose: \
  -fx "(1.0/(1.0+exp(10.0*(0.5-u)))-0.006693)*1.0092503" \
  rose-sigmoidal.png

Esta expressão resulta em uma versão de alto contraste da imagem de origem:

rose ==> rose-sigmoidal

A expressão pode incluir atribuições de variáveis. As atribuições, na maioria dos casos, reduzem a complexidade de uma expressão e permitem algumas operações que poderiam não ser possíveis de outra forma. Por exemplo, vamos criar um gradiente radial:

magick -size 70x70 canvas: \
  -fx "Xi=i-w/2; Yj=j-h/2; 1.2*(0.5-hypot(Xi,Yj)/70.0)+0.5" \
  radial-gradient.png

O comando acima retorna esta imagem:

radial-gradient

Esta expressão FX adiciona ruído aleatório a uma imagem:

magick photo.jpg -fx 'iso=32; rone=rand(); rtwo=rand(); \
  myn=sqrt(-2*ln(rone))*cos(2*Pi*rtwo); myntwo=sqrt(-2*ln(rtwo))* \
  cos(2*Pi*rone); pnoise=sqrt(p)*myn*sqrt(iso)* \
  channel(4.28,3.86,6.68)/255; max(0,p+pnoise)' noisy.png

Este script FX utiliza um laço para criar um conjunto de Julia:

magick -size 400x400 xc:gray -fx " \
  Xi=2.4*i/w-1.2; \
  Yj=2.4*j/h-1.2; \
  for (pixel=0.0, (hypot(Xi,Yj) < 2.0) && (pixel < 1.0), \
    delta=Xi^2-Yj^2; \
    Yj=2.0*Xi*Yj+0.2; \
    Xi=delta+0.4; \
    pixel+=0.00390625 \
  ); \
  pixel == 1.0 ? 0.0 : pixel" \
  \( -size 1x1 xc:white xc:red xc:orange xc:yellow xc:green1 xc:cyan xc:blue \
     xc:blueviolet xc:white -reverse +append -filter Cubic -resize 1024x1! \) \
  -clut -rotate -90 julia-set.png

Julia Fractals

Este script FX imprime os 10 primeiros números primos:

magick xc:gray -fx " \
  for (prime=2, prime < 30, composite=0; \
    for (nn=2, nn < (prime/2+1), if ((prime % nn) == 0, composite++, ); nn++); \
      if (composite <= 0, debug(prime), ); prime++)" null:

Para mais exemplos, consulte Using FX, The Special Effects Image Operator.

A opção -fx substitui qualquer sequência de imagens por um clone da primeira imagem atualizado com os resultados da expressão. Se preferir aplicar a expressão a cada imagem da sequência, use +fx em vez disso.

A expressão FX é interpretada em uma única thread; no entanto, é executada em múltiplas threads, a menos que a expressão inclua a função debug().

A próxima seção discute a linguagem de expressões FX.

A Anatomia de uma Expressão FX

A Linguagem de Expressões FX

A linguagem formal de expressões FX é definida aqui:

numbers:
inteiro, ponto flutuante, notação científica (+/- obrigatório, e.g. 3.81469e-06), sufixos numéricos do Sistema Internacional (e.g. KB, Mib, GB, etc.)
constants:
E (número de Euler), Epsilon, Opaque, Phi (razão áurea), Pi, QuantumRange, QuantumScale, Transparent
FX operators (em ordem de precedência):
^ (potência), - unário, , /, % (módulo), +, -, <<, >>, <, <=, >, >=, +=, -=, =, /=, %=, <<= , >>=, &=, |=, ++, --, ==, !=, & (AND bit a bit), | (OR bit a bit), && (AND lógico), || (OR lógico), ~ (NOT lógico), ?: (condicional ternário)
array:
uma imagem oferece armazenamento em array (e.g. p[-1,-1].r) limitado por sua largura e altura. Uma sequência de imagens representa múltiplos arrays (e.g. u.p[0,0].r, v.p[0,0].r). O armazenamento é limitado a valores Quantum, e.g. [0..65535] para builds Q16 e ponto flutuante para builds com HDRI habilitado.
math functions:
abs(), acos(), acosh(), airy(), alt(), asin(), asinh(), atan(), atanh(), atan2(), ceil(), clamp(), cos(), cosh(), debug(), drc(), epoch(), erf(), exp(), floor(), gauss(), gcd(), hypot(), int(), isnan(), j0(), j1(), jinc(), ln(), log(), logtwo(), magicktime(), max(), min(), mod(), not(), pow(), rand(), round(), sign(), sin(), sinc(), sinh(), sqrt(), squish(), tan(), tanh(), trunc()
channel functions:
define até 5 canais de pixel
color names:
red, cyan, black, etc.
color functions:
srgb(), srgba(), rgb(), rgba(), cmyk(), cmyka(), hsl(), hsla(), etc.
color hex values:

ccc, #cbfed0, #b9e1cc00, etc.

symbols:

u: primeira imagem da lista
v: segunda imagem da lista
s: imagem atual da lista (para %[fx:] caso contrário = u)
t: índice da imagem atual (s) na lista
n: número de imagens na lista
i: deslocamento de coluna
j: deslocamento de linha
p: pixel a usar (absoluto ou relativo ao pixel atual)
w: largura desta imagem
h: altura desta imagem
z: profundidade de canal
r: valor do vermelho (de RGBA), de um pixel específico ou atual
g: verde
b: azul
a: alfa
o: opacidade
c: valor de ciano da cor CMYK do pixel
y: amarelo
m: magenta
k: preto
all: todos os canais
this: este canal
intensity: intensidade do pixel
hue: matiz do pixel
saturation: saturação do pixel
lightness: luminosidade do pixel
luma: luma do pixel
page.width: largura da página
page.height: altura da página
page.x: deslocamento x da página
page.y: deslocamento y da página
printsize.x: tamanho de impressão x
printsize.y: tamanho de impressão y
resolution.x: resolução x
resolution.y: resolução y
depth: profundidade da imagem
extent: extensão da imagem
minima: mínimo da imagem
maxima: máximo da imagem
mean: média da imagem
median: mediana da imagem
standard_deviation: desvio padrão da imagem
kurtosis: curtose da imagem
skewness: assimetria da imagem (adicione um especificador de canal para calcular uma estatística para esse canal, e.g. depth.r)
iterators:
do(), for(), while()
image attributes:
s.depth, s.kurtosis, s.maxima, s.mean, s.minima, s.resolution.x, s.resolution.y, s.skewness, s.standard_deviation
user settings:

defina símbolos Fx como configurações de usuário, e.g.

magick ... -set option:wd1 "%[fx:w/2]" -resize "%[fx:wd1-5]" ...

A Expressão FX

Uma expressão FX pode incluir qualquer combinação dos seguintes:

x ^ y
exponenciação (xy)
( ... )
agrupamento
x * y
multiplicação
x / y
divisão
x % y
módulo
x + y
adição
x - y
subtração
x << y
deslocamento à esquerda
x >> y
deslocamento à direita
x < y
relação booleana, retorna o valor 1.0 se x < y, caso contrário 0.0
x <= y
relação booleana, retorna o valor 1.0 se x <= y, caso contrário 0.0
x > y
relação booleana, retorna o valor 1.0 se x > y, caso contrário 0.0
x >= y
relação booleana, retorna o valor 1.0 se x >= y, caso contrário 0.0
x == y
relação booleana, retorna o valor 1.0 se x == y dentro de epsilon (1e-12), caso contrário 0.0. Dois valores são iguais quando sua diferença está dentro de cerca de 1e‑12.
x != y
relação booleana, retorna o valor 1.0 se x != y dentro de epsilon (1e-12), caso contrário 0.0
x & y
AND binário
x | y
OR binário
x && y
conectivo AND lógico, retorna o valor 1.0 se x > 0 e y > 0, caso contrário 0.0
x || y
conectivo OR lógico (inclusivo), retorna o valor 1.0 se x > 0 ou y > 0 (ou ambos), caso contrário 0.0
~x
operador NOT lógico, retorna o valor 1.0 se não x > 0, caso contrário 0.0
+x
mais unário, retorna 1.0*valor
-x
menos unário, retorna -1.0*valor
condition ? true-statements : false-statements
expressão condicional ternária, retorna o valor true-statements se condition != 0, caso contrário false-statements
x = y
atribuição; variáveis de um único caractere são reservadas, em vez disso use 2 ou mais caracteres, apenas combinações de letras (e.g. Xi e não X1)
x ; y
separador de instrução
phi
constante (1.618034...)
pi
constante (3.14159265359...)
e
constante (2.71828...)
QuantumRange
constante de valor máximo de pixel (255 para Q8, 65535 para Q16)
QuantumScale
constante 1.0/QuantumRange
intensity
intensidade do pixel cujo valor respeita a opção -intensity.
hue
matiz do pixel
saturation
saturação do pixel
lightness
luminosidade do pixel; equivalente a 0.5max(red,green,blue) + 0.5min(red,green,blue)
luminance
luminância do pixel; equivalente a 0.212656red + 0.715158green + 0.072186*blue
red, green, blue, etc.
nomes de cor

ccc, #cbfed0, #b9e1cc00, etc.

 valores hexadecimais de cor
rgb(), rgba(), cmyk(), cmyka(), hsl(), hsla()
funções de cor
s, t, u, v, n, i, j, w, h, z, r, g, b, a, o, c, y, m, k
símbolos
abs(x)
função de valor absoluto
acos(x)
função arco cosseno
acosh(x)
função cosseno hiperbólico inverso
airy(x)
função de Airy (max=1, min=0); airy(x)=[jinc(x)]2=[2j1(pix)/(pi*x)]2
alt(x)
função de alternância de sinal (retorna 1.0 se int(x) for par, -1.0 se int(x) for ímpar)
asin(x)
função arco seno
asinh(x)
função seno hiperbólico inverso
atan(x)
função arco tangente
atanh(x)
função tangente hiperbólica inversa
atan2(y,x)
função arco tangente de duas variáveis
ceil(x)
menor valor inteiro não inferior ao argumento
channel(...)
suporta de zero a cinco argumentos, e.g., channel(0.1) define o primeiro canal como 0.1 e zera os demais canais.
clamp(x)
limita o valor
cos(x)
função cosseno
cosh(x)
função cosseno hiperbólico
debug(x)
imprime x (útil para depurar sua expressão)
do(statements, condition)
itera enquanto a condição for diferente de 0
drc(x,y)
compressão de faixa dinâmica (curva de joelho); drc(x,y)=(x)/(y*(x-1)+1); -1<y<1
epoch(date-time)
converte a propriedade date-time no número de segundos desde a época, 00:00:00 UTC
erf(x)
função de erro
exp(x)
função exponencial natural (ex)
floor(x)
maior valor inteiro não superior ao argumento
for(initialize, condition, statements)
itera enquanto a condição for diferente de 0
gauss(x)
função gaussiana; gauss(x)=exp(-xx/2)/sqrt(2pi)
gcd(x,y)
máximo divisor comum
hypot(x,y)
a raiz quadrada de x2+y2
if(condition, nonzero-statements, zero-statements)
interpreta a expressão dependendo da condição
int(x)
função maior inteiro (retorna o maior inteiro menor ou igual a x)
isnan(x)
retorna 1.0 se x for NAN, 0.0 caso contrário
j0(x)
funções de Bessel de x do primeiro tipo de ordem 0
j1(x)
funções de Bessel de x do primeiro tipo de ordem 1
jinc(x)
função jinc (max=1, min=-0.1323); jinc(x)=2j1(pix)/(pi**x)
ln(x)
função logaritmo natural
log(x)
logaritmo base 10
logtwo(x)
logaritmo base 2
ln(x)
logaritmo natural
magicktime()
o tempo atual em segundos desde a época, 00:00:00 UTC
max(x, y)
máximo entre x e y
min(x, y)
mínimo entre x e y
mod(x, y)
função resto de ponto flutuante
not(x)
retorna 1.0 se x for zero, 0.0 caso contrário
pow(x,y)
função potência (xy)
rand()
valor uniformemente distribuído no intervalo [0.0, 1.0) com um período de 2 elevado à 128ª -1
round()
arredonda para o valor inteiro, independentemente da direção de arredondamento
sign(x)
retorna -1.0 se x for menor que 0.0, caso contrário 1.0
sin(x)
função seno
sinc(x)
função sinc (max=1, min=-0.21); sinc(x)=sin(pix)/(pix)
squish(x)
função squish; squish(x)=1.0/(1.0+exp(-x))
sinh(x)
função seno hiperbólico
sqrt(x)
função raiz quadrada
tan(x)
função tangente
tanh(x)
função tangente hiperbólica
trunc(x)
arredonda para inteiro, em direção a zero
while(condition, statements)
itera enquanto a condição for diferente de 0
image.depth, image.kurtosis, image.maxima, image.mean, image.median, image.minima, image.resolution.x, image.resolution.y, image.skewness, image.standard_deviation
atributos da imagem

A semântica da expressão inclui estas regras:

  • os símbolos não diferenciam maiúsculas de minúsculas
  • apenas um condicional ternário (e.g. x ? y : z) por instrução
  • instruções são atribuições ou a expressão final a ser retornada
  • uma atribuição inicia uma instrução, ela não é um operador
  • variáveis de um único caractere são reservadas. Atribuições a built-ins reservados lançam uma exceção; e.g. r=3.0; r retorna Attempted assignment to non-UserSymbol 'r' at '3.0'.
  • operadores unários têm prioridade mais baixa que operadores binários, isto é, o menos unário (negação) tem precedência mais baixa que a exponenciação, então -3^2 é interpretado como -(3^2) = -9. Use parênteses para esclarecer sua intenção (e.g. (-3)^2 = 9).
  • deve-se ter cuidado ao usar o símbolo de barra ('/'). A sequência de caracteres 1/2x é interpretada como (1/2)x. A interpretação contrária deve ser escrita explicitamente como 1/(2x). Novamente, o uso de parênteses ajuda a esclarecer o significado e deve ser usado sempre que houver qualquer chance de má interpretação.
  • Como -- é o operador de decremento de variável, use parênteses para subtrair um número negativo, e.g, -4-(-5).

Imagens de Origem

Os símbolos u e v referem-se à primeira e à segunda imagens, respectivamente, na sequência de imagens atual. Refira-se a uma imagem específica de uma sequência acrescentando seu índice a qualquer referência de imagem (geralmente u), com índice zero para o início da sequência. Um índice negativo conta a partir do fim. Por exemplo, u[0] é a primeira imagem da sequência, u[2] é a terceira, u[-1] é a última imagem e u[t] é a imagem atual. A imagem atual também pode ser referenciada por s. Se o número da sequência exceder o comprimento da sequência, a contagem é envolvida (wrapped). Assim, em uma sequência de 3 imagens, u[-1], u[2] e u[5] referem-se todos à mesma (terceira) imagem.

Como exemplo, formamos uma imagem fazendo a média da primeira e da terceira imagens (a segunda imagem (índice 1) é ignorada e simplesmente descartada):

magick image1.jpg image2.jpg image3.jpg -fx "(u+u[2])/2" image.jpg

Por padrão, a imagem à qual p, r, g, b, a, etc., são aplicados é a imagem atual s na lista de imagens. Isso é equivalente a u, exceto quando usado em uma sequência de escape %[fx:...].

É importante observar o papel especial desempenhado pela primeira imagem. Esta é a única imagem da sequência de imagens que é modificada; as demais imagens são usadas apenas pelos seus dados. Como exemplo ilustrativo, considere o seguinte e observe que a configuração -channel red instrui -fx a modificar apenas o canal verde; nada nos canais vermelho ou azul mudará. É instrutivo refletir sobre por que o resultado não é simétrico.

magick logo: -flop logo: -resize "20%" -channel green -fx "(u+v)/2" image.jpg

logo-sm-flop.png logo-sm.png ==> logo-sm-fx.png

Acessando Pixels

Todos os valores de cor são normalizados para a faixa de 0.0 a 1.0. O canal alfa varia de 0.0 (totalmente transparente) a 1.0 (totalmente opaco).

Os pixels são processados um de cada vez, mas um pixel diferente de uma imagem pode ser especificado usando um índice de pixel representado por p. Por exemplo,

p[-1].g      valor do verde do pixel imediatamente à esquerda do pixel atual
p[-1,-1].r   valor do vermelho do pixel diagonalmente à esquerda e acima do pixel atual

Para especificar uma posição absoluta, use chaves, em vez de colchetes.

p{0,0}.r     valor do vermelho do pixel no canto superior esquerdo da imagem
p{12,34}.b   valor de pixel azul na coluna número 12, linha 34 da imagem

Valores inteiros da posição recuperam a cor do pixel referenciado, enquanto valores de posição não inteiros retornam uma cor mesclada de acordo com a configuração atual de -interpolate.

Uma posição fora dos limites da imagem recupera um valor ditado pela configuração da opção -virtual-pixel.

Especifique u.r para especificar o canal vermelho da imagem atual. Se você não especificar um qualificador de canal, obtém o canal atual. Use mean.this para definir o canal de saída como a média apenas do canal de entrada. Use mean.all para definir a média geral dos canais de entrada.

Aplicar uma Expressão a Canais de Imagem Selecionados

Use a configuração -channel para especificar o canal de saída do resultado. Se nenhum canal de saída for fornecido, o resultado é definido em todos os canais, exceto o canal de opacidade. Por exemplo, para substituir o canal vermelho de alpha.png pela média dos canais verdes das imagens alpha.png e beta.png, use:

magick alpha.png beta.png -channel red -fx "(u.g+v.g)/2" gamma.png

Resultados

O operador -fx avalia a expressão fornecida para cada canal (definido por -channel) de cada pixel na primeira imagem (u) da sequência. Os valores calculados são armazenados temporariamente em uma cópia (clone) dessa primeira imagem até que todos os pixels tenham sido processados, após o que esta única nova imagem substitui a lista de imagens na sequência de imagens atual. Assim, no exemplo anterior, a versão atualizada de alpha.png substitui ambas as imagens originais, alpha.png e beta.png, antes de ser salva como gamma.png.

A imagem atual s é definida como a primeira imagem da sequência (u), e t como seu índice, 0. Os símbolos i e j referenciam o pixel atual sendo processado.

Para uso com -format, o escape de valor %[fx:] é avaliado apenas uma vez para cada imagem na sequência de imagens atual. À medida que cada imagem da sequência é avaliada, s e t referem-se sucessivamente à imagem atual e ao seu índice, enquanto i e j são definidos como zero, e o canal atual é definido como vermelho (-channel é ignorado). Um exemplo:

$ magick canvas:'rgb(25%,50%,75%)' rose: -colorspace gray  \
  -format 'Red channel of NW corner of image #%[fx:t] is %[fx:s]\n' info:
Red channel of NW corner of image #0 is 0.464883
Red channel of NW corner of image #1 is 0.184582

Aqui usamos os índices das imagens para rotacionar cada imagem de forma diferente, e usamos -set com o índice da imagem para definir um atraso de pausa diferente na primeira imagem da animação:

magick rose: -duplicate 29 -virtual-pixel Gray -distort SRT '%[fx:360.0*t/n]' \
  -set delay '%[fx:t == 0 ? 240 : 10]' -loop 0 rose.gif

Este exemplo testa a diferença entre duas imagens, medida pelo RMSE. Se a diferença for maior que 0.1, retorna 1; caso contrário, retorna 0:

magick water.png reference.png -metric RMSE -compare -format "%[fx:%[distortion]>0.1]" info:

O escape de cor %[pixel:] ou %[hex:] é avaliado uma vez por imagem e por canal de cor nessa imagem (-channel é ignorado). Os valores gerados são então convertidos em uma string de cor (um nome de cor ou um valor de cor hexadecimal). Os símbolos i e j são definidos como zero, e s e t referem-se a cada imagem atual sucessiva e seu índice.

O método epoch() recebe uma propriedade date-time, por exemplo:

magick rose.png -precision 16 -format '%[fx:epoch(%%[date:modify])]' info: