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:
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:
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:
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
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
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:








