FX 特效图像操作符 对图像的每个像素通道应用数学表达式。FX 表达式语言提供了一种强大而灵活的方式来操作图像,使您可以对图像执行各种操作和变换。使用 FX 来
- 创建画布、渐变、数学色彩图
- 在图像和通道之间移动颜色值
- 平移、翻转、镜像、旋转、缩放、倾斜以及通常扭曲图像
- 将多个图像合并或合成在一起
- 卷积或合并相邻像素
- 生成图像度量或“指纹”
操作符遍历图像的所有像素以及每个像素的所有通道,并返回一个包含结果的新图像。表达式可以引用图像序列中的任何图像,但只返回第一个图像的副本,并根据您的表达式进行适当的更新。
表达式可以很简单
magick -size 64x64 canvas:black -channel blue -fx "1/2" fx_navy.png
这里,我们将黑色图像转换为深蓝色图像
或者表达式可以很复杂
magick rose: \ -fx "(1.0/(1.0+exp(10.0*(0.5-u)))-0.006693)*1.0092503" \ rose-sigmoidal.png
此表达式将生成源图像的高对比度版本
表达式可以包含变量赋值。在大多数情况下,赋值可以降低表达式的复杂性,并允许一些其他方式可能无法进行的操作。例如,让我们创建一个径向渐变
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
上面的命令返回此图像
此 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
此 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)
- x 和 y 中的最大值
- min(x, y)
- x 和 y 中的最小值
- 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)。
源图像
符号 u 和 v 分别指当前图像序列中的第一张图像和第二张图像。通过在任何图像引用(通常是 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
默认情况下,应用 p、r、g、b、a 等等的图像是在图像列表中的当前图像 s。这等效于 u,除非在转义序列 %[fx:...] 中使用。
需要注意的是第一张图像所扮演的特殊角色。它是图像序列中唯一被修改的图像,其他图像只用于其数据。作为一个说明性的例子,考虑以下内容,并注意设置 -channel red 会指示 -fx 只修改绿色通道;红色或蓝色通道中的内容不会改变。思考一下为什么结果不是对称的,这是很有启发的。
magick logo: -flop logo: -resize "20%" -channel green -fx "(u+v)/2" image.jpg
访问像素
所有颜色值都归一化为 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.png 和 beta.png 图像的绿色通道的平均值,请使用
magick alpha.png beta.png -channel red -fx "(u.g+v.g)/2" gamma.png
结果
-fx 运算符会针对序列中第一张图像 (u) 的每个像素的每个通道(由 -channel 设置)计算给定的表达式。计算出的值会临时存储在该第一张图像的副本(克隆)中,直到所有像素都处理完毕,之后这个新的单一图像会替换当前图像序列中的图像列表。因此,在前面的示例中,alpha.png 的更新版本会替换原始的 alpha.png 和 beta.png 两张图像,然后再保存为 gamma.png。
当前图像 s 被设置为序列中的第一张图像 (u),t 被设置为它的索引 0。符号 i 和 j 引用正在处理的当前像素。
用于 -format 的值转义 %[fx:] 仅对当前图像序列中的每张图像评估一次。当评估序列中的每张图像时,s 和 t 会依次引用当前图像及其索引,而 i 和 j 会被设置为零,当前通道被设置为红色(-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 被忽略)评估一次。生成的数值随后被转换为颜色字符串(命名颜色或十六进制颜色值)。符号 i 和 j 被设置为零,s 和 t 分别引用依次当前图像和索引。