Color Modes

FX 特效图像操作符

FX 特效图像操作符FX 表达式的结构

FX 特效图像操作符 对图像的每个像素通道应用数学表达式。FX 表达式语言提供了一种强大而灵活的方式来操作图像,使您可以对图像执行各种操作和变换。使用 FX 来

  • 创建画布、渐变、数学色彩图
  • 在图像和通道之间移动颜色值
  • 平移、翻转、镜像、旋转、缩放、倾斜以及通常扭曲图像
  • 将多个图像合并或合成在一起
  • 卷积或合并相邻像素
  • 生成图像度量或“指纹”

操作符遍历图像的所有像素以及每个像素的所有通道,并返回一个包含结果的新图像。表达式可以引用图像序列中的任何图像,但只返回第一个图像的副本,并根据您的表达式进行适当的更新。

表达式可以很简单

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

这里,我们将黑色图像转换为深蓝色图像

    black ==> navy

或者表达式可以很复杂

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

此表达式将生成源图像的高对比度版本

    rose ==> rose-sigmoidal

表达式可以包含变量赋值。在大多数情况下,赋值可以降低表达式的复杂性,并允许一些其他方式可能无法进行的操作。例如,让我们创建一个径向渐变

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

上面的命令返回此图像

    radial-gradient

此 FX 表达式将随机噪声添加到图像中

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

此 FX 脚本使用循环来创建一个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

此 FX 脚本打印前 10 个素数

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:

有关更多示例,请参见 使用 FX,特效图像操作符

-fx 选项用第一个图像的克隆替换任何图像序列,该克隆用表达式的结果更新。如果您希望将表达式应用于序列中的每个图像,请改为使用 +fx

FX 表达式在单线程中解释,但它在多个线程中执行,除非表达式包含 debug() 函数。

下一部分讨论 FX 表达式语言。

FX 表达式的结构

FX 表达式语言

正式的 FX 表达式语言定义如下

数字
整数、浮点数、科学记数法(需要 +/-,例如 3.81469e-06)、国际单位制数字后缀(例如 KB、Mib、GB 等)
常量
E(欧拉数)、Epsilon、Opaque、Phi(黄金分割)、Pi、QuantumRange、QuantumScale、Transparent
FX 操作符(按优先级顺序)
^(幂)、一元 -、*、/、%(模)、+、-、<<、>>、<、<=、>、>=、+=、-=、*=、/=、%=、<<= 、>>=、&=、|=、++、--、==、!=、&(按位与)、|(按位或)、&&(逻辑与)、||(逻辑或)、~(逻辑非)、?:(三元条件)
数组
图像提供数组存储(例如 p[-1,-1].r),其边界为宽度和高度。图像序列表示多个数组(例如 u.p[0,0].r、v.p[0,0].r)。存储限于 Quantum 值,例如,对于 Q16 版本为 [0..65535],对于支持 HDRI 的版本为浮点数。
数学函数
abs()、acos()、acosh()、airy()、alt()、asin()、asinh()、atan()、atanh()、atan2()、ceil()、clamp()、cos()、cosh()、debug()、drc()、erf()、exp()、floor()、gauss()、gcd()、hypot()、int()、isnan()、j0()、j1()、jinc()、ln()、log()、logtwo()、max()、min()、mod()、not()、pow()、rand()、round()、sign()、sin()、sinc()、sinh()、sqrt()、squish()、tan()、tanh()、trunc()
通道函数
定义最多 5 个像素通道
颜色名称
红色、青色、黑色等
颜色函数
srgb()、srgba()、rgb()、rgba()、cmyk()、cmyka()、hsl()、hsla() 等
颜色十六进制值
#ccc、#cbfed0、#b9e1cc00 等
符号
u:列表中的第一个图像
v:列表中的第二个图像
s:列表中的当前图像(对于 %[fx:] 否则 = u)
t:列表中当前图像 (s) 的索引
n:列表中的图像数量
i:列偏移量
j:行偏移量
p:要使用的像素(相对于当前像素的绝对或相对值)
w:此图像的宽度
h:此图像的高度
z:通道深度
r:特定或当前像素的红色值(来自 RGBA)
g:绿色
b:蓝色
a:alpha
o:不透明度
c:像素的 CMYK 颜色的青色值
y:黄色
m:洋红
k:黑色
all:所有通道
this:此通道
intensity:像素强度
hue:像素色调
saturation:像素饱和度
lightness:像素亮度
luma:像素亮度
page.width:页面宽度
page.height:页面高度
page.x:页面 x 偏移量
page.y:页面 y 偏移量
printsize.x:x 打印尺寸
printsize.y:y 打印尺寸
resolution.x:x 分辨率
resolution.y:y 分辨率
depth:图像深度
extent:图像范围
minima:图像最小值
maxima:图像最大值
mean:图像平均值
median:图像中位数
standard_deviation:图像标准差
kurtosis:图像峰度
skewness:图像偏度(添加通道说明符以计算该通道的统计数据,例如 depth.r)
迭代器
do()、for()、while()
图像属性
s.depth、s.kurtosis、s.maxima、s.mean、s.minima、s.resolution.x、s.resolution.y、s.skewness、s.standard_deviation
用户设置
将 Fx 符号定义为用户设置,例如:
magick ... -set option:wd1 "%[fx:w/2]" -resize "%[fx:wd1-5]" ...

FX 表达式

FX 表达式可以包含以下任何组合

x ^ y
指数运算 (xy)
( ... )
分组
x * y
乘法
x / y
除法
x % y
模运算
x + y
加法
x - y
减法
x << y
左移
x >> y
右移
x < y
布尔关系,如果 x < y,则返回值 1.0,否则为 0.0
x <= y
布尔关系,如果 x <= y,则返回值 1.0,否则为 0.0
x > y
布尔关系,如果 x > y,则返回值 1.0,否则为 0.0
x >= y
布尔关系,如果 x >= y,则返回值 1.0,否则为 0.0
x == y
布尔关系,如果 x == y,则返回值 1.0,否则为 0.0
x != y
布尔关系,如果 x != y,则返回值 1.0,否则为 0.0
x & y
二进制与
x | y
二进制或
x && y
逻辑与连接词,如果 x > 0 且 y > 0,则返回值 1.0,否则为 0.0
x || y
逻辑或连接词(包含),如果 x > 0 或 y > 0(或两者),则返回值 1.0,否则为 0.0
~x
逻辑非运算符,如果 not x > 0,则返回值 1.0,否则为 0.0
+x
一元加,返回值 1.0*值
-x
一元减,返回值 -1.0*值
condition ? true-statements : false-statements
三元条件表达式,如果 condition != 0,则返回值 true-statements,否则为 false-statements
x = y
赋值;单字符变量是保留的,请改用 2 个或更多个字符,仅使用字母组合(例如 Xi 不是 X1)
x ; y
语句分隔符
phi
常量 (1.618034...)
pi
常量 (3.14159265359...)
e
常量 (2.71828...)
QuantumRange
常量最大像素值(Q8 为 255,Q16 为 65535)
QuantumScale
常量 1.0/QuantumRange
intensity
像素强度,其值符合 -intensity 选项。
hue
像素色调
saturation
像素饱和度
lightness
像素亮度;等效于 0.5*max(red,green,blue) + 0.5*min(red,green,blue)
luminance
像素亮度;等效于 0.212656*red + 0.715158*green + 0.072186*blue
red, green, blue
颜色名称
#ccc, #cbfed0, #b9e1cc00
颜色十六进制值
rgb()、rgba()、cmyk()、cmyka()、hsl()、hsla()
颜色函数
s、t、u、v、n、i、j、w、h、z、r、g、b、a、o、c、y、m、k
符号
abs(x)
绝对值函数
acos(x)
反余弦函数
acosh(x)
反双曲余弦函数
airy(x)
艾里函数 (max=1, min=0); airy(x)=[jinc(x)]2=[2*j1(pi*x)/(pi*x)]2
alt(x)
符号交替函数(如果 int(x) 为偶数,则返回值 1.0,如果 int(x) 为奇数,则返回值 -1.0)
asin(x)
反正弦函数
asinh(x)
反双曲正弦函数
atan(x)
反正切函数
atanh(x)
反双曲正切函数
atan2(y,x)
两个变量的反正切函数
ceil(x)
不小于参数的最小整数值
channel(...)
支持零到五个参数,例如,channel(0.1) 将第一个通道设置为 0.1 并将其他通道归零。
clamp(x)
钳位值
cos(x)
余弦函数
cosh(x)
双曲余弦函数
debug(x)
打印 x(在调试表达式时非常有用)
do(statements, condition)
在条件不等于 0 时迭代
drc(x,y)
动态范围压缩(膝盖曲线);drc(x,y)=(x)/(y*(x-1)+1); -1<y<1
erf(x)
误差函数
exp(x)
自然指数函数 (ex)
floor(x)
不大于参数的最大整数值
for(initialize, condition, statements)
在条件不等于 0 时迭代
gauss(x)
高斯函数;gauss(x)=exp(-x*x/2)/sqrt(2*pi)
gcd(x,y)
最大公约数
hypot(x,y)
x2+y2 的平方根
if(condition, nonzero-statements, zero-statements)
根据条件解释表达式
int(x)
最大整数函数(返回值不大于 x 的最大整数)
isnan(x)
如果 x 为 NAN,则返回 1.0,否则为 0.0
j0(x)
第一类零阶贝塞尔函数,自变量为 x
j1(x)
第一类一阶贝塞尔函数,自变量为 x
jinc(x)
jinc 函数(最大值为 1,最小值为 -0.1323);jinc(x)=2*j1(pi*x)/(pi**x)
ln(x)
自然对数函数
log(x)
以 10 为底的对数函数
logtwo(x)
以 2 为底的对数函数
ln(x)
自然对数
max(x, y)
xy 中的最大值
min(x, y)
xy 中的最小值
mod(x, y)
浮点数求余函数
not(x)
如果 x 为零,则返回 1.0,否则返回 0.0
pow(x,y)
幂函数 (xy)
rand()
在区间 [0.0, 1.0) 内均匀分布的值,周期为 2 的 128 次方减 1
round()
舍入到整数,不考虑舍入方向
sign(x)
如果 x 小于 0.0,则返回 -1.0,否则返回 1.0
sin(x)
正弦函数
sinc(x)
sinc 函数(最大值为 1,最小值为 -0.21);sinc(x)=sin(pi*x)/(pi*x)
squish(x)
squish 函数;squish(x)=1.0/(1.0+exp(-x))
sinh(x)
双曲正弦函数
sqrt(x)
平方根函数
tan(x)
正切函数
tanh(x)
双曲正切函数
trunc(x)
舍入到整数,朝零方向舍入
while(condition, statements)
在条件不等于 0 时迭代
image.depth, image.kurtosis, image.maxima, image.mean, image.median, image.minima, image.resolution.x, image.resolution.y, image.skewness, image.standard_deviation
图像属性

表达式语义包括以下规则:

  • 符号不区分大小写
  • 每个语句只允许一个三元运算符(例如 x ? y : z)
  • 语句是赋值语句或要返回的最终表达式
  • 赋值语句开始一个新的语句,它不是运算符
  • 单字符变量是保留的。对保留的内置函数进行赋值会导致异常,例如 r=3.0; r 返回 在 '3.0' 处尝试对非用户符号 'r' 进行赋值
  • 一元运算符的优先级低于二元运算符,即一元减号(否定)的优先级低于求幂运算,因此 -3^2 被解释为 -(3^2) = -9。使用括号来明确你的意图(例如 (-3)^2 = 9)。
  • 在使用斜杠('/')符号时要格外小心。字符序列 1/2x 被解释为 (1/2)x。相反的解释应该显式地写为 1/(2x)。再次强调,使用括号有助于澄清含义,在任何可能产生误解的地方都应该使用括号。
  • 由于 -- 是变量递减运算符,因此使用括号来减去一个负数,例如 -4-(-5)

源图像

符号 uv 分别指当前图像序列中的第一张图像和第二张图像。通过在任何图像引用(通常是 u)后面附加其索引来引用序列中的特定图像,其中序列的开头为零索引。负索引从末尾开始计数。例如,u[0] 是序列中的第一张图像,u[2] 是第三张图像,u[-1] 是最后一张图像,u[t] 是当前图像。当前图像也可以用 s 来引用。如果序列号超过序列的长度,则计数会循环。因此,在 3 张图像的序列中,u[-1]u[2]u[5] 都引用相同的(第三张)图像。

例如,通过对第一张图像和第三张图像(忽略第二张图像(索引为 1))进行平均来形成一张图像。

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

默认情况下,应用 prgba 等等的图像是在图像列表中的当前图像 s。这等效于 u,除非在转义序列 %[fx:...] 中使用。

需要注意的是第一张图像所扮演的特殊角色。它是图像序列中唯一被修改的图像,其他图像只用于其数据。作为一个说明性的例子,考虑以下内容,并注意设置 -channel red 会指示 -fx 只修改绿色通道;红色或蓝色通道中的内容不会改变。思考一下为什么结果不是对称的,这是很有启发的。

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

访问像素

所有颜色值都归一化为 0.0 到 1.0 的范围。alpha 通道范围从 0.0(完全透明)到 1.0(完全不透明)。

像素是一次处理一个,但是可以使用由 p 表示的像素索引来指定图像中的不同像素。例如:

p[-1].g      green value of pixel to the immediate left of the current pixel
p[-1,-1].r   red value of the pixel diagonally left and up from current pixel

要指定绝对位置,请使用花括号,而不是方括号。

p{0,0}.r     red value of the pixel in the upper left corner of the image
p{12,34}.b   blue pixel value at column number 12, row 34 of the image

位置的整数值将检索引用像素的颜色,而非整数值的位置将根据当前 -interpolate 设置返回混合的颜色。

图像边界之外的位置将检索由 -virtual-pixel 选项设置决定的值。

指定 u.r 来指定当前图像的红色通道。如果不指定通道限定符,则获得当前通道。使用 mean.this 将输出通道设置为仅输入通道的平均值。使用 mean.all 设置输入通道的总体平均值。

应用表达式来选择图像通道

使用 -channel 设置来指定结果的输出通道。如果没有给出输出通道,则结果将设置在所有通道上,除了不透明度通道。例如,要将 alpha.png 的红色通道替换为来自 alpha.pngbeta.png 图像的绿色通道的平均值,请使用

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

结果

-fx 运算符会针对序列中第一张图像 (u) 的每个像素的每个通道(由 -channel 设置)计算给定的表达式。计算出的值会临时存储在该第一张图像的副本(克隆)中,直到所有像素都处理完毕,之后这个新的单一图像会替换当前图像序列中的图像列表。因此,在前面的示例中,alpha.png 的更新版本会替换原始的 alpha.pngbeta.png 两张图像,然后再保存为 gamma.png

当前图像 s 被设置为序列中的第一张图像 (u),t 被设置为它的索引 0。符号 ij 引用正在处理的当前像素。

用于 -format 的值转义 %[fx:] 仅对当前图像序列中的每张图像评估一次。当评估序列中的每张图像时,st 会依次引用当前图像及其索引,而 ij 会被设置为零,当前通道被设置为红色(-channel 被忽略)。一个例子

$ 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

在这里,我们使用图像索引来以不同的方式 旋转 每张图像,并使用 -set 和图像索引来为动画中的第一张图像设置不同的 暂停延迟

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

此示例测试两张图像之间的差异,通过 RMSE 来衡量。如果差异大于 0.1,则返回 1;否则返回 0

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

颜色转义 %[pixel:]%[hex:] 会针对图像中的每张图像和每个颜色通道(-channel 被忽略)评估一次。生成的数值随后被转换为颜色字符串(命名颜色或十六进制颜色值)。符号 ij 被设置为零,st 分别引用依次当前图像和索引。