⚠️ Este es un sitio de traducción no oficial, sin relación con ImageMagick Studio LLC. Para información autorizada, consulte la página original (https://imagemagick.org/fx/).

La anatomía de una expresión FX

El operador de imagen de efectos especiales FX • La anatomía de una expresión FX

El operador de imagen de efectos especiales FX aplica una expresión matemática a cada canal de píxel de una imagen. El lenguaje de expresiones FX ofrece una manera potente y flexible de manipular imágenes, lo que permite realizar una amplia variedad de operaciones y transformaciones sobre ellas. Use FX para:

  • crear lienzos, degradados y mapas de color matemáticos
  • mover valores de color entre imágenes y canales
  • trasladar, voltear, reflejar, rotar, escalar, cizallar y, en general, distorsionar imágenes
  • fusionar o componer varias imágenes
  • convolucionar o fusionar píxeles vecinos
  • generar métricas o «huellas digitales» de imágenes

Este operador recorre todos los píxeles de una imagen y todos los canales de cada píxel, y devuelve una nueva imagen con los resultados. La expresión puede hacer referencia a cualquier imagen de la secuencia de imágenes, pero solo se devuelve una copia de la primera imagen con las actualizaciones adecuadas según su expresión.

La expresión puede ser sencilla:

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

Aquí convertimos una imagen negra en una imagen azul marino:

black ==> navy

O bien la expresión puede ser compleja:

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

Esta expresión produce una versión de alto contraste de la imagen de origen:

rose ==> rose-sigmoidal

La expresión puede incluir asignaciones de variables. Las asignaciones, en la mayoría de los casos, reducen la complejidad de una expresión y permiten algunas operaciones que de otro modo no serían posibles. Por ejemplo, creemos un degradado 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

El comando anterior devuelve esta imagen:

radial-gradient

Esta expresión FX añade ruido aleatorio a una imagen:

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 usa un bucle para crear un 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 los primeros 10 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 ver más ejemplos, consulte Using FX, The Special Effects Image Operator.

La opción -fx reemplaza cualquier secuencia de imágenes por un clon de la primera imagen actualizado con los resultados de la expresión. Si prefiere aplicar la expresión a cada imagen de la secuencia, use +fx en su lugar.

La expresión FX se interpreta en un solo hilo; sin embargo, se ejecuta en varios hilos, salvo que la expresión incluya la función debug().

La siguiente sección describe el lenguaje de expresiones FX.

La anatomía de una expresión FX

El lenguaje de expresiones FX

El lenguaje formal de expresiones FX se define aquí:

numbers:
entero, punto flotante, notación científica (se requiere +/−, p. ej. 3.81469e-06), sufijos numéricos del Sistema Internacional (p. ej. KB, Mib, GB, etc.)
constants:
E (número de Euler), Epsilon, Opaque, Phi (proporción áurea), Pi, QuantumRange, QuantumScale, Transparent
FX operators (en orden de precedencia):
^ (potencia), - unario, , /, % (módulo), +, -, <<, >>, <, <=, >, >=, +=, -=, =, /=, %=, <<= , >>=, &=, |=, ++, --, ==, !=, & (AND a nivel de bits), | (OR a nivel de bits), && (AND lógico), || (OR lógico), ~ (NOT lógico), ?: (condicional ternario)
array:
una imagen ofrece almacenamiento en forma de arreglo (p. ej. p[-1,-1].r) acotado por su anchura y altura. Una secuencia de imágenes representa varios arreglos (p. ej. u.p[0,0].r, v.p[0,0].r). El almacenamiento se limita a valores Quantum, p. ej. [0..65535] para compilaciones Q16 y punto flotante para compilaciones con 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:
definen hasta 5 canales de píxel
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: primera imagen de la lista
v: segunda imagen de la lista
s: imagen actual de la lista (para %[fx:]; en caso contrario = u)
t: índice de la imagen actual (s) en la lista
n: número de imágenes de la lista
i: desplazamiento de columna
j: desplazamiento de fila
p: píxel a usar (absoluto o relativo al píxel actual)
w: anchura de esta imagen
h: altura de esta imagen
z: profundidad de canal
r: valor de rojo (de RGBA), de un píxel específico o del actual
g: verde
b: azul
a: alfa
o: opacidad
c: valor cian del color CMYK del píxel
y: amarillo
m: magenta
k: negro
all: todos los canales
this: este canal
intensity: intensidad del píxel
hue: matiz del píxel
saturation: saturación del píxel
lightness: luminosidad del píxel
luma: luma del píxel
page.width: anchura de la página
page.height: altura de la página
page.x: desplazamiento x de la página
page.y: desplazamiento y de la página
printsize.x: tamaño de impresión x
printsize.y: tamaño de impresión y
resolution.x: resolución x
resolution.y: resolución y
depth: profundidad de la imagen
extent: extensión de la imagen
minima: mínimo de la imagen
maxima: máximo de la imagen
mean: media de la imagen
median: mediana de la imagen
standard_deviation: desviación estándar de la imagen
kurtosis: curtosis de la imagen
skewness: asimetría de la imagen (añada un especificador de canal para calcular una estadística de ese canal, p. ej. 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:

define símbolos Fx como ajustes de usuario, p. ej.

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

La expresión FX

Una expresión FX puede incluir cualquier combinación de lo siguiente:

x ^ y
exponenciación (x elevado a y)
( ... )
agrupación
x * y
multiplicación
x / y
división
x % y
módulo
x + y
suma
x - y
resta
x << y
desplazamiento a la izquierda
x >> y
desplazamiento a la derecha
x < y
relación booleana, devuelve el valor 1.0 si x < y, de lo contrario 0.0
x <= y
relación booleana, devuelve el valor 1.0 si x <= y, de lo contrario 0.0
x > y
relación booleana, devuelve el valor 1.0 si x > y, de lo contrario 0.0
x >= y
relación booleana, devuelve el valor 1.0 si x >= y, de lo contrario 0.0
x == y
relación booleana, devuelve el valor 1.0 si x == y dentro de epsilon (1e-12), de lo contrario 0.0. Dos valores son iguales cuando su diferencia está dentro de aproximadamente 1e‑12.
x != y
relación booleana, devuelve el valor 1.0 si x != y dentro de epsilon (1e-12), de lo contrario 0.0
x & y
AND binario
x | y
OR binario
x && y
conectiva AND lógica, devuelve el valor 1.0 si x > 0 e y > 0, de lo contrario 0.0
x || y
conectiva OR lógica (inclusiva), devuelve el valor 1.0 si x > 0 o y > 0 (o ambos), de lo contrario 0.0
~x
operador NOT lógico, devuelve el valor 1.0 si no se cumple x > 0, de lo contrario 0.0
+x
más unario, devuelve 1.0*valor
-x
menos unario, devuelve -1.0*valor
condition ? true-statements : false-statements
expresión condicional ternaria, devuelve true-statements si condition != 0, de lo contrario false-statements
x = y
asignación; las variables de un solo carácter están reservadas, use en su lugar 2 o más caracteres, solo combinaciones de letras (p. ej. Xi, no X1)
x ; y
separador de instrucciones
phi
constante (1.618034...)
pi
constante (3.14159265359...)
e
constante (2.71828...)
QuantumRange
constante del valor máximo de píxel (255 para Q8, 65535 para Q16)
QuantumScale
constante 1.0/QuantumRange
intensity
intensidad del píxel cuyo valor respeta la opción -intensity.
hue
matiz del píxel
saturation
saturación del píxel
lightness
luminosidad del píxel; equivale a 0.5max(red,green,blue) + 0.5min(red,green,blue)
luminance
luminancia del píxel; equivale a 0.212656red + 0.715158green + 0.072186*blue
red, green, blue, etc.
nombres de color

ccc, #cbfed0, #b9e1cc00, etc.

 valores de color hexadecimales
rgb(), rgba(), cmyk(), cmyka(), hsl(), hsla()
funciones de color
s, t, u, v, n, i, j, w, h, z, r, g, b, a, o, c, y, m, k
símbolos
abs(x)
función de valor absoluto
acos(x)
función arco coseno
acosh(x)
función coseno hiperbólico inverso
airy(x)
función de Airy (máx=1, mín=0); airy(x)=[jinc(x)]2=[2j1(pix)/(pi*x)]2
alt(x)
función de alternancia de signo (devuelve 1.0 si int(x) es par, -1.0 si int(x) es impar)
asin(x)
función arco seno
asinh(x)
función seno hiperbólico inverso
atan(x)
función arco tangente
atanh(x)
función tangente hiperbólica inversa
atan2(y,x)
función arco tangente de dos variables
ceil(x)
el menor valor entero no inferior al argumento
channel(...)
admite de cero a cinco argumentos, p. ej., channel(0.1) establece el primer canal en 0.1 y pone a cero los demás canales.
clamp(x)
acota el valor
cos(x)
función coseno
cosh(x)
función coseno hiperbólico
debug(x)
imprime x (útil para depurar la expresión)
do(statements, condition)
itera mientras la condición no sea igual a 0
drc(x,y)
compresión de rango dinámico (curva de rodilla); drc(x,y)=(x)/(y*(x-1)+1); -1<y<1
epoch(date-time)
convierte una propiedad date-time al número de segundos desde la época, 00:00:00 UTC
erf(x)
función de error
exp(x)
función exponencial natural (e elevado a x)
floor(x)
el mayor valor entero no superior al argumento
for(initialize, condition, statements)
itera mientras la condición no sea igual a 0
gauss(x)
función gaussiana; gauss(x)=exp(-xx/2)/sqrt(2pi)
gcd(x,y)
máximo común divisor
hypot(x,y)
la raíz cuadrada de x2+y2
if(condition, nonzero-statements, zero-statements)
interpreta la expresión según la condición
int(x)
función parte entera (devuelve el mayor entero menor o igual que x)
isnan(x)
devuelve 1.0 si x es NAN, 0.0 en caso contrario
j0(x)
funciones de Bessel de x de primera especie de orden 0
j1(x)
funciones de Bessel de x de primera especie de orden 1
jinc(x)
función jinc (máx=1, mín=-0.1323); jinc(x)=2j1(pix)/(pi**x)
ln(x)
función logaritmo natural
log(x)
logaritmo en base 10
logtwo(x)
logaritmo en base 2
ln(x)
logaritmo natural
magicktime()
la hora actual en segundos desde la época, 00:00:00 UTC
max(x, y)
máximo de x e y
min(x, y)
mínimo de x e y
mod(x, y)
función de resto en punto flotante
not(x)
devuelve 1.0 si x es cero, 0.0 en caso contrario
pow(x,y)
función potencia (x elevado a y)
rand()
valor distribuido uniformemente en el intervalo [0.0, 1.0) con un período de 2 elevado a 128 menos 1
round()
redondea a un valor entero, independientemente de la dirección de redondeo
sign(x)
devuelve -1.0 si x es menor que 0.0, de lo contrario 1.0
sin(x)
función seno
sinc(x)
función sinc (máx=1, mín=-0.21); sinc(x)=sin(pix)/(pix)
squish(x)
función squish; squish(x)=1.0/(1.0+exp(-x))
sinh(x)
función seno hiperbólico
sqrt(x)
función raíz cuadrada
tan(x)
función tangente
tanh(x)
función tangente hiperbólica
trunc(x)
redondea a entero, hacia cero
while(condition, statements)
itera mientras la condición no sea igual a 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 de imagen

La semántica de la expresión incluye estas reglas:

  • los símbolos no distinguen mayúsculas de minúsculas
  • solo un condicional ternario (p. ej. x ? y : z) por instrucción
  • las instrucciones son asignaciones o la expresión final que se devuelve
  • una asignación inicia una instrucción, no es un operador
  • las variables de un solo carácter están reservadas. Las asignaciones a integradas reservadas lanzan una excepción; p. ej. r=3.0; r devuelve Attempted assignment to non-UserSymbol 'r' at '3.0'.
  • los operadores unarios tienen menor prioridad que los binarios, es decir, el menos unario (negación) tiene menor precedencia que la exponenciación, por lo que -3^2 se interpreta como -(3^2) = -9. Use paréntesis para aclarar su intención (p. ej. (-3)^2 = 9).
  • hay que tener cuidado al usar el símbolo de barra ('/'). La cadena de caracteres 1/2x se interpreta como (1/2)x. La interpretación contraria debe escribirse explícitamente como 1/(2x). De nuevo, el uso de paréntesis ayuda a aclarar el significado y debe emplearse siempre que exista alguna posibilidad de malinterpretación.
  • como -- es el operador de decremento de variable, use paréntesis para restar un número negativo, p. ej., -4-(-5).

Imágenes de origen

Los símbolos u y v se refieren, respectivamente, a la primera y a la segunda imagen de la secuencia de imágenes actual. Para referirse a una imagen concreta de una secuencia, añada su índice a cualquier referencia de imagen (normalmente u), con un índice cero para el inicio de la secuencia. Un índice negativo cuenta desde el final. Por ejemplo, u[0] es la primera imagen de la secuencia, u[2] es la tercera, u[-1] es la última imagen y u[t] es la imagen actual. La imagen actual también puede referenciarse con s. Si el número de secuencia excede la longitud de la secuencia, el conteo se envuelve. Así, en una secuencia de 3 imágenes, u[-1], u[2] y u[5] se refieren todos a la misma (la tercera) imagen.

Como ejemplo, formamos una imagen promediando la primera y la tercera imágenes (la segunda imagen (índice 1) se ignora y simplemente se descarta):

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

De forma predeterminada, la imagen a la que se aplican p, r, g, b, a, etc., es la imagen actual s de la lista de imágenes. Esto equivale a u, salvo cuando se usa en una secuencia de escape %[fx:...].

Es importante notar el papel especial que desempeña la primera imagen. Es la única imagen de la secuencia que se modifica; las demás imágenes se usan únicamente por sus datos. Como ejemplo ilustrativo, considere lo siguiente, y observe que el ajuste -channel red indica a -fx que modifique solo el canal verde; nada en los canales rojo o azul cambiará. Resulta instructivo reflexionar sobre por qué el resultado no es 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

Acceso a los píxeles

Todos los valores de color están normalizados al rango de 0.0 a 1.0. El canal alfa va de 0.0 (totalmente transparente) a 1.0 (totalmente opaco).

Los píxeles se procesan de uno en uno, pero se puede especificar un píxel distinto de una imagen mediante un índice de píxel representado por p. Por ejemplo,

p[-1].g      valor de verde del píxel inmediatamente a la izquierda del píxel actual
p[-1,-1].r   valor de rojo del píxel en diagonal a la izquierda y arriba del píxel actual

Para especificar una posición absoluta, use llaves en lugar de corchetes.

p{0,0}.r     valor de rojo del píxel de la esquina superior izquierda de la imagen
p{12,34}.b   valor de azul del píxel en la columna 12, fila 34 de la imagen

Los valores enteros de la posición recuperan el color del píxel referenciado, mientras que los valores de posición no enteros devuelven un color mezclado según el ajuste -interpolate actual.

Una posición fuera del límite de la imagen recupera un valor determinado por el ajuste de la opción -virtual-pixel.

Especifique u.r para indicar el canal rojo de la imagen actual. Si no especifica un calificador de canal, obtiene el canal actual. Use mean.this para establecer el canal de salida en la media de solo el canal de entrada. Use mean.all para establecer la media general de los canales de entrada.

Aplicar una expresión a canales de imagen seleccionados

Use el ajuste -channel para especificar el canal de salida del resultado. Si no se indica ningún canal de salida, el resultado se establece sobre todos los canales excepto el canal de opacidad. Por ejemplo, para reemplazar el canal rojo de alpha.png por la media de los canales verdes de las imágenes alpha.png y beta.png, use:

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

Resultados

El operador -fx evalúa la expresión dada para cada canal (establecido por -channel) de cada píxel de la primera imagen (u) de la secuencia. Los valores calculados se almacenan temporalmente en una copia (clon) de esa primera imagen hasta que se hayan procesado todos los píxeles, tras lo cual esta única imagen nueva reemplaza la lista de imágenes de la secuencia de imágenes actual. Así, en el ejemplo anterior, la versión actualizada de alpha.png reemplaza ambas imágenes originales, alpha.png y beta.png, antes de guardarse como gamma.png.

La imagen actual s se establece en la primera imagen de la secuencia (u), y t en su índice, 0. Los símbolos i y j hacen referencia al píxel actual que se está procesando.

Para usar con -format, el escape de valor %[fx:] se evalúa una sola vez por cada imagen de la secuencia de imágenes actual. A medida que se evalúa cada imagen de la secuencia, s y t hacen referencia sucesivamente a la imagen actual y a su índice, mientras que i y j se ponen a cero y el canal actual se establece en rojo (-channel se ignora). Un ejemplo:

$ 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

Aquí usamos los índices de imagen para rotar cada imagen de forma distinta, y usamos -set con el índice de imagen para establecer un retardo de pausa diferente en la primera imagen de la animación:

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 ejemplo prueba la diferencia entre dos imágenes, medida con RMSE. Si la diferencia es mayor que 0.1 devuelve 1; de lo contrario devuelve 0:

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

El escape de color %[pixel:] o %[hex:] se evalúa una vez por imagen y por canal de color de esa imagen (-channel se ignora). Los valores generados se convierten luego en una cadena de color (un nombre de color o un valor de color hexadecimal). Los símbolos i y j se ponen a cero, y s y t hacen referencia a cada imagen e índice sucesivamente actuales.

El método epoch() toma una propiedad date-time, por ejemplo:

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