⚠️ 这是一个非官方翻译网站,与 ImageMagick Studio LLC 无关。准确信息请参阅原文(https://usage.imagemagick.org/transform/index.html)

ImageMagick 使用示例 -- 图像变换

ImageMagick 使用示例 前言与索引
艺术化变换

这些运算会为了视觉效果或艺术化效果而对图像整体外观产生重大改变。然而,尽管图像整体外观往往发生了戏剧性的变化,原始图像本身通常在结果中仍然可以辨认出来。


艺术化变换

浮起或凹陷的边框

[-raise](https://imagemagick.org/command-line-options/#raise)」运算符是如此简单的图像变换,几乎算不上是变换。它所做的仅仅是给现有图像添加一个矩形的斜面高光。 |

  magick rose: -raise 5  rose_raise.gif

[IM 输出]
反转的凹陷效果可以使用该运算符的「加号」形式生成…… |

  magick rose: +raise 5  rose_sunken.gif

[IM 输出]
此运算符有点像给图像加框,但它并不是把额外的像素作为边框添加,而是由「[-raise](https://imagemagick.org/command-line-options/#raise)」运算符对图像的边缘像素重新着色。这使它成为一种图像变换。 | 实际上,图像加框正是通过先添加一个边框、再对其进行 raise 处理来实现的!
---|---
| 此运算符仅对矩形图像有效,对于具有透明背景的图像则会失败,因为颜色修改同样会变透明。基本上,它是个相当笨的运算符!
---|---

添加内侧边框

用户想要的不是在图像外侧放置边框,而是直接把边框叠加到图像自身的边缘上。为实现这一效果,解决办法是绘制一个与图像尺寸精确对齐的矩形。由于内置的「rose」图像尺寸为 70×46 像素,因此生成的叠加层与该尺寸完全一致。 |

  magick rose: -fill none -stroke navy -strokewidth 11  \
          -draw 'rectangle 0,0 69,45'   inside_border.jpg

[IM 输出]
添加的边框宽度由矩形的「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)」控制。即

{stroke width}  =  {border width} * 2 - 1

因此,上面的 6 像素边框需要将「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)」设为 11。如果你不知道图像的尺寸,那么可以先对图像执行Shave,再照常添加边框。这种做法大概更简单,不过或许没那么灵活。 |

  magick rose: -bordercolor green -shave 6x6 -border 6x6 inside_border2.jpg

[IM 输出]

随机像素扩散

[-spread](https://imagemagick.org/command-line-options/#spread)”会把每个像素替换为源图像中随机邻近像素的颜色。这一随机选择是依据像素插值虚拟像素设置的使用来进行的。例如…… |

  magick -size 80x40 xc:red xc:blue -append -spread 5 spread_interpolated.png

[IM 输出]
如果你检查这些像素图像,会看到有些像素呈现红色和蓝色的混合。也就是说,它们是被插值的,而不是简单地被扩散或交换。距离值越小,这种现象越明显。另外请注意,“[-spread](https://imagemagick.org/command-line-options/#spread)”也会利用虚拟像素设置。 |

  magick -size 80x80 xc: -virtual-pixel black -spread 10 spread_virtual.png

[IM 输出]
如你所见,你会得到一个随机化的边界,大多由纯黑的虚拟像素构成。不过也有少量灰色像素,是从图像真实像素与虚拟像素之间的边界插值而来的。要获得更传统的像素扩散效果,可以通过使用“[-interpolate](https://imagemagick.org/command-line-options/#interpolate) [Nearest](misc.html#nearest)”强制对特定像素进行颜色查找,从而防止这种混色。为避免虚拟像素带来的问题以及可能出现的“边缘色偏”,我建议你使用“[-virtual-pixel](https://imagemagick.org/command-line-options/#virtual-pixel) [Mirror](misc.html#mirror)”。这样便得到一种更传统的像素随机“扩散”…… |

  magick rose: -interpolate nearest -virtual-pixel mirror \
                -spread 5   spread_rose.png

[IM 输出]

建设中

上述做法的主要问题在于,你可能会丢失图像中的一些像素数据。也就是说,像素不是被“交换”,而是被随机复制的,这意味着图像中的某个特定像素可能被复制或丢失。从 IM v6.9.2-2 起,你可以使用“[+spread](https://imagemagick.org/command-line-options/#spread)”真正地交换图像内的像素,也就是说图像中的任何像素都不会被复制或丢失。原图像的每个像素都仍然存在,只是被移到了新的位置。不过由于像素的处理方式,像素可能会被“双重交换”。也就是说,某个特定像素可能被交换后,又被选中与后面的像素再次交换。这意味着某个特定像素的漂移距离可能超过 spread 参数所要求的。这种双重交换还意味着像素更容易向右下角进一步扩散。当然,这种移动是平衡的,会有大量像素向左上方发生较小的漂移。例如,这里我在扩散像素时把原图像作为参照前置。 |

  magick -size 40x40 xc:red xc:blue -append \
          \( +clone +spread 5 \) +append spread_bias.png

[IM 输出]
请注意有些红色像素向下扩散得更多,不过你也会看到少数蓝色像素向上扩散得比预期更多(尽管是朝向图像的左侧)。当你对 spread 使用较小的距离参数时,这个问题会更明显。要解决这个双重交换问题并不容易,我们正在寻找一种“有限区域打乱”算法来解决它。但在此期间,你至少可以通过夹着一次 Transverse(左上到右下的对角镜像)扭曲、执行两次 spread,来减轻方向性偏差。 |

  magick -size 40x40 xc:red xc:blue -append \
          \( +clone +spread 5 -transverse -spread 5 -transverse \) \
          +append spread_no_bias.png

[IM 输出]
当然,这确实会让扩散更明显、线性更弱,但至少不存在方向性偏差,也没有像素的复制/丢失。上述这一补充做法是从一场论坛讨论中发展出来的:t=28043 IM Forum Discussion rearrange vertical pixel row

晕影照片变形

一个特殊的运算符,可将图像变为带有柔和模糊轮廓的圆形。 |

  magick rose: -background black -vignette 0x5  rose_vignette.gif

[IM 输出]
使用零(或极小)的 sigma 可以去除模糊,生成椭圆或卵形的边框。但请注意,它实际上并不会使用尽可能大的椭圆,所以你可能想自己动手来做。 |

  magick rose: -background black -vignette 0x0  rose_vignette_0.gif

[IM 输出]
你可以将它与透明度(以及 PNG 格式)一起使用…… |

  magick rose: -alpha Set -background none -vignette 0x3  rose_vignette.png

[IM 输出]
另一种参数指定方法是给第二个 sigma 分量使用一个非常大的数,然后用第一个 radius 来定义模糊的扩展范围。这会为晕影模糊生成一种“线性”分布,而不是更常见的高斯分布。 |

  magick rose: -background black -vignette 5x65000  rose_vignette_linear.gif

[IM 输出]
另一种用于制作更矩形的晕影、为图像生成柔和边缘的技巧,在柔和边缘的缩略图中有演示。

复杂的宝丽来变形

得益于 Timothy Hunter(因 RMagick 而知名)的工作,一个“[-polaroid](https://imagemagick.org/command-line-options/#polaroid)”变形运算符被加入到 IM v6.3.2。

Polaroid® 是 Polaroid Corporation 的注册商标。

例如,这里我给一张照片缩略图赋予宝丽来风格的外观。这张图像是仰视巴黎凯旋门内部的螺旋楼梯(向下)拍摄的。这是一段非常长的楼梯! |

  magick spiral_stairs_sm.jpg -thumbnail 120x120 \
          -bordercolor white -background black  +polaroid  poloroid.png

[IM 输出]
| 请注意,生成的图像带有半透明的阴影,因此你要么必须使用 PNG 格式的图像,要么对于 GIF 或 JPG 格式用“[-flatten](https://imagemagick.org/command-line-options/#flatten)”把结果拼合到固定的背景色上。
---|---
这个运算符非常复杂,它会添加边框(依据“[-bordercolor](https://imagemagick.org/command-line-options/#bordercolor)”设置)、让纸张“卷曲”,并给阴影添加一个反向的卷曲。阴影颜色可以通过“[-background](https://imagemagick.org/command-line-options/#background)”颜色设置来控制。如你在上面所见,该运算符的加号形式会将结果随机旋转一定的量。这个运算符能让照片索引比原本更有趣、不那么呆板。该运算符的减号形式则让你控制图像的旋转角度。 |

  magick spiral_stairs_sm.jpg -thumbnail 120x120 \
          -bordercolor AliceBlue -background SteelBlue4 -polaroid 5 \
          poloroid_5.png

[IM 输出]
如果图像带有“[-caption](https://imagemagick.org/command-line-options/#caption)”元数据,该文本也会通过“[caption:](text.html#caption)”图像生成运算符被添加到宝丽来边框的下边缘。也就是说,它会按照照片的宽度进行断词换行。 |

  magick -caption '%c %f\n%wx%h' spiral_stairs_sm.jpg -thumbnail 120x120 \
          -bordercolor Lavender  -background gray40  +polaroid \
          poloroid_captioned.png

[IM 输出]
其他标准文本设置(依据“[caption:](text.html#caption)”)让你可以控制所添加标题的外观。 |

  magick spiral_stairs_sm.jpg -thumbnail 120x120 -font Candice -pointsize 18 \
          -bordercolor Snow -background black -fill dodgerblue -stroke navy \
          -gravity center  -set caption "Spiral Stairs\!"  -polaroid 10 \
          poloroid_controls.png

[IM 输出]
| _之所以使用图像元数据属性“[-caption](https://imagemagick.org/command-line-options/#caption)”,是因为内部使用了“[caption:](text.html#caption)”这一文本转图像生成器。

另一方面,IM 命令“[montage](montage.html#montage)”使用“[-label](https://imagemagick.org/command-line-options/#label)”,因为它用的是不做断词换行的“[label:](text.html#label)”文本转图像生成器。_
---|---
该变形为给照片图像添加一点“卷曲”而使用的 RotateWave 错切扭曲,往往会在生成图像的文本中产生水平方向的模糊线条。这是一个众所周知的图像扭曲问题(参见旋转细线),可以通过使用超采样技巧来解决。基本上,我们把宝丽来生成为实际想要尺寸的两倍大,然后再把图像调整为最终的正常尺寸。图像尺寸的缩小能有效地锐化生成的图像,更重要的是锐化标题文本。不过,要让这一方法奏效,我们不仅需要一张至少为最终尺寸两倍的图像,还可能需要给图像更大的边框,并以正常两倍的“[-density](https://imagemagick.org/command-line-options/#density)”来绘制文本。不要增大字体的“[-pointsize](https://imagemagick.org/command-line-options/#pointsize)”,因为那并不会以完全相同的方式放大文本。 |

  magick -caption 'Spiral Staircase, Arc de Triumph, Paris, April 2006' \
          spiral_stairs_sm.jpg  -thumbnail 240x240 \
          -bordercolor Lavender -border 5x5   -density 144  \
          -gravity center  -pointsize 8   -background black \
          -polaroid -15     -resize 50%     poloroid_modified.png

[IM 输出]
如你所见,尽管我们使用了小得多的字体 pointsize,标题文本依然非常锐利、清晰、易读。原图像中可能存在的其他细节也是如此。这样做唯一的缺点是,生成图像的阴影会更小、更不模糊。若想完全控制宝丽来变形,你可以自己完成其中涉及的所有步骤。最初的手法记录在 Tim Hunter 的页面 RMagick Polaroid Effect 上。步骤是:创建并附加标题、添加边框、用 wave 让照片卷曲、添加一个反向卷曲的阴影,最后旋转图像。要了解更多例子和其他自制方法,请参见宝丽来缩略图示例宝丽来照片的蒙太奇。你可能也会对 RubbleWeb IM Examples, Other 中的一些宝丽来例子感兴趣。

油画、色彩的团块

[-paint](https://imagemagick.org/command-line-options/#paint)」运算符用于将图片变成像是往画布上涂抹厚厚的颜料「团块」而画成的绘画。其结果是把相邻的颜色融合成更大的单色区域。

  magick rose: -paint 1   rose_paint_1.gif
  magick rose: -paint 3   rose_paint_3.gif
  magick rose: -paint 5   rose_paint_5.gif
  magick rose: -paint 10  rose_paint_10.gif
  magick rose: -blur 0x3 -paint 10  rose_blur_paint_10.gif

[IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出]

请注意,当颜料团块的半径较大时,团块开始呈现出方正的外观。正如上面最后一张图所示,事先将图像稍加模糊,可以在一定程度上让这一效果变得平滑。这是个有趣的效果,或许可以用来制作一些奇特而美妙的背景图像。例如,参见它在背景示例中的用法。最后再提一个警告。虽然「[-paint](https://imagemagick.org/command-line-options/#paint)」本应生成单一的纯色区域,但在半径值较大时,它有在某些区域产生垂直渐变的倾向。这非常令人恼火,可能是个 bug。有谁知道原因吗?「[-paint](https://imagemagick.org/command-line-options/#paint)」也有替代方案。其一是改用「-statistic Mode」,它为每个像素赋予给定矩形邻域内的「主导色」,能产生更好看的结果。 |

  magick rose: -statistic Mode 10 rose_paint_mode.gif

[IM 输出]
其二是使用某些形态学方法,更具体地说是面向彩色图像的 Intensity 变体。例如这里对 rose 执行一次「OpenIntensity」形态学运算。 |

  magick rose: -morphology OpenI Disk rose_paint_open.gif

[IM 输出]
这里我用一个略小的「Disk」来执行「CloseIntensity」。 |

  magick rose: -morphology CloseI Disk:2.5 rose_paint_close.gif

[IM 输出]
你不一定要用「disk」,也可以为它生成的团块设计自己的「画笔」形状的核。例如,试试用一个斜线画笔如何。

炭笔画,场景的艺术家速写

炭笔效果意在模拟艺术家对给定图像所作的炭笔速写。「[-charcoal](https://imagemagick.org/command-line-options/#charcoal)」运算符在某些方面类似于计算机视觉所用的边缘检测变换。它基本上是试图把图像中物体的主要边界和边缘变成铅笔与炭笔的阴影。那一个参数本应表示边缘线条的粗细。

  magick rose: -charcoal 1   rose_charcoal_1.gif
  magick rose: -charcoal 3   rose_charcoal_3.gif
  magick rose: -charcoal 5   rose_charcoal_5.gif

[IM 输出] [IM 输出] [IM 输出] [IM 输出]

关于在真实图像上使用炭笔变换的更好示例,请参见照片的炭笔速写 从技术上讲,「[-charcoal](https://imagemagick.org/command-line-options/#charcoal)」运算符就是对原图像的灰度转换施加了一些阈值处理的「[-edge](https://imagemagick.org/command-line-options/#edge)」运算符。

铅笔速写变换

[-sketch](https://imagemagick.org/command-line-options/#sketch)」运算符基本上是将一种线条笔触的图案应用到图像上,生成看起来像艺术性铅笔速写的效果。参数控制笔触的长度和角度。不过,它最好应用于较大且带有分明阴影的图像。关于这个运算符及其内部工作原理的完整示例,请参见铅笔速写

浮雕,营造金属压印

[-emboss](https://imagemagick.org/command-line-options/#emboss)」运算符试图生成一种像是用酸在金属板上蚀刻灰度图像的效果。它在许多方面与我们后面将要看到的「[-shade](https://imagemagick.org/command-line-options/#shade)」运算符非常相似,但没有那种 3D 感的边缘。它的参数是 radius/sigma,其中只有 sigma 重要。我发现这个参数不太有用,实际上可能有 bug。这个参数在最近的某个 IM 版本中也变了。我完全不知道发生了什么。如果你懂,请帮我搞明白。

  magick rose: -emboss 0x.5  rose_emboss_0x05.gif
  magick rose: -emboss 0x.9  rose_emboss_0x09.gif
  magick rose: -emboss 0x1   rose_emboss_0x10.gif
  magick rose: -emboss 0x1.1 rose_emboss_0x11.gif
  magick rose: -emboss 0x1.2 rose_emboss_0x12.gif
  magick rose: -emboss 0x2   rose_emboss_0x20.gif

[IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出]

这个运算符是灰度运算符,意思是它会分别应用到三个颜色通道上。因此它只应应用于灰度图像。正如你在上面看到的,彩色图像会产生一些奇怪的效果。

  magick rose: -colorspace Gray  -emboss 0x.5  rose_g_emboss_0x05.gif
  magick rose: -colorspace Gray  -emboss 0x.9  rose_g_emboss_0x09.gif
  magick rose: -colorspace Gray  -emboss 0x1   rose_g_emboss_0x10.gif
  magick rose: -colorspace Gray  -emboss 0x1.1 rose_g_emboss_0x11.gif
  magick rose: -colorspace Gray  -emboss 0x1.2 rose_g_emboss_0x12.gif
  magick rose: -colorspace Gray  -emboss 0x2   rose_g_emboss_0x20.gif

[IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出]

 如果有人确切知道 emboss 算法本应做什么,
请告诉我。

隐写术,在图像中隐藏一幅秘密图像

[-stegano](https://imagemagick.org/command-line-options/#stegano)」运算符其实更像是一个「好玩」的运算符。例如,间谍可以用它把信息藏在一幅随机图像的「混沌」之中。首先给个警告……

切勿在隐写术中使用 JPEG、GIF 或任何其他「有损」的图像编码

例如,我们来生成一条你想发给同伙间谍的隐秘消息(图像)…… | |

  magick -gravity center -size 50x40 label:"Watch\nthe\nPidgeon" message.gif
  magick identify message.gif

[IM 输出]
| [IM 文本]


请注意,我们还需要消息图像的尺寸(36x43 像素),因此上面用了 identify。接下来把它以某个偏移放入某幅图像中。所用的偏移(以及消息尺寸)就是这条隐藏消息的加密「密钥」。 |

  magick composite message.gif rose: -stegano +15+2  rose_message.png

[IM 输出]
现在你可以把那幅图像发给你的同志,他大概已经知道消息的尺寸和偏移。我们便能恢复藏在图像中的消息…… |

  magick -size 50x40+15+2 stegano:rose_message.png message_recovered.gif

[IM 输出]
容纳的图像越大,恢复出的图像就越好,因此把小图像藏进大图像中,要比上面所示的例子更好。为了让你看清隐藏的消息是如何分散到整幅容器图像中的,我们来把合成后的图像与原图作一次比较。 | |

  magick compare -metric PAE rose: rose_message.png   rose_difference.png

[IM 输出]
| [IM 文本]


这显示了消息图像是如何被加密并分散到整幅容器图像中以将其隐藏的。而且上面返回的「PAE」度量表明,最大差异仅为该图像所用 8 位色值中的一个色值。也就是说,极其微小。微小到只要对图像作一点小小的改动或修改,藏在其中的消息就会被破坏。差异如此之小,以至于你甚至不能用带有损压缩的 JPEG 作为图像格式,也不能用任何其他有损图像格式(包括 GIF)作为容器图像。此外,如果你用了错误的「偏移密钥」,也得不到消息…… |

  magick -size 50x40+14+2 stegano:rose_message.png message_bad.gif

[IM 输出]
你还可以使用区域设置来限制消息被隐藏在图像的哪个区域。尝试恢复消息时也需要同样的设置。这大概会让在一幅大图像中(尤其是限定在「繁忙」区域时)查找并解码隐藏消息的难度提高一个数量级。不过要警告的是,这并不是一种在密码学上很安全的技术。尤其是在原始源图像也能获取的情况下。对图像作频率分析,一般就能让攻击者知道存在隐藏消息。作为图像版权保护的方法,隐写术运算符同样毫无用处,因为对图像最微小的改动都会破坏消息,从而使其失效。作为间谍工具它也不怎么样,对一幅尺寸尚可的图像来说「组合」数量如此之少。任何大致知道你在做什么的人,大概都能很快破解它。还是坚持使用那些广为人知、久经考验的密码学方法为好。它唯一真正实用的用途,就是作为一个好玩的工具,或者作为向已有图像添加极少量噪声的一种方式。

加密图像数据

"[-encipher](https://imagemagick.org/command-line-options/#encipher)" 和 "[-decipher](https://imagemagick.org/command-line-options/#decipher)" 这两个运算符基本上会把图像数据加密成一团乱码。也就是说,在之后解密之前,图像内容本身完全无法辨认。例如,这可以用来保护公共服务上的敏感图像,使得只有掌握秘密口令的人日后才能查看它。不过首先要警告一句……

加密时不要使用 JPEG、GIF 或任何其他“有损”图像编码

例如,让我们加密上面创建的那张秘密消息图像,使用我保存在一个并不那么“秘密”的文件 "pass_phrase.txt" 中的口令。

  magick message.gif    -encipher pass_phrase.txt  \
          -depth 8 png24:message_hidden.png

[IM 输出] [IM 输出]

| _加密后的图像假定其以 8 位图像文件格式保存。因此,建议在最终保存到输出文件之前,通过设置 "[-depth](https://imagemagick.org/command-line-options/#depth) 8" 来强制施加这一限制。

上面还需要 "png24" 来确保输出不是调色板或颜色映射的 "png8:" 图像,那样同样无法正常工作。_
---|---
如你所见,得到的图像看起来完全是一堆乱码,丝毫看不出图像的真实内容。现在你可以把这张图像发布到网上,只有知道确切原始口令的人才能还原图像数据…… |

  magick message_hidden.png -decipher pass_phrase.txt message_restored.gif

[IM 输出]
但请注意,如果图像数据以某种方式损坏,你将无法还原它。这也包括 PNG 以灰度图像格式类型保存的情况。因此,只能使用无损图像格式,例如 PNG、MIFF、TIFF,甚至是 Pixel Enumeration Text。而使用有损图像格式,如 JPEG、PNG8 和 GIF,则会损坏图像数据,从而破坏加密结果。注意,任何可能描述图像的元数据仍会以明文形式存在。这意味着,你可以用图像自身的 'comment' 字符串作为口令来加密图像,或者用某个较短的密码加密该注释后再使用。这是个简单的想法,可以让口令更多变。加密图像只是其中一步。将结果再往前推进一点,就能生成一张不经过一些额外处理便无法简单解密的图像。例如,这里我使用了一些 Simple Non-Destructive Distorts 来迷惑任何试图以常规方式解密图像的人。

  echo "password" | magick message.gif -encipher - \
                      -transpose  -depth 8   png24:message_obfuscate.png
  echo "password" | magick message_obfuscate.png -transpose \
                      -decipher -  message_restored_2.png

[IM 输出] [IM 输出] [IM 输出]

如果你在上面的解密命令中没有加入 "[-transpose](https://imagemagick.org/command-line-options/#transpose)",图像就不会被正确解密。另请注意,由于使用的是流式密码(见下方专家提示),仅仅使用某种 "[-roll](https://imagemagick.org/command-line-options/#roll)" 并不能阻止图像被解密,至少部分内容仍会被解出。注意,上面我没有用文件来保存‘口令’,而是通过标准输入把口令喂给 "magick" 命令,这样你就可以用其他程序或命令从用户处获取口令、生成口令或采用其他方式,而不必使用以明文保存口令的文本文件。口令也可以从其他可自由下载的文件和图像生成。例如,你可以用某张知名的、可自由下载的参考图像的签名或注释字符串来解密你的图像。比如这里我用 "rose.gif" 图像的签名来加密并随后解密 "message.gif" 图像。

  magick identify -format %# rose.gif |\
    magick message.gif  -encipher - -depth 8    png24:message_signed.png
  magick identify -format %# rose.gif |\
    magick message_signed.png   -decipher -   message_restored_3.png

[IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出]

从 IM v6.4.8-0 起,"[-encipher](https://imagemagick.org/command-line-options/#encipher)" 和 "[-decipher](https://imagemagick.org/command-line-options/#decipher)" 所用的文件可以是二进制文件。因此,你甚至可以直接用一张图像本身作为口令。

  magick message.gif   -encipher rose.gif  -depth 8  png24:message_binary.png
  magick message_binary.png   -decipher rose.gif   message_restored_4.png

[IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出]

| 在 IM v6.4.8-0 之前,二进制文件会在遇到的第一个 'NULL' 字符处停止读取。如果使用 PNG 图像,这种情况会相当早地发生。
---|---
这项技术是精确的(除非部分数据在传输中被破坏)。因此,你可以用它来加密一张包含其他隐藏信息的图像,例如 Stegano Image。这意味着即使当局真的解密了图像,或强迫你透露密码,他们看到的也是一张真实的图像,但那张图像可能并不是最终隐藏的那张。 | "[-encipher](https://imagemagick.org/command-line-options/#encipher)" 和 "[-decipher](https://imagemagick.org/command-line-options/#decipher)" 运算符是在 IM v6.3.8-6 中加入的,但需要你在构建配置中包含 "--enable-cipher" 选项。不过到了 IM v6.4.6(究竟何时改的?)这个配置项就不再需要了,它成为了标准配置设置。因此你多半可以直接使用它。
---|---
| _该密码采用自同步流式密码实现,而流式密码又由分组密码构建而来。

这意味着即便你只下载了图像的一部分——因传输错误而受损——你仍然可以解密它,尽管图像的某些部分可能已被破坏。你也无需下载整张图像,就能解密并查看那些成功下载的部分。

但你确实需要口令才有任何成功解密图像的可能,因为这是一种非常非常强的加密。

_
---|---

图像像素化

将图像像素化,基本上是把图像变成一组大块的彩色“像素”,只显示原图的模糊轮廓。这两种技术都要先缩小图像(以生成更少的像素),然后再放大,从而使用缩放运算符采样运算符来生成色块,形成“像素块”。究竟使用什么颜色,取决于图像是如何被缩小的:是取单个像素样本,还是取合并后的平均色。

  magick rose: -sample 25%  -scale 70x46\!  rose_pixelate_sampled.gif
  magick rose: -scale  25%  -scale 70x46\!  rose_pixelate_scaled.gif
  magick rose: -resize 25%  -scale 70x46\!  rose_pixelate_resized.gif

[IM 输出] [IM 输出] [IM 输出] [IM 输出]

如你所见,“采样”得到的区域会有更加分明(锯齿状)的“像素”,而另外两种方法则使用合并或平均后的颜色,往往会产生更柔和、但对每个“像素”而言更准确的色彩表现。另请参见保护他人隐私,其中给出了一个只对图像中较小的遮罩区域(例如某人的脸部)应用此技术的示例。

像素网格

对图像进行网格化,与图像像素化非常相似。这种情况下,我们只想放大图像,以生成图像细节的、清晰的像素级视图。通常处理的是一张非常小的图像。最简单的方法与前一个示例相同,只需对一张小图像进行缩放,将其像素放大即可。

  magick rose: -crop 10x10+12+20 +resize  grid_input.png
  magick grid_input.png  -scale  1000%  grid_scale.png

[IM 输出] [IM 输出]

简单缩放的问题在于,在像素颜色相近的区域,你会难以看清单个“像素块”。我们需要在像素周围加上边框,以将它们分隔开。为此,我们需要叠加一张生成的平铺遮罩。关于在单条命令中使用生成的平铺图像的各种方法,请参见使用已在内存中的图像进行平铺。这里我们生成一张黑底白色的“网格”,并使用Screen 合成将其叠加(叠加白色,同时让黑色区域保持原样)。 |

  magick -size 10x10 xc: -draw 'rectangle 1,1 9,9' -write mpr:block +delete \
          grid_input.png -scale 1000% -size 101x101 tile:mpr:block \
          +swap -compose screen -composite grid_blocks.png

[IM 输出]
请注意,用于生成平铺块的尺寸为 _scale_ *_image_size_ +_gap_size_(本例中为 10*10+1 => 101)。我还交换了这两张图像,使最终图像的尺寸来自平铺图像,而非缩放后的图像——后者的尺寸小一个像素。不过,由于我把平铺图像用作了目标,这可能会丢失原图中原有的任何图像元数据。这里我生成了圆形的彩色“斑点”,但这次使用了Multiply 合成(叠加黑色,同时让白色区域保持原样)。 |

  magick -size 10x10 xc: -draw 'circle 5,5 1,3' -negate \
               -write mpr:spot +delete \
          grid_input.png -scale 1000% -size 101x101 tile:mpr:spot \
          +swap -compose multiply -composite grid_spots.png

[IM 输出]
你也可以让网格边框变为透明,方法是同样对平铺叠加层取反(黑色区域变为透明),并改用CopyOpacity 合成而不是Multiply。还可以加入其他颜色,但要做到这一点,你必须使用一张真正包含透明度的平铺图像。为此,你需要把这张黑白平铺图像转换为一张形状遮罩。例如,这里我使用基本形态学运算符在彩色叠加层中生成菱形的“孔洞”。为此,先绘制一个“种子”像素,再使用菱形形态学核将其扩展。 |

  magick -size 10x10 xc: -draw 'point 5,5' -morphology Erode:4 Diamond \
          -background Navy -alpha shape -write mpr:diamond +delete \
          grid_input.png -scale 1000% -splice 1x1+0+0 \
          -size 101x101 -background none tile:mpr:diamond \
          -alpha set -compose Over -composite grid_diamonds.png

[IM 输出]
| 请注意,“tile:”编码器会用当前背景色替换图像中的任何透明区域。如果你想保留平铺图像的透明度,请设置“-background none”或“-compose Src”。前者更简便。
---|---
请注意,从技术上讲,alpha 形状处理既可以在保存平铺图像之前进行,也可以在平铺该平铺图像之后、叠加它之前进行。选择权在你。最后一种技术是使用一个糟糕的重采样滤镜来制造重采样失败,从而为每个像素生成带锯齿的圆形。这算不上什么好技术(是在滥用图像处理的失败),但它确实构成了一张像素网格。

间隔铺开的瓦片

一个类似的问题是把图像中的瓦片网格间隔铺开。这不是简单地把单个像素放大成「像素块」,而是在图像的矩形区域之间插入间距。也就是说,按固定间隔向图像拼接额外的像素。目前最好的解决方法是把图像拆分成行与列,在把这些瓦片追加拼回去之前,先给每块瓦片拼接上额外的间距。例如……

  magick rose: -background SkyBlue \
          -crop 10x0 +repage -splice 3x0 +append \
          -crop 0x10 +repage -splice 0x3 -append \
          grid_tile.png

[IM 输出] [IM 输出]

这是另一种方法,它同样先把原图分割成瓦片,但接着用一些 DIY FX 表达式,根据每块瓦片的旧位置计算出它的新位置。 |

  magick rose: -crop 10x10 \
          -set page '+%[fx:page.x+3*page.x/10]+%[fx:page.y+3*page.y/10]' \
          -background skyblue -layers merge +repage  grid_tile_fx.png

[IM 输出]
上面的数字「3」是要添加的间隙宽度,「10」是瓦片尺寸。之后你只需要给结果加上边框或其他边缘即可。再多花一点功夫,你甚至可以给上面网格中每块瓦片的位置加入一些随机的「抖动」,得到不那么规整的效果。这两种方法的问题都在于,生成大量小图像只为再把它们拼接回去,确实会带来很多工作量。尤其是瓦片尺寸非常小的时候。有人提出了一种更好的方法,即对 Splice 运算符做一个特殊扩展,见 IM 论坛讨论 Splice (adding tile gridding gaps)


计算机视觉变换

边缘检测

[-edge](https://imagemagick.org/command-line-options/#edge)」运算符会突出显示图像中存在颜色梯度的区域。它是一个灰度运算符,因此会分别应用到三个颜色通道上。

  magick mask.gif -edge 1   mask_edge_1.gif
  magick mask.gif -edge 2   mask_edge_2.gif
  magick mask.gif -edge 3   mask_edge_3.gif
  magick mask.gif -edge 10  mask_edge_10.gif

[IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出]

如你所见,边缘只被添加到颜色梯度超过 50% 白色的区域!我不知道这是 bug 还是有意为之,但这意味着上面的边缘几乎完全位于原始 mask 图像的白色部分。在利用「[-edge](https://imagemagick.org/command-line-options/#edge)」运算符的结果时,这一点可能极其重要。例如,如果你对一幅包含黑色轮廓的图像做边缘检测,「[-edge](https://imagemagick.org/command-line-options/#edge)」运算符会把黑色线条「成双化」,产生一个奇怪的结果。

  magick piglet.gif  -colorspace Gray  -edge 1 -negate  piglet_edge.gif

[IM 输出] [IM 输出]

不过,通过在做边缘检测之前先对图像取反,成双的线条会向内靠拢并合并到一起,从而消除「双线」效果。 |

  magick piglet.gif -colorspace Gray \
                 -negate -edge 1 -negate    piglet_edge_neg.gif

[IM 输出]
我发现这些边缘往往太过锐利,使得结果图像的边缘不够平滑。因此我发现给结果加上极其轻微的模糊,能让外观改善不少。 |

  magick piglet_edge_neg.gif  -blur 0x.5  piglet_edge_blur.gif

[IM 输出]
这里我把边缘检测应用到一幅彩色图像上,以及它的灰度版本,来向你展示它对照片类图像的效果。

  magick rose:                  -edge 1  rose_edge.gif
  magick rose: -colorspace Gray  -edge 1  rose_edge_grey.gif

[IM 输出] [IM 输出]

如你所见,若不把图像转换为灰度,各颜色通道的边缘会彼此完全独立地生成。

Canny 边缘检测器

从 IM v6.8.9-0 起,IM 现在支持 Canny 边缘检测器。(见 IM 论坛上的公告示例)。这是一种非常先进的边缘检测算法,能在所有锐利边缘处生成非常强的(二值的)单像素宽线条,且几乎不受噪声干扰。例如,这里我们把它应用到上面用过的测试图像上……

  magick mask.gif           -canny 0x1+10%+30%  mask_canny.gif
  magick piglet.gif         -canny 0x1+10%+30%  piglet_canny.gif
  magick piglet.gif -negate -canny 0x1+10%+30%  piglet_canny_neg.gif
  magick rose:             -canny 0x1+10%+30%  rose_canny.gif

[IM 输出] [IM 输出]
[IM 输出] [IM 输出] [IM 输出]

如你所见,它产生的结果比上面的 Edge 运算符锐利得多。模糊的抗锯齿边缘对结果几乎没有影响,产生的是细细的位图线条。此外,正如 piglet 图像所示,它不会像前面的边缘运算符那样被放在某一特定的一侧。因此,对输入图像取反没有任何效果。但和所有边缘检测器一样,它在处理背景「杂乱」的真实世界图像时可能会出问题,比如内置的 rose 图像。这种干净的结果在后面的 Hough 直线检测中非常重要。

从抗锯齿形状得到边缘轮廓

普通边缘检测方法最大的问题是结果高度锯齿化。也就是说,无论形状是平滑的(抗锯齿)还是锯齿的,它都会产生非常阶梯状的像素效果。例如,这里是一个平滑抗锯齿的对话气泡(「WebDings」字体字符「(」)。 |

  magick -size 80x80 -gravity center -font WebDings label:')' voice.gif

[IM 输出]
这是它经过边缘检测后的图像…… |

  magick voice.gif -edge 1 -negate   voice_edge.gif

[IM 输出]
如你所见,它看起来很糟糕,边缘外侧有少量抗锯齿,而线条内侧则完全是锯齿(阶梯)状的外观。对图像取反会在图像外侧生成类似的轮廓,但同样在线条外侧有强烈的锯齿。 |

  magick voice.gif -negate -edge 1 -negate   voice_edge_negate.gif

[IM 输出]
当你已经拥有一幅带抗锯齿边缘的图像时,另一种做法是生成原始形状与其「抖动」副本之间的差异图像。例如,这里我们求出原图与向右偏移(或抖动)1 个像素的图像之间的差异图像。 |

  magick voice.gif \( +clone -roll +1+0 \) -compose difference -composite \
          -negate   voice_jitter_horiz.gif

[IM 输出]
注意,这样做不能为水平方向的斜边生成良好的边缘。不过,通过同时结合水平和垂直方向的抖动差异图像,我们可以得到形状非常好的抗锯齿轮廓。 |

  magick voice.gif \
          \( -clone 0 -roll +1+0 -clone 0 -compose difference -composite \) \
          \( -clone 0 -roll +0+1 -clone 0 -compose difference -composite \) \
          -delete 0  -compose screen -composite -negate  voice_jitter_edge.gif

[IM 输出]
这种技法还有一个优点,就是无论遮罩是否被取反都同样有效。但要注意,结果相对于原图有 1/2 像素的偏移,因此如果你需要把原始形状和轮廓两者结合起来得到想要的结果,可能还需要做一些进一步的「变形」处理来重新对齐其中之一。

从位图形状生成边缘轮廓

位图图像要难处理得多,因为它们没有任何可用于生成平滑轮廓的抗锯齿像素。例如,这里有一个花哨的“心形”,它是从“WebDings”字体(字符 'Y')中提取出来的。不过我特意把它生成为一张锯齿化的位图,以模拟从网络下载的糟糕位图图像。比如一张含透明区域的 GIF 图像的轮廓。 |

  magick +antialias -size 80x80 -gravity center \
          -font WebDings label:Y   heart.gif

[IM 输出]
于是我们有了这张糟糕的图像,但我们想要找到的是图像的轮廓,而不是它的形状。直接使用边缘检测只会在位图形状外侧生成一条纯位图边缘。 |

  magick heart.gif -edge 1 -negate   heart_edge.gif

[IM 输出]
经过反相的边缘会生成一幅边缘图像,但它位于黑色区域的内侧。 |

  magick heart.gif -negate -edge 1 -negate   heart_edge_negate.gif

[IM 输出]
把上面两者相加,你就会得到一条以位图形状边缘为中心、宽 2 像素的边缘。 |

  magick heart.gif \( +clone -negate \) -edge 1 \
          -compose add -composite  -negate  heart_edge_double.gif

[IM 输出]
如你所见,尽管原始图像本身在这方面并不算太糟,得到的结果图像却高度锯齿化,轮廓中出现了“阶梯”状的效果。这并不是一个好的解决方案。使用 '[EdgeIn](morphology.html#edge-in)' 形态学方法或其他类似方法,可以生成稍好一些的边缘。 |

  magick heart.gif  -negate -morphology EdgeIn Diamond -negate heart_edgein.gif

[IM 输出]
只要以恰当的方式用缩放来模糊边缘,再用 Solarize 提取出构成边缘的中灰像素,也能达到类似的效果。加上一项 "-filter Cubic" 设置,或使用其他重采样滤镜,则可以生成更粗的边缘。 |

  magick heart.gif -resize 400% -resize 25% \
          -solarize 50% -evaluate multiply 2 -negate heart_resize.gif

[IM 输出]
或者,若想要更可控的柔和效果,你可以直接模糊形状,再以类似的方式提取边缘。我发现 '0.7' 的模糊值效果最好,并限定为 3 像素以加快处理速度。 |

  magick heart.gif -blur 3x.7 -solarize 50% -level 50%,0 heart_blur.gif

[IM 输出]
注意这里用到了色阶运算符,它等价于前一个例子中使用的 "-evaluate multiply 2 -negate"。有了抗锯齿的边框后,如果你只是想让原始形状变得平滑、而不是取它的轮廓,现在就可以把原始形状重新加回去。只是要记住,轮廓正好位于原始图像边缘之上,因此在尺寸上会比前面几个例子大半个像素。你是否知道其他从形状(无论抗锯齿还是位图)生成抗锯齿轮廓的方法?如果有,请把它寄给我,或发到 IM 论坛。我会署上你的名字。

使用栅格转矢量转换器描边

最理想的方案之一,是借助一款非 IM 的 '栅格转矢量 ' 转换程序,用 magick 把这个位图形状变成矢量轮廓。能完成此任务的程序包括:"ScanFont"、"CorelTrace"、Adobe 的 "Streamline",以及 "[Vector Magic](http://vectormagic.com/home)"。不过其中多数至少需要花些钱。"VectorMagick" 和另一款描摹程序 "AutoTracer" 提供了可免费使用的在线图像转换器。其他免费方案有 "[AutoTrace](http://autotrace.sourceforge.net/)" 或 "[PoTrace](http://potrace.sourceforge.net/)"。欢迎提供更多建议。这些描摹程序使用简单,但通常需要某种形式的图像前后处理。它们支持的输入格式有限,并输出一幅矢量图像,从而生成输入图像的 '平滑' 形态。我更偏爱 "AutoTrace",因为它不会缩放生成的 SVG 数据,从而得到标准的线条粗细,不过你无法在 '管道' 中使用它。为获得最佳效果,最好确保只向它输入一幅基本的位图图像,为此我们可以对输入图像做阈值处理,同时将其转换为 autotrace 能识别的图像格式。之后我就能用 magick 把该图像变成一幅 SVG 矢量图像。

  magick heart.gif -colorspace gray -threshold 50% heart_tmp.pbm
  autotrace -output-format svg -output-file heart.svg heart_tmp.pbm
  magick heart.svg heart_svg.gif
  rm -f heart_tmp.pbm

[IM 输出] | | | [IM 文本]

| [IM 输出]

自 IM v6.4.2-6 起,你可以直接用 "autotrace:" 图像输入委托来完成上述步骤。这只需要安装 "autotrace" 命令即可。例如 |

  magick autotrace:heart.gif  heart_traced.gif

[IM 输出]
如果你的 IM 编译时带有 "[AutoTrace](http://autotrace.sourceforge.net/)" 委托库,也可以让 IM 直接从内存中的图像生成 SVG 图像。相关细节参见 SVG 输出处理。例如……

  magick heart.gif  heart_2.svg

[IM 文本]

当然,此时的 SVG 输出代表的是原始图像的平滑版本,这在本例中并不是我们真正想要的。但既然我们现在已经拥有了 矢量形式 的位图形状,就可以简单地调整 SVG 的 'style' 属性,让轮廓被 'stroke'(描边)而非把形状 'fill'(填充)。随后可以把修改后的 SVG 再次送回 ImageMagick,重新生成干净的轮廓栅格图像。例如…… |

  cat heart.svg |
    sed 's/"fill:#000000[^"]*"/"fill:none; stroke:black;"/' |
      magick svg:- heart_outline.gif

[IM 输出]
确实有点繁琐,但平滑抗锯齿的效果完全值得这番功夫。如果能把描边或其他改动直接作为选项传给 "autotrace" 命令本身就好了,但目前还没有这个功能。你还可以进一步修改 SVG 输出,加粗边缘,或指定其他描边或背景颜色,改变矢量边缘形状的填充颜色。例如,这里我们为形状生成一条更粗的轮廓并填充红色,全部都做了漂亮的抗锯齿处理。 |

  cat heart.svg |
    sed 's/"fill:#000000;[^"]*"/"fill:red; stroke:black; stroke-width:5;"/' |
        magick svg:- heart_outline_thick.gif

[IM 输出]
如此完美的心形,用任何其他方式都无法从位图形状生成。我们也可以只提取 'd="..."' 路径元素,直接作为 Draw 命令中的 SVG 路径字符串使用。这样一来,你就能对该矢量轮廓套用任意其他 IM 绘制设置,从而完全掌控最终结果。关于使用 "[AutoTrace](http://autotrace.sourceforge.net/)" 程序的另一个例子,参见 使用 Autotrace 生成骨架

霍夫直线检测器

霍夫直线检测器("[-hough-lines](https://imagemagick.org/command-line-options/#hough-lines)" 于 IM v6.8.9-1 加入)是一种非常复杂的变换,包含许多阶段(详情参见 Wikipedia, Hough Transform)。基本上,它设计用于检查一幅图像,在黑色背景上寻找白色的线,并尝试返回图像中存在的任何线段(像素的线性序列)的精确位置。这对于诸如去除图像旋转,或确定图像中的透视变换等操作非常重要,从而可以重复施加或将其移除。以下是该运算符的完整选项集

  -background {_background_} -stroke {_line_color_} -hough-lines {_W_}x{_H_}+{_threshold_}

颜色(backgroundline_color)用于设置结果图像中线条的颜色(如果你确实要绘制它们)。霍夫运算符的参数(W}x{H}+{threshold)用于定义在中间的“搜索图像”中寻找“峰值”所用滤镜的大小和阈值。也就是说,它控制着运算符实际“找到”我们试图检测的线的效果如何(见下文)。你可以调整这些参数以帮助定位线条。例如,让我们试着找出一幅矩形图像中的线。首先需要把图像简化为线,为获得干净的结果,推荐使用 Canny 边缘检测器

  magick shape_rectangle.gif -canny 0x1+10%+30% rectangle.gif

[IM 输出] [IM 输出]

现在让我们对这幅图像应用霍夫直线检测器。 |

  magick rectangle.gif -background black -stroke red \
          -hough-lines 5x5+20   rectangle_lines.gif

[IM 输出]
如你所见,找到了 5 条线,其中 2 条彼此非常接近。多出一条线的原因是图像中的矩形并不完美。虽然我在此显示的是栅格(GIF)图像结果,但霍夫运算符实际上生成的是 Magick Vector Graphics Format 的矢量图像。这意味着你可以列出线条信息以作进一步处理。

  magick rectangle.gif -background black -stroke red \
          -hough-lines 5x5+20   rectangle_lines.mvg

[IM 文本]

注意,这些线是从图像的一条边缘(带浮点值)绘制到另一条边缘的。由此你可以看出,第二条和第三条线就是那两条彼此接近的线。MVG 输出中的注释给出了该线在图像中"命中"的累计像素数,因此能很好地表明该线在图像中的强度。这个值总会大于你给 "[-hough-line](https://imagemagick.org/command-line-options/#hough-line)" 运算符的阈值。由此你可以看出第一条和最后一条线(它们是接近的匹配)在强度上大致相等,因此很难在两者之间做取舍。若你遇到这种情况,我建议你尝试改进边缘检测步骤。注意 MVG 图像并未定义任何颜色。本例中的颜色设置其实并未被使用。只有当你在把上述结果转换为"栅格"图像时确实"绘制"这些矢量时(正如我们之前所做的那样),颜色才会被用到。你还可以通过一个特殊的 define 查看中间的"搜索图像",即在每个方向上寻找白色像素的"累加器"。 |

  magick rectangle.gif \
          -define hough-lines:accumulator=true -hough-lines 5x5+20 \
          -delete 0 -contrast-stretch 0.1% rectangle_accumulator.gif

[IM 输出]
该 define 只是把"搜索图像"附加到图像结果之后,在本例中我们将其删除。我们还对"累加值"施加了一些对比度以使其更清晰可见。这就是霍夫检测器的参数所搜索的图像。该图像宽度始终为 180 像素(每 1 像素对应线角度的 1 度),而高度是图像对角线长度的两倍。因此,峰值的位置直接定义了线的角度,以及该线相对于输入图像中心点的垂直距离。也就是说,X 坐标是以度为单位的角度,Y 坐标是从中心算起的距离,范围从 -对角线距离到 +对角线距离。若你仔细观察右下角的峰值,就能明白我们为何得到两条线而非一条。这里的峰值是"成对的",两者之间有一道小缝隙。该算法基于 Fred Wienhaus 的脚本 "[houghlines](http://www.fmwconcepts.com/imagemagick/houghlines/)",不过采用了不同的"垂直距离"累加处理方式。


局部自适应阈值处理

建设中

[-lat](https://imagemagick.org/command-line-options/#lat)”运算符会尝试根据周围窗口内各像素的值,对每个像素进行自适应阈值处理。它常用于对背景不均匀(即光照不均)的图像做阈值处理。其依据的假设是:小窗口内的像素大致具有相同的背景色,也大致具有相同的前景色。

例如。
   magick input.png -lat 17 output.png

上例中使用了一个 17 像素见方的“窗口”来确定图像在每一点处的平均颜色;若某像素比该平均值暗,则将其置为黑色,若比平均值亮,则置为白色。较小的窗口尺寸会让阈值对光照的细微变化更敏感,计算更快,但也更容易受图像噪点的不利影响。

示例

较大的窗口尺寸会让阈值对光照的细微变化不那么敏感,计算更慢,但受图像噪点的影响也更小。其效果是让阈值的选取对像素值的细微变化或多或少地更敏感或更迟钝。

示例


窗口不必是正方形。  例如……
  magick input.png -lat 15x25 output.png

你还可以提供一个偏移量,它会被加到计算出的平均颜色上,使每个像素的局部阈值变亮或变暗。例如可借此减弱噪点的影响,或减弱像素值细微变化的影响。

  magick input.png -lat 15x25+2%

这些细微变化通常在使用扫描仪或数码相机采集图像时产生。使用正的偏移量可让自适应阈值处理对像素值的细微变化不那么敏感;使用负的阈值则可让自适应阈值对像素值的细微变化更敏感。或者,也可以在用 “-lat” 处理之前先降低图像中的噪点。

总之,每个像素都按以下逻辑进行阈值处理:
  AVG = 窗口内每个像素的平均值
  IF(输入像素 > AVG + OFFSET)
     输出像素为 BLACK
  else
     输出像素为 WHITE

---

另一种替代方法是,使用(取模)减法从原图中减去一份模糊副本,
然后再进行阈值处理。

   magick rose: -colorspace gray -lat 10x10+0% x:

大致等价于……

   magick rose: -colorspace gray \( +clone -blur 10x65535 \) \
           -compose subtract -composite -threshold 50%  x:

这里特殊的 "-blur 10x65535" 是一种线性平均模糊,它将自身限制在一个
10x10 的窗口内。

“Subtract”合成是一种数学上的取模运算,它会把
变为负数的值回绕到大于 50% 的值。

如果你想加入偏移量,可以再减去一张纯色
背景图像,方法是使用 -flatten……  例如

   magick rose: -colorspace gray -lat 10x10+10% x:

大致等价于……

   magick rose: -colorspace gray \( +clone -blur 10x65535 \) \
           -compose subtract -background gray10 -flatten -threshold 50%  x:

以上内容改编自 D Hobson dhobson@yahoo.com 提供的初始笔记


   -adaptive-sharpen
        仅在图像的边缘附近进行锐化

   -segment cluster-threshold x smoothing-threshold
         对色彩空间(而非图像中的对象)进行分割
         这可能会产生非常冗长的输出。
         如果想深入了解,它应用的是“模糊 c 均值算法”。

与此相关的还有 -despeckle,用于去除单个异色像素。

用两张图像(左右眼各一张)生成 3D 立体图
这也称为互补色立体图(anaglyph)
  magick composite left.jpg right.jpg -stereo anaglyph.jpg

Shade 3D 高光

Shade 用法

[-shade](https://imagemagick.org/command-line-options/#shade)”运算符我一直认为是 ImageMagick 提供的最有趣的运算符之一。该运算符的文档只是粗略地提示了它的能力。我花了大量的个人研究才弄懂这个运算符,甚至才搞清楚如何才能最好地运用它给 IM 用户带来的强大功能。基本上,这个运算符所做的就是假定给定的图像是一种称为“高度场”的东西。也就是说,一幅灰度图像代表某个物体或地形的表面。颜色“white”代表图像中的最高点,而“black”代表最低点。这种表示法源自 1980 年的计算机视觉研究,其中使用了带有强烈“相机灯光”的照片,使近处的点变亮,远处的点变暗。 由于“[-shade](https://imagemagick.org/command-line-options/#shade)”需要一幅灰度图像,该运算符会自动移除输入图像中的所有颜色。同样,图像中可能存在的任何透明度也完全无用,会被该运算符忽略。
现在“[-shade](https://imagemagick.org/command-line-options/#shade)”接受这个灰度高度场,并把一束光照射到它上面。其结果就是由此产生的光影的表现。请记住,你必须把输入图像想象成一个“表面”,输出才有意义。为了进行演示,我们需要一幅“高度场”图像,那么就让我们画一幅吧。
  magick -font Candice -pointsize 64 -background black -fill white \
          label:A  -trim +repage -bordercolor black -border 10x5 \
          shade_a_mask.gif

[IM 输出]
这幅图像同时也相当于某个形状的“遮罩”,它常常不仅用作“[-shade](https://imagemagick.org/command-line-options/#shade)”的输入,还用于遮罩图像,以便从着色结果中裁出相同的形状。请参见下文的遮罩着色图像。对于“[-shade](https://imagemagick.org/command-line-options/#shade)”运算符来说,这幅图像看起来会像一片平坦的黑色平原,上面有一块平坦的白色高台垂直向上隆起。因此只有这幅图像的边缘才会产生有趣的效果。为达到这一效果,两个参数定义了光线照射的方向。第一个参数是光线所来自的_方向_。因此“0”度角来自东方(即左侧),“90”是从北方(即顶部)逆时针方向,依此类推。例如……

  magick shade_a_mask.gif   -shade    0x45   shade_direction_0.gif
  magick shade_a_mask.gif   -shade   45x45   shade_direction_45.gif
  magick shade_a_mask.gif   -shade   90x45   shade_direction_90.gif
  magick shade_a_mask.gif   -shade  135x45   shade_direction_135.gif
  magick shade_a_mask.gif   -shade  180x45   shade_direction_180.gif

你明白其中的意思了。光线可以来自任何方向。另一个参数是_仰角_,代表光源与地面所成的角度。你可以把它想象成白天太阳的高度,因此“0”是黎明,“90”是正当头顶。

[diagram]

  magick shade_a_mask.gif   -shade  90x0    shade_elevation_0.gif
  magick shade_a_mask.gif   -shade  90x15   shade_elevation_15.gif
  magick shade_a_mask.gif   -shade  90x30   shade_elevation_30.gif
  magick shade_a_mask.gif   -shade  90x45   shade_elevation_45.gif
  magick shade_a_mask.gif   -shade  90x60   shade_elevation_60.gif
  magick shade_a_mask.gif   -shade  90x75   shade_elevation_75.gif
  magick shade_a_mask.gif   -shade  90x90   shade_elevation_90.gif

如你所见,当_仰角_为“0”时,形状只在光线所来自的那一侧被高光照亮。其余一切都是黑色的,因为没有光照射到任何其他表面上。我称之为“黎明高光”,它有其独特的用途。这就引出了第一个值得注意的地方。经过“[-shade](https://imagemagick.org/command-line-options/#shade)”处理的图像往往会有比高光区域更多的暗部或阴影区域。着色并不均匀。随着光线在“高度场”图像上方升得越来越高,图像的整体亮度会变得越来越白,直到在“正午”或_仰角_为“90”时,任何平坦区域都会呈现出耀眼的白色,只有坡面和边缘才会被着色为灰色,其中中灰是最大值,即“悬崖般”的坡度变化。这幅“正午”图像是另一个特殊情形,它有点像一个边缘检测系统,不过对于锐利的边缘,它的宽度在 2 到 4 像素之间。我过去曾用这幅图像为“[-shade](https://imagemagick.org/command-line-options/#shade)”图像的斜切边缘生成遮罩,以便使平坦区域变得透明。如果_仰角_超过“90”度,你将得到与光线从另一方向照射时相同的结果。因此参数“0x135”会产生与“180x45”完全相同的结果。负的_仰角_也会产生相同的结果,就好像光线从下方向上照射到一个“半透明”般的表面上一样。因此“0x-45”将与“0x45”相同。换句话说,对于某个特定的着色效果,通常还有另外 4 个参数也会产生相同的结果。根据以上所述,我会认为参数“120x45”大约是直接使用着色输出的最佳选择。例如,这里用它创建了一些斜切文字……

  magick -size 320x100 xc:black \
          -font Candice -pointsize 72 -fill white \
          -draw "text 25,65 'Anthony'" \
          -shade 120x45  shade_anthony.jpg

[IM 输出]

[-shade](https://imagemagick.org/command-line-options/#shade)”的一个主要问题是实际产生的斜切边缘的厚度。像我上面用的那种锐利边缘,总会产生约 4 像素宽的斜切,向遮罩区域内外两侧各延伸。除了在使用“[-shade](https://imagemagick.org/command-line-options/#shade)”运算符前后对图像进行缩放之外,没有办法调整这个厚度。如果你想弄清楚在某个特定_仰角_照明角度下“平坦区域”究竟会有多亮,那么你可以使用下面的命令,对一片平坦的纯色表面进行着色。

  magick -size 50x50 xc:white -draw 'circle 25,25 20,10' \
          -blur 0x2  -shade 0x45   -gravity center -crop 1x1+0+0 txt:-

[IM 文本]

如你所见,_仰角_为“45”度会产生相当明亮的平坦色调,约为 70% 灰度,这对于一般观看来说是个合理的灰度级别。然而,如果你打算用着色为各种形状生成 3D 高光,那么实际的灰度级别就变得非常重要。这一点我们稍后会在创建叠加高光中探讨。基本上,关于“[-shade](https://imagemagick.org/command-line-options/#shade)”运算符的内容就是这些了。不过,要有效地使用它还涉及一整套技巧与可能性,我们接下来就来看看。

遮罩着色形状

如上所述,简单的‘遮罩’形状常与 "[-shade](https://imagemagick.org/command-line-options/#shade)" 配合使用,从简单形状生成复杂的三维效果。例如,让我们对一幅直接着色的遮罩图像来这样做。

  magick shade_direction_135.gif  shade_a_mask.gif \
          -alpha Off -compose CopyOpacity -composite   shade_beveled.png

[IM 输出] [IM 输出] [IM 输出]

请注意,"[-shade](https://imagemagick.org/command-line-options/#shade)" 运算符生成的斜面大约有一半实际上落在遮罩区域之外。换句话说,笔直的斜面在遮罩后被削去了一半。另一方面,垂直的即‘正午’着色图像(使用 '90' 度_仰角_)可用于只提取斜面边缘,让图像中心保持空心。

  magick shade_direction_135.gif \
          \( shade_elevation_90.gif -normalize -negate \) \
          -alpha Off -compose CopyOpacity -composite   shade_beveled_edge.png

[IM 输出] [IM 输出] [IM 输出]

不过要注意,‘正午’着色图像虽然提供了一种遮罩 "[-shade](https://imagemagick.org/command-line-options/#shade)" 运算符效果位置(及强度)的方法,却并不能完全覆盖这些效果。通过将‘正午’着色图像与原始遮罩结合,你可以略微扩大该遮罩的尺寸,从而生成更好的遮罩斜面图像。 [IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出]

[IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出]
|

  magick shade_direction_135.gif \
          \( shade_elevation_90.gif -normalize -negate \
             shade_a_mask.gif -compose screen -composite \) \
          -alpha Off -compose CopyOpacity -composite   shade_beveled_plus.png

[IM 输出]
请记住,使用 IM v6,你可以在同一条命令中生成我之前生成的‘着色’图像。因此,上面的结果本可以完全从头生成。例如。 |

  magick -font Candice -pointsize 72 -background black -fill white \
          label:X  -trim +repage -bordercolor black -border 10x5 \
          \( -clone 0 -shade  135x45 \) \
          \( -clone 0 -shade  0x90  -normalize -negate \
             -clone 0 -compose screen -composite \) \
          -delete 0 -alpha Off -compose CopyOpacity -composite \
          shade_beveled_X.png

[IM 输出]

着色形状图像

Alpha 提取运算符不仅能将带形状图像的 alpha 通道提取为灰度遮罩,还有一个副作用:把形状保留在‘已关闭’的 alpha 通道中。由于它处于‘关闭’状态,许多图像处理运算符(包括 "[-shade](https://imagemagick.org/command-line-options/#shade)")都不会触及它,从而保留其细节。这意味着,对于带形状的图像,你可以先提取形状、完成处理,然后在所有图像处理结束之后,只需再次开启 Alpha即可恢复透明度!例如,这里我在透明背景上画一个‘心形’,对图像做一些模糊和着色,然后恢复图像原始的形状轮廓。 |

  magick -size 100x100 -gravity center -background None \
          -font WebDings label:Y \
          -alpha Extract -blur 0x6 -shade 120x21 -alpha On \
          -normalize +level 15%  -fill Red -tint 100%    shade_heart.png

[IM 输出]
我只能说,哇,太省时间了!比之前那条带着各种额外处理和图像克隆的命令简单多了。

圆化着色边缘

正如你在上一个例子中看到的,对图像形状遮罩进行模糊后,边缘‘峭壁’的‘斜坡’会被平滑掉,仿佛被岁月磨蚀一般。这为着色图像带来了漂亮的圆化效果。

  magick -size 50x50 xc:black -fill white -draw 'circle 25,25 20,10' \
          shade_circle_mask.gif
  magick shade_circle_mask.gif            -shade 120x45  shade_blur_0.gif
  magick shade_circle_mask.gif -blur 0x1  -shade 120x45  shade_blur_1.gif
  magick shade_circle_mask.gif -blur 0x2  -shade 120x45  shade_blur_2.gif
  magick shade_circle_mask.gif -blur 0x3  -shade 120x45  shade_blur_3.gif
  magick shade_circle_mask.gif -blur 0x4  -shade 120x45  shade_blur_4.gif
  magick shade_circle_mask.gif -blur 0x5  -shade 120x45  shade_blur_5.gif

如你所见,模糊不仅会圆化边缘,还会使光照效果变暗。你可以通过对结果进行归一化来最大化其对比度,从而使最亮点和最暗点分别回到纯白色和纯黑色。

  magick shade_blur_3.gif   -normalize  shade_blur_3n.gif

[IM 输出] [IM 输出]

这样做唯一的缺点是通常也会使着色图像变暗。这一点我们需要在创建叠加高光中加以考虑。让我们也通过直接遮罩来收尾这幅着色图像吧……

  magick shade_blur_3n.gif shade_circle_mask.gif \
          -alpha Off -compose CopyOpacity -composite   shade_blur_3n_mask.png

[IM 输出] [IM 输出]

如你所见,对遮罩图像进行模糊能非常漂亮地圆化所得形状的边缘。

创建叠加高光

"[-shade](https://imagemagick.org/command-line-options/#shade)" 运算符的输出很不错,但你其实很少会想要一幅形状的纯灰度图像。它需要一些颜色。然而这并不那么容易,因为两种主要的加色方式——为中间调着色以只给灰度重新上色,或用‘叠加’alpha 合成来用图像替换灰色区域——都依赖于一种特殊形式的灰度图像。也就是说,完美的中间调灰色('grey50')会被颜色或图像替换,而更白或更暗的灰色则会相应地使颜色或图像变白或变暗。这些在未修改区域为完美中间调灰色的特殊灰度‘叠加高光’图像,用 "[-shade](https://imagemagick.org/command-line-options/#shade)" 创建起来并不那么直接。不过以下是我发现的一些较简单的方法。在 "[-shade](https://imagemagick.org/command-line-options/#shade)" 中使用 30 度仰角光照,是为被着色形状的平坦区域生成完美中间调灰色的一种方法。例如,这里我对一幅图像着色,然后提取左上角像素,以检查图像中‘平坦’部分所得的颜色。 [IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出]

| |

  magick -size 50x50 xc:black -fill white -draw 'circle 25,25 20,10' \
          -blur 0x2  -shade 120x30           shade_30.png
  magick shade_30.png   -gravity center -crop 1x1+0+0 txt:-

[IM 输出]
| [IM 文本]


遗憾的是,改变上面命令中 "[-blur](https://imagemagick.org/command-line-options/#blur)" 的圆化效果往往也会改变着色图像所得的高光强度。也就是说,使用较大的模糊不仅会产生看起来圆润的边缘,还会使高光暗到几乎不可见。这意味着你需要为所生成的 "[-shade](https://imagemagick.org/command-line-options/#shade)" 图像输出添加更多对比度,才能让高光作为叠加图像发挥作用。要解决这个问题,我们需要一种把这种对比度效果从圆化调整中去除的方法。通常的做法是直接对图像做 "[-normalize](https://imagemagick.org/command-line-options/#normalize)",但对 30 度着色图像这样做,会导致‘平坦’区域不再是完美的灰色。例如…… | |

  magick -size 50x50 xc:black -fill white -draw 'circle 25,25 20,10' \
          -blur 0x2  -shade 120x30 -normalize   shade_30_norm.png
  magick shade_30_norm.png   -gravity center -crop 1x1+0+0 txt:-

[IM 输出]
| [IM 文本]


不过经过进一步试验后,我发现使用 21.78 度的着色仰角,在归一化之后,能产生所需的完美中间调灰度,同时还有很好的强高光效果。 | |

  magick -size 50x50 xc:black -fill white -draw 'circle 25,25 20,10' \
          -blur 0x2  -shade 120x21.78 -normalize   shade_21_norm.png
  magick shade_21_norm.png   -gravity center -crop 1x1+0+0 txt:-

[IM 输出]
| [IM 文本]


由于着色图像现在经过了 "[-normalize](https://imagemagick.org/command-line-options/#normalize)" 运算符处理,用于‘圆化边缘’的 "[-blur](https://imagemagick.org/command-line-options/#blur)" 值将不再影响结果的最终强度。这是好得多的方法。总之,对着色图像进行归一化会使中间调偏离完美灰色。现在我们可以完全独立于其他调整来调节高光的输出强度。通常由于归一化的结果很极端,我们需要一种受控的去归一化,或者说反对比度控制,来把高光降到所需的程度。调节所得高光最简单的方法,是用完美灰色对图像进行颜色着色。这会使图像中的所有色阶都朝中央的纯中间调灰色移动。例如……

  magick -size 50x50 xc:black -fill white -draw 'circle 25,25 20,10' \
          \( +clone -blur 0x2 -shade 120x21.78 -normalize  \) \
          +swap -alpha Off -compose CopyOpacity -composite  shade_tint_0.png
  magick shade_tint_0.png -fill grey50  -colorize 10%  shade_tint_10.png
  magick shade_tint_0.png -fill grey50  -colorize 30%  shade_tint_30.png
  magick shade_tint_0.png -fill grey50  -colorize 50%  shade_tint_50.png
  magick shade_tint_0.png -fill grey50  -colorize 80%  shade_tint_80.png

除了对高光进行线性着色之外,另一种做法是改用 Sigmoidal 非线性对比度来减弱其整体效果,同时保留高光中极亮/极暗的斑点。这能让高光效果看起来更“自然”,还能让高光更亮,仿佛表面更具反射性。不过,为了让这项技术更有效,我们需要确保阴影结果中不存在纯白和纯黑的颜色。这可以通过先使用 "[-contrast-stretch](https://imagemagick.org/command-line-options/#contrast-stretch)" 的 '0%' 而非 "[-normalize](https://imagemagick.org/command-line-options/#normalize)",并像上文那样对结果做少量去归一化来实现。这看似只是给高光叠加图像的生成增添了复杂度,但强调高光中的亮斑使得这些额外处理物有所值。例如……

  magick -size 50x50 xc:black -fill white -draw 'circle 25,25 20,10' \
          \( +clone -blur 0x2 -shade 120x21.78 -contrast-stretch 0% \) \
          +swap -alpha Off -compose CopyOpacity -composite shade_sig_0.png

  magick shade_sig_0.png  -sigmoidal-contrast 10x50%  shade_sig-10.png
  magick shade_sig_0.png  -sigmoidal-contrast  5x50%  shade_sig-5.png
  magick shade_sig_0.png  -sigmoidal-contrast  2x50%  shade_sig-2.png

  magick shade_sig_0.png  +sigmoidal-contrast  2x50%  shade_sig+2.png
  magick shade_sig_0.png  +sigmoidal-contrast  5x50%  shade_sig+5.png
  magick shade_sig_0.png  +sigmoidal-contrast 10x50%  shade_sig+10.png

如你所见,整体高光的强度降低了,但来自反射光的亮斑依然一如既往地明亮,只是尺寸变小了。其结果是让形状呈现出更加自然的“光泽”外观。这项技术唯一的缺点是同时也会生成一个阴影“斑点”,不过它通常不那么明显。最后,我们可以将“高光斑点”与整体高光减弱结合起来,生成一套高度可配置的高光叠加生成控制项…… [IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出]

[IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出] [IM 输出]
|

  magick -size 50x50 xc:black -fill white -draw 'circle 25,25 20,10' \
          \( +clone -blur 0x4 -shade 120x21.78 -contrast-stretch 0% \
             +sigmoidal-contrast 7x50% -fill grey50 -colorize 10%  \) \
          +swap -alpha Off -compose CopyOpacity -composite shade_overlay.png

[IM 输出]

总之,上面的示例包含四个独立的控制项……
"[blur](https://imagemagick.org/command-line-options/#blur)" :圆化形状边缘(0.001=斜切 2=平滑 10=圆润)
"[shade](https://imagemagick.org/command-line-options/#shade)" :光线照射的方向(120=左上 60=右上)
"[sigmoidal](https://imagemagick.org/command-line-options/#sigmoidal-contrast)" :控制表面反射的高光斑点(1=平坦 5=良好 10=反光 )
"[colorize](https://imagemagick.org/command-line-options/#colorize)" :高光的整体对比度( 0%=明亮 10%=良好 50%=暗淡 )

请注意,虽然上面的示例被整形为原始的“圆形”形状,但透明度应当只在应用了 'Overlay' 合成之后再恢复,而不是之前。此外,如果你打算在同一形状上反复使用某个高光(在完成任何旋转之后),可以为每个打算使用的形状预先各生成一次高光叠加图像,并保存结果以供多次重复使用。这种阴影叠加重用的一个例子,是在 IM 讨论论坛 中从平面源图像生成 3D DVD 封面。我也强烈建议你亲自试验上述技术,因为它们是让你的平面形状图像看起来更加逼真的关键。如果你想到了其他关于高光处理的点子,请告诉我。

未来计划:
   为叠加图像着色
   将叠加图像与某张图像进行 Alpha 合成

使用黎明阴影高光

在上文 遮罩阴影图像 中,我们展示了“正午”或“中午”阴影图像(使用 仰角 '90')对于遮罩、以及定位 “[-shade](https://imagemagick.org/command-line-options/#shade)” 所产生效果的位置和范围是何等有用。不过,某个形状的水平或“黎明”阴影图像(使用 仰角 '0')同样相当有用。例如,它可以用作白色或黑色图像的遮罩,为形状分别生成独立的高光与阴影效果。它还可用于确保一个形状获得大致相等的明暗区域(甚至也可以是不相等的量),因为我是分别生成它们的,而且方式完全可控。

FUTURE: 此处将补充更多细节

有关使用此技术的示例,请参见第一个 高级 3D 徽标


使用 FX,DIY 图像运算符 图像列表运算符 "[-fx](https://imagemagick.org/command-line-options/#fx)" 是一个通用的 DIY 运算符,它不归入任何特定类别的 IM 运算符,因为它几乎可以用来创建任何图像操作。本站各处都有它的使用示例,但这里我们将专门看看它的能力以及你可以如何运用它们。这个命令的能力如此通用,以至于它可以,

  • 创建画布、渐变、数学颜色映射表。
  • 在图像与通道之间移动颜色值。
  • 以几乎任何可以想象的方式调整图像颜色
  • 对图像进行平移、翻转、镜像、旋转、缩放、错切以及各种扭曲。
  • 将多个图像合并或合成在一起。
  • 以奇妙的方式平铺图像。
  • 对相邻像素进行卷积或合并。
  • 生成图像度量或'指纹'
  • 以不寻常的方式比较图像。

当然,这些技术中有许多已经是 IM 的一部分,能给出更快、更灵活的结果。但如果某项功能没有内置,"[-fx](https://imagemagick.org/command-line-options/#fx)" 便允许你自行生成所需操作的版本。事实上,我和其他人常常用它来为后来被纳入 IM 核心库的新操作制作原型。举例来说,请参见 DIY 新有序抖动替代方案,我在其中用 "[-fx](https://imagemagick.org/command-line-options/#fx)" 开发了 “[-ordered-dither](https://imagemagick.org/command-line-options/#ordered-dither)” 运算符的修订版本。这个运算符本质上允许你对一个或多个图像执行自由形式的数学运算。有关该命令的官方摘要,请参见 ImageMagick 网站 上的 FX,特效图像运算符

FX 基本用法

该命令接受一个图像序列,输入图像的数量随你需要,通常是一到两幅。它会用第一幅图像的副本替换所有输入图像,而这份副本已按“[-fx](https://imagemagick.org/command-line-options/#fx)”函数的运算结果加以修改。也就是说,第一幅图像中的任何元数据都会保留在“[-fx](https://imagemagick.org/command-line-options/#fx)”运算符的结果里。为便于数学运算,所提供的全部颜色值都被归一化到 0.0 至 1.0 的取值范围,运算结果也应落在此范围内。这也包括透明度或 alpha 通道,其值从 0.0(表示完全透明)到 1.0(表示完全不透明)。这些值代表“alpha 透明度”,实际上与 IM 在内部通常存储透明度的方式(即 matte 值)正好相反。不过这种形式在数学上更正确,也更易于使用。“[-channel](https://imagemagick.org/command-line-options/#channel)”设置定义第一幅(也称为“第零幅”或“u”)图像中的哪些通道会被“[-fx](https://imagemagick.org/command-line-options/#fx)”运算符的结果所替换。默认情况下,这仅限于原图的颜色通道(“RGB”)。除非更改“[-channel](https://imagemagick.org/command-line-options/#channel)”设置以纳入 alpha(“A”)通道,否则该图像中已有的透明度不会被修改。表达式会对每个像素执行一次,也会对正在处理的像素中的每个颜色通道各执行一次。此外,由于表达式每次执行时都会被重新解析,复杂的表达式在大图上处理可能需要一些时间。例如,这里我们定义一幅黑色图像,然后把蓝色通道设为半亮,从而形成“藏青色”。 |

  magick -size 64x64 xc:black -channel blue -fx '1/2' fx_navy.gif

[IM 输出]
接下来我们取一个黑到白的渐变,然后把蓝色和绿色通道设为零,于是它变成了黑到红的渐变。 |

  magick -size 64x64 gradient:black-white \
           -channel blue,green    -fx '0'    fx_red.gif

[IM 输出]
| _为了让“-channel”设置更接近“-fx”运算符,它现在接受字母“RGBA”的任意组合,用来指定运算符要限定作用的通道。

这意味着,要把“-fx”的输出限制到只有蓝色和绿色通道,你现在可以写“-channel BG”,而不必写更长的“-channel blue,green”。_
---|---
上面这些示例其实无需“[-fx](https://imagemagick.org/command-line-options/#fx)”也能生成,但能对一幅已有图像做这样的处理,正是它成为强大图像运算符的原因。实际上该函数可以读取并使用内存中当前图像序列里任意一幅图像的任意像素或特定颜色。第一幅“第零”图像被赋予特殊名称“u”,第二幅图像为“v”。内存中的其他图像可通过索引引用。因此“u[3]”是当前图像序列中的第四幅图像,而“u[-1]”是序列中的最后一幅。这与图像列表运算符所用的索引方式相同,所以你应当会觉得得心应手。若未给出其他限定符,所使用的颜色值就是所指定图像中的对应颜色。也就是说,除非你明确表示要使用红色,否则它会使用命令当时正在处理的那个颜色通道的颜色值。也就是说,处理蓝色通道时它会对蓝色值应用该表达式。除非另有指示,它会对图像中的每一个像素处理各个 RGB 颜色值(由默认的“[-channel](https://imagemagick.org/command-line-options/#channel)”设置决定)。也就是 3wh 次计算,用给定的表达式修改图像中的所有值。例如,这里我们取 IM 内置的“rose:”图像,把所有像素值乘以 50%。 |

  magick rose: -fx 'u*1.5'    fx_rose_brighten.gif

[IM 输出]
在上面的示例中,红、绿、蓝各个值都被乘以了 1.5。如果结果值超出 0 到 1 的范围,就会被限制到相应的边界(本例中为 1.0),除非你使用的是默认的 HDRI 版 ImageMagick数学化调色直方图曲线中探讨了许多其他用于重新着色的“[-fx](https://imagemagick.org/command-line-options/#fx)”公式。由于我们也能在修改第一幅图像的表达式中引用当前图像序列里的任意图像,因此几乎可以按我们想要的任何方式合并两幅乃至更多图像。这里我们生成一幅黑-红-蓝配色图,方法是把一个黑到蓝渐变(经过旋转)的蓝色通道复制到上面生成的黑到红渐变中。

  magick -size 64x64 gradient:black-blue -rotate -90  fx_blue.gif
  magick fx_red.gif  fx_blue.gif \
          -channel B  -fx 'v'    fx_combine.gif

[IM 输出] [IM 输出] [IM 输出]

| _当然,我们本可以直接改用通道复制合成方法,那会快得多。但这不是重点。

不过反过来也成立。几乎每一个 IM 图像操作都可以用等价的 FX 函数来替代。

_
---|---
上例中的第二幅图像仅用作源图像。实际发生的是,“[-fx](https://imagemagick.org/command-line-options/#fx)”首先只创建第一幅图像的副本,然后依据公式、利用给出的所有其他图像来修改这份副本,最后丢弃所有输入图像,用修改后的第一幅图像副本取而代之。你还可以基于图像内每个像素的位置来计算数值。值“i,j”是当前正在处理的像素的位置,而“w,h”给出图像的尺寸(除非给出特定的图像限定符,否则指第一幅图像)。例如,这里我们生成一幅 DIY 渐变图像。 |

  magick rose: -channel G -fx 'sin(pi*i/w)' -separate   fx_sine_gradient.gif

[IM 输出]
或者用上“i,j”这两个位置值做些更复杂的事情。 |

  magick -size 80x80 xc: -channel G -fx  'sin((i-w/2)*(j-h/2)/w)/2+.5'\
          -separate fx_2d_gradient.gif

[IM 输出]
在生成灰度渐变时,只需让 -fx 运算符仅处理一个颜色通道(例如上例中的“G”即绿色通道),就能让它的运行速度提升约 3 倍。随后可将该通道分离出来,生成最终的灰度图像。这能带来非常大的提速,在使用非常复杂的“[-fx](https://imagemagick.org/command-line-options/#fx)”公式时尤其明显。想了解更多 FX 生成的渐变,参见示例打造你自己的渐变 你可以利用位置信息,通过“p{x,y}”语法从源图像中查取特定像素。例如,你可以轻松写出自己的“镜像”类函数(类似“[-flop](https://imagemagick.org/command-line-options/#flop)”图像运算符),它把每个像素替换为原始源图像中“镜像”位置处的颜色值。 |

  magick rose: -fx  'p{w-i-1,j}'  fx_rose_mirror.gif

[IM 输出]
通过以图像形式创建扭曲图像映射或其他类型的数值查找表,这类“图像扭曲”变得更加强大。DIY 抖动图案与阈值图中提供了相关示例,其中用 FX 将特定颜色替换为来自其他图像的图案。 FX 表达式生成的最终图像,其尺寸与给出的第一幅图像相同,因此要生成更大的图像,你需要把第一幅图像设为你想要的尺寸。在这种情形下,可以用第二幅(甚至第三幅)图像作为颜色源(这也是下一个示例中要用到 Swap 的原因)。例如,这里我们对 rose 图像进行调整尺寸(使用插值缩放或调整尺寸)以生成更大的图像。 |

  magick rose:  -size 120x80 xc: +swap  \
           -fx 'v.p{  (i+.5)*v.w/w-.5, (j+.5)*v.h/h-.5  }' \
           fx_scaled.png

[IM 输出]
注意像素查找是如何进行的,它看起来可能很复杂,但这正是缩放(扭曲)图像的正确方式。基本上,表达式中额外加上的那些 '0.5' 值,是为了在输入坐标 'i,j' 和位置查找 'v.p{...}' 所使用的像素坐标与实际数学计算(缩放)所需的、在数学上更严谨的图像坐标之间正确换算所必需的。上述做法实际上正是任何形式的图像扭曲所采用的确切方法。通过开启详细扭曲摘要,你可以看到大多数扭曲的这个 FX 等价形式。它会为大多数图像扭曲报告一个 FX 等价形式,作为一种复查扭曲是否按预期运作的手段。用 FX 自助运算符来完成图像扭曲,恰恰展示了这个运算符究竟有多强大。若不是有这个运算符,我怀疑许多新的操作(例如扭曲、稀疏色彩或有序抖动)根本不会被加入 ImageMagick 核心库。下面是稍微简单一点的例子,交换玫瑰图像的红色和蓝色通道。看看你能否弄清它是如何工作的。 |

  magick rose: \( +clone -channel R -fx B \) \
          +swap -channel B -fx v.R     fx_rb_swap.gif

[IM 输出]
| _完成同样效果更快更好的方式,是使用 "[-separate](https://imagemagick.org/command-line-options/#separate)" 和 "[-combine](https://imagemagick.org/command-line-options/#combine)")。参见合并 RGB 通道图像。或者你也可以使用 "[-color-matrix](https://imagemagick.org/command-line-options/#color-matrix)" 来把同样的事做得更快。

你从中看出规律了吗?_
---|---
由于 "[-channel](https://imagemagick.org/command-line-options/#channel)" 的默认设置,它将 "[-fx](https://imagemagick.org/command-line-options/#fx)" 运算符的输出限制为仅三个色彩通道。这意味着如果你想影响 alpha 通道或透明度通道,就必须通过更改通道设置来显式指定它。例如,让我们把 "rose:" 图像做成半透明的,方法是将所有 alpha 通道值设为一半。 |

  magick rose: -alpha set  -channel A  -fx '0.5'    fx_rose_trans.png

[IM 输出]
注意,为了让上述操作正常运作,我需要确保 "rose:" 实际拥有一个供 "[-fx](https://imagemagick.org/command-line-options/#fx)" 使用的 alpha 通道。我用Alpha 通道控制运算符做到了这一点。"[-fx](https://imagemagick.org/command-line-options/#fx)" 运算符操控图像 RGBA 通道的这种能力,使它非常适合用于处理通道与蒙版。
从 IM 6.2.10 起,你可以在 "[-fx](https://imagemagick.org/command-line-options/#fx)" 表达式中加入变量赋值,这让你能够降低某些表达式的复杂度——这些表达式若用其他方式基本上是不可能实现的。例如,这里我基于到某个特定点的距离创建了一个渐变(该点赋给了变量 'xx' 和 'yy')。若不使用变量,这个公式可能会变得非常难读。 |

  magick -size 100x100 xc:  -channel G \
          -fx 'xx=i-w/2; yy=j-h/2; rr=hypot(xx,yy); (.5-rr/70)*1.2+.5' \
          -separate  fx_radial_gradient.png

[IM 输出]
| 由于 "[-fx](https://imagemagick.org/command-line-options/#fx)" 采用的分词处理很简单,变量名只能由字母组成,且不得包含数字。另外,由于许多单个字母被用作访问图像信息的内部变量,建议变量名至少为两个字母长。因此我使用 'xx' 和 'yy',而不是仅仅 'x' 或 'y'。
---|---
| _"[-fx](https://imagemagick.org/command-line-options/#fx)" 函数 'rr=hypot(xx,yy)' 是在 IM v6.3.6 中加入的,用来加速那个非常常用的表达式 'rr=sqrt(xx*xx+yy*yy)'。

当然,如果你需要的是距离的平方,就应避免使用 'hypot()' 函数,以及它隐含的 sqrt() 函数。_
---|---
关于一些真正复杂表达式的更多示例,参见更复杂的自助渐变,这些若没有多语句赋值将无法实现。同样的道理也适用于透视扭曲的 FX 形式。从 IM 版本 6.3.0-1 起,"[-fx](https://imagemagick.org/command-line-options/#fx)" 表达式的复杂度开始需要外部文件,因此现在可以用标准的 '@_filename_' 从文件中读取表达式。 |

   echo "u*2" | magick rose: -fx "@-"  fx_file.png

[IM 输出]
这也意味着你可以使用更复杂的脚本,为特定任务生成具体的 FX 表达式。在内部,文件只是被读入一个字符串并像往常一样解释。对 "[-fx](https://imagemagick.org/command-line-options/#fx)" 而言,其他重要的设置还有 "[-virtual-pixel](https://imagemagick.org/command-line-options/#virtual-pixel)" 和 "[-interpolate](https://imagemagick.org/command-line-options/#interpolate)"。虚拟像素设置允许你设定当查找坐标超出输入图像所覆盖的区域时应返回什么颜色或图像结果。这让你可以为模糊之类的效果设置边缘处理,也可以将图像平铺到更大的区域上。插值设置允许你指定当查找坐标(浮点值)落在输入图像中像素的整数坐标之间时,IM 应如何混合相邻像素的颜色。更多信息参见插值像素查找。 | 在不同时期又加入了另外一些函数
IM v6.3.6 : hypot()
IM v6.7.3-4 : while(), not(), guass(), squish()

---|---

FX 调试

debug(_expr_)”本质上是一种打印浮点值的方式,每次计算 FX 表达式时都会输出。这反过来提供了一种调试表达式的方法。不过你可以通过使用三元的 if-else 表达式来限制“debug()”的输出。例如下面这条命令会打印内置“rose:”图像中像素 10,10 的浮点颜色值。而实际的图像结果则通过使用“[NULL:](files.html#null)”图像处理器被忽略。 |

  magick rose: -fx 'i==10&&j==10?debug(u):1; u' null:

[IM 输出]
记住输出是在标准错误上,而不是通常的标准输出,这样你就可以在命令管道中使用它而不会出现问题。请注意 FX 表达式是如何被执行了三次的,仅针对那一个像素、每个通道各执行一次。再乘以像素的数量,你就可以想象出如果“debug()”没有被限制在单个像素上,即便对于这样一张小图像,输出会有多长。

类似 FX 的内置运算

[-fx](https://imagemagick.org/command-line-options/#fx) 运算符提供了一种开发 ImageMagick 此前不存在的新图像处理功能的途径。用户借此开发出的成果,使 ImageMagick 得以扩展出新的功能与方法,例如色彩查找表("[-clut](https://imagemagick.org/command-line-options/#clut)")。不过一般来说,某个新方法用 "[-fx](https://imagemagick.org/command-line-options/#fx)" 稳定下来后,其表达式便会被转换为更快的内置运算,通常作为一组同类运算符的一部分加入。这些包括以下通用图像运算符及其方法…… -evaluate | 直接修改像素、颜色值、通道的函数。
(参见下文 Evaluate)。
---|---
-function | 更复杂的像素、颜色值、通道修改函数。
(参见下文 Function)。
-evaluate-sequence | 以数学方式合并多图序列
(参见下文 Evaluate-Sequence)。
-sparse-color | 通用图像重新着色运算符。
(参见稀疏颜色渐变
-compose | 通用的多图组合与叠加方法。
(参见 Alpha 合成)。
-distort | 通用图像扭曲运算符,采用反向像素映射。
(参见扭曲运算符
-morphology | 通用的区域效果卷积/形态学函数。
(参见形态学运算符卷积运算符
人们在开发新型图像操作时,通常先用 "[-fx](https://imagemagick.org/command-line-options/#fx)" 运算符做原型。一旦调试完成,那个"方法"便会转换为 ImageMagick Core 库中全新的超高速内置运算符。如果用户认为自己的 "[-fx](https://imagemagick.org/command-line-options/#fx)" 表达式(或其他自定义函数)会是对 IM 的有用补充,而现有图像运算符尚未涵盖它,欢迎贡献出来;只要能由上述某个通用运算符处理,添加起来应该相当容易。例如,我自己在比较两幅图像时需要一种"颜色相近则遮罩"的操作。它已作为新的 "[-compose](https://imagemagick.org/command-line-options/#compose)" 方法 "[ChangeMask](compose.html#changemask)" 加入。这又让我得以为 GIF 动画添加更复杂的透明度优化。如果 "[-fx](https://imagemagick.org/command-line-options/#fx)" 的速度与复杂度开始成为问题,那么改用 PerlMagick 之类的 API 脚本语言或许更好。该 API 的发行版中就有一个使用 PerlMagick 的示例 "[pixel_fx.pl](https://github.com/ImageMagick/ImageMagick/blob/main/PerlMagick/demo/pixel-fx.pl)"。

FX 表达式用作格式与标注转义

从 IM 6.2.10 版本起,你现在可以在诸如 "[-format](https://imagemagick.org/command-line-options/#format)" 和 "[-annotate](https://imagemagick.org/command-line-options/#annotate)" 参数所用的图像属性转义字符串中使用 FX 表达式。转义序列 '%[fx:...]' 会被替换为一个数字(浮点值),针对当前图像序列中的每个图像各计算一次。不过 FX 表达式 在处理时会稍作修改。具体来说……

  • 当前像素坐标 'i'、'j' 被固定为 0,因此图像变量单独使用时只返回像素 0,0 的值,除非使用了 'p{}' 索引。
  • 除非选定了某个色彩通道,否则只返回红色通道的值。
  • 默认图像引用 's' 被设为当前正在标注或识别的图像。
  • 索引 't' 返回 's' 所引用图像的索引。

| 在 IM v6.6.8-6 之前,"t" 图像索引和 "n" 图像总数这两个 FX 表达式值都是坏的,对所有图像分别只返回 0 和 1。等价的百分号转义 '%p' 和 '%n' 也是如此。
---|---
例如,这里我用 "[-annotate](https://imagemagick.org/command-line-options/#annotate)" 给每个图像标注上其左上角的颜色。 |

  magick -size 150x25 xc:DarkRed xc:Green xc:Blue \
          -fill white -gravity center \
          -annotate 0 '%[fx:t] / %[fx:n] : %[fx:r],%[fx:g],%[fx:b]' \
          annotate_fx_%d.gif

[IM 输出] [IM 输出] [IM 输出]
注意写入的文本对每个图像都不同,因为 'r' 实际上等价于 's.p{0,0}.r'。'g' 和 'b' 色彩通道的值也是如此。当然,每个值都返回 0.0 到 1.0 范围内的归一化值。为了更方便地输出特定像素的颜色值,IM v6.3.0 中还新增了一个 '%[pixel:...]' 转义。该运算符会对每个图像中的每个通道各调用一次给定的 FX 表达式,并把返回值格式化为 IM 能作为颜色参数处理的颜色。

  magick -size 300x100 gradient:yellow-limegreen \
          -gravity NorthWest  -annotate 0 '%[pixel:s.p{0,0}]' \
          -gravity Center     -annotate 0 '%[pixel:s.p{0,50}]' \
          -gravity SouthEast  -annotate 0 '%[pixel:s.p{0,99}]' \
          annotate_pixel.gif

[IM 输出]

你也可以用带 "[-format](https://imagemagick.org/command-line-options/#format)" 的 "[identify](basics.html#identify)" 命令直接输出结果。 |

  magick identify -format '%[fx:atan(1)*4]' null:

[IM 输出]
这将以数学方式计算并返回 PI 的值,不过该值也可以通过内置变量 'pi' 直接获取。你还可以生成随机数。例如,生成一个介于 -5 到 10(含两端)之间的整数。这里我使用与 "magick identify" 命令等价的 "[info:](files.html#info:)"。 |

  magick xc: -format '%[fx:int(rand()*16)-5]' info:

[IM 输出]
更多方法参见 Identify 的替代方案:文本输出选项。另见 圆角边框,它使用了 FX 表达式 来根据图像的宽度和高度信息生成绘制字符串。你可以 使用 FX 公式计算图像位置,甚至可以借助其他图像的尺寸和位置来定位(参见 逐步计算的位置)。你还可以在 文件名百分号转义 中使用 FX 转义,根据计算出的值生成新文件。示例可参见 平铺裁剪 中的最后一个例子。
上述所有操作本质上都会运行 "[-format](https://imagemagick.org/command-line-options/#format)",因而会对当前图像序列中的每一张图像各运行一次其中包含的 FX 表达式。"[-print](https://imagemagick.org/command-line-options/#print)" 运算符的工作方式与 "[-identify](https://imagemagick.org/command-line-options/#identify)" 十分类似,区别在于它只运行一次,并且可以访问当前图像序列中的所有图像。使用该运算符时,你可以用 'u[{i}]' 来访问任意图像的值,这一点与上述方式不同。
FX 表达式也可以应用于其他色彩空间中的图像,因此我可以,例如,找出三种不同颜色的 'Hue'(色相)值(位于 'red' 通道中)。 |

  magick xc:red xc:green xc:blue -colorspace HSL \
          -format '%[fx: s.r ]\n' info:

[IM 输出]
你还可以用 IM 做一些直接的颜色数学运算,例如求出 'gold'、'yellow' 和 'khaki' 的平均颜色。 |

  magick xc: -format '%[pixel:(gold+yellow+khaki)/3]' info:

[IM 输出]
而下面这个例子则展示了该颜色与三种源颜色相比的样子…… |

  magick xc:gold xc:yellow xc:khaki +append \
          \( xc: -fx '(gold+yellow+khaki)/3' \) \
          -scale 90x30\! -append fx_hues.png

[IM 输出]
你还可以使用 "[-print](https://imagemagick.org/command-line-options/#print)" 来打印信息。它只对整个图像序列应用一次。这意味着你可以用这个运算符来计算涉及多张图像、复杂得多的 '%[fx:...]' 表达式。

从其他图像获取数据

然而,使用 FX 转义表达式存在一个严重的问题。在创建图像时,IM 无法直接访问当前图像序列中的其他图像。在典型的图像创建过程中,通常并不需要这样做,因为新图像一般不依赖于内存中已有的图像。基本上,如果你想采集另一幅图像中某个特定像素的颜色(不同于你正在绘制的这幅,如上例),或者正在创建一幅新图像,那么 IM 核心函数与所需信息之间就没有直接的联系。例如,如果你尝试用内置的 "rose:" 图像中像素 12,26(一个偏蓝的像素)的颜色来创建一个标签,直接的做法会失败! |

  magick rose: label:'%[pixel:p{12,26}]' -delete 0 label_fx_direct.gif

[IM 输出]
其实 rose 图像本身并不包含任何黑色像素,所以上面的结果是错误的。解决办法是先提取想要的信息,并将其保存到全局的 IM 元数据中。这样它就会被传递给库核心中的所有子例程,包括那些用于图像创建的子例程。 |

  magick rose: -set option:mylabel '%[pixel:u.p{12,26}]' -delete 0 \
          label:'%[mylabel]'    label_fx_indirect.gif

[IM 输出]
这并不直观,但我们现在得到了正确的结果。特殊的 'option:' 标记告诉 "[-set](https://imagemagick.org/command-line-options/#set)" 选项,你希望将给定的设置保存为一个全局产物,而不是作为图像的 'Attribute' 或 'Properties' 字符串,这一点与 "[-define](https://imagemagick.org/command-line-options/#define)" 相同。不过 "[-set](https://imagemagick.org/command-line-options/#set)" 形式允许你在设置产物时展开百分号转义,而 "[-define](https://imagemagick.org/command-line-options/#define)" 则不行。当 "label:" 运算符展开其百分号转义时,会先把给定的 'key' 作为每幅图像的 'attribute' 或 'properties' 来查找,但如果找不到任何内容,接着就会在全局产物设置中查找该 'key'。因此,我们从上一幅图像创建的那个全局 'artifact' 会被使用,即便在创建产物时那幅图像已经不再存在。基本上,'Artifact' 设置在 "magick" 命令的整个生命周期内都是全局的,因此可以用来把信息从一幅图像传递到另一幅图像。对于编程的 API,这种情况可以避免,因为你可以直接从图像中读取所需的数据并自行生成标签字符串,而无需让 IM 以如此曲折的方式来存储这些信息。


Evaluate 与 Function,自由形式的通道修改器

由于 FX 运算符是一个解释型表达式处理器,因此新增了 "[-evaluate](https://imagemagick.org/command-line-options/#evaluate)" 运算符,让你能够更快地完成简单的图像修改。后来在 IM v6.4.8-8 中又新增了更复杂的 "[-function](https://imagemagick.org/command-line-options/#function)" 运算符,以便在复杂的图像调整中获得更大的灵活性。这两个运算符,连同其他图像色阶调整运算符,例如 "[-negate](https://imagemagick.org/command-line-options/#negate)"、"[-level](https://imagemagick.org/command-line-options/#level)",很可能最适合在你应用这些图像之前,用于对灰度图像做细微的调整。尤其是在灰度图像中,比如用于背景移除高光与阴影叠加,以及图像映射的生成与微调。

Evaluate,简单的数学运算

"[-evaluate](https://imagemagick.org/command-line-options/#evaluate)" 运算符基本上是 "[-fx](https://imagemagick.org/command-line-options/#fx)" 运算符的一个快速但非常简单的版本(实际上它比 -fx 加入 IM 还早了几个月)。不过它只限于一次简单的运算,使用单个由用户提供的常数。你可以使用下面的命令查看 evaluate 中内置了哪些函数

  magick -list evaluate

这包括常见的数学函数 'add'、'subtract'、'multiply' 和 'divide',都是针对常数值进行的。与 [-fx](https://imagemagick.org/command-line-options/#fx) 运算符不同,这里的值不会被归一化到 0 到 1 的范围,而是保持图像真实的颜色值。因此在 Q8 的 IM 中减去 50(参见质量与深度)会造成很大的减幅,而在 Q16 版本的 IM 中,则只是一个几乎察觉不到的微小变化。不过,如果你在参数后加上 '%',该参数就会表示最大颜色值的百分比(即 'QuantumRange',它等于 '2 _quality_ -1')。这意味着,只要对相应的 evaluate 方法恰当地使用百分比,你就可以让 "[-evaluate](https://imagemagick.org/command-line-options/#evaluate)" 的参数与 IM 的质量级别无关。 举例来说,要把图像中所有颜色值都简单地替换为 50% 的灰度级别,使用 'Set ' 既非常简单又非常快 |

  magick rose: -evaluate set 50%  rose_set_gray.gif

[IM 输出]
"[-evaluate](https://imagemagick.org/command-line-options/#evaluate)" 运算符同样包含常见的数学函数 'add'、'subtract'、'multiply' 和 'divide'。 例如,要把图像的对比度减半,你可以先用 'divide ' 把它除以 '2',再 'add ' '25% 以让它重新围绕理想的灰色居中。 |

  magick rose: -evaluate divide 2 -evaluate add 25%  rose_de-constrast.gif

[IM 输出]
这比直接用 "[-fx](https://imagemagick.org/command-line-options/#fx)" 运算符搭配 'u/2+.25' 要快上好几个数量级。因此只要有可能,你都应该优先使用这个运算符而不是 "[-fx](https://imagemagick.org/command-line-options/#fx)"。"[-evaluate](https://imagemagick.org/command-line-options/#evaluate)" 的_主要_问题在于,所有结果都会被裁剪到 0 到 'QuantumRange' 的范围内(除非你使用的是 HDRI 版本的 ImageMagick),因为每个修改后的值都会被写回图像数据。这意味着,在任何一次单独的 "[-evaluate](https://imagemagick.org/command-line-options/#evaluate)" 运算之后,这些值都可能被 'QuantumRange' 裁剪掉。 因此,如果你试图照原样直接应用一个对比度增强函数(相当于 "[-fx](https://imagemagick.org/command-line-options/#fx) '2*u-.25' "),就会得不到正确的结果,因为翻倍后的值在做减法之前就已经被裁剪了。 |

  magick rose: -evaluate multiply 2  -evaluate subtract 25% \
          rose_contrast.gif

[IM 输出]
首先,'multiply ' 会把所有较大的颜色值都裁剪到最大值,然后 'subtract' 会裁剪下界的值。结果就是对上界进行了错误的裁剪,产生一幅偏暗且颜色失真的图像。 直接的解决办法是先 'subtract ' 相应的常数(对下界做最终且正确的裁剪),再做乘法,实际上就是使用等价公式 '(u-.125)*2' |

  magick rose: -evaluate subtract 12.5%  -evaluate multiply 2 \
          rose_contrast2.gif

[IM 输出]
不过,针对这个"裁剪"问题有很多替代方案。第一个合乎逻辑的方案是较新的多项式函数方法(见下文)。其他替代方案还包括使用色阶调整运算符,甚至按颜色进行色阶调整,只需指定你想要拉伸、以填满整个颜色范围的原始颜色值即可。总之,在使用多个 "[-evaluate](https://imagemagick.org/command-line-options/#evaluate)" 方法时,要注意颜色值的裁剪问题。
"[-evaluate](https://imagemagick.org/command-line-options/#evaluate)" 运算符和 "[-fx](https://imagemagick.org/command-line-options/#fx)"(以及大多数其他底层 IM 运算符)一样,会受 "[-channel](https://imagemagick.org/command-line-options/#channel)" 的影响。这让你可以把图像的 alpha 透明度与颜色通道分开控制。而且没错,和 "[-fx](https://imagemagick.org/command-line-options/#fx)" 一样,透明度被当作 'alpha 值' 而不是 'matte' 值来处理。例如,作为溶解类操作的一部分,把图像变成 50% 透明。 |

  magick rose: -alpha set  -channel A -evaluate divide 2   rose_transparent.png

[IM 输出]
结果是一幅半透明的图像,也就是说,显示时你看到的颜色有一半是网页的背景色。因此显示出来的图像会朝背景色变暗。我还常常发现,在把各个通道分离成用于特定用途的独立图像之前,先对单个颜色通道使用 "[-evaluate](https://imagemagick.org/command-line-options/#evaluate)" 往往更容易(参见分离通道)。例如,这里我用它来做一种快速但不太常见的灰度化。基本上,我把每个通道乘以相应的量,然后分离并把这些通道相加,得到一幅按特定颜色比例进行灰度化的图像。 |

  magick test.png -channel R -evaluate multiply .2 \
                   -channel G -evaluate multiply .5 \
                   -channel B -evaluate multiply .3 \
                   -channel RGB -separate -background black -compose plus -flatten gray_253.png

[IM 输出]

求值数学函数

包含在 Evaluate 中的,还有一组专用的数学函数。这些函数在实现上一般使用归一化的颜色值(0 到 1 的范围),输出同样经过归一化,从而适配图像的完整颜色范围。S 形对比度 函数也是这类数学函数适配的一个例子。

幂运算

例如 'Pow ' 函数(IM v6.4.1-9 加入)作用于归一化的颜色值,让用户可以修改图像亮度。它完全等价于 C 语言的 pow() 函数(使用 0 - 1 范围的归一化颜色值)

    value = pow(value, constant)

因此若要生成一条“抛物线”式的渐变,可以使用参数 '2'。或者使用 '0.5' 生成一条“平方根”式的渐变。例如……

  magick -size 20x600  gradient: -rotate 90  gradient.png
  magick gradient.png  -evaluate Pow  2   eval_pow_parabola.png
  magick gradient.png  -evaluate Pow 0.5  eval_pow_sq_root.png

[IM 输出] [IM 输出] [IM 输出]
[IM 输出] [IM 输出] [IM 输出]

| 下方的三张图同时以图表和原图本身展示了该渐变的剖面。这样更容易看出一张渐变图是如何被修改成另一张的。它是通过Gnuplot 绘图程序生成的,使用的是 IM Examples Scripts 目录中的脚本 "[im_profile](../static/img/scripts/im_profile)"。
---|---
这实际上等价于伽玛调整 运算符,只是参数被取了倒数。例如 "-gamma 2" 运算等价于 "-evaluate pow 0.5" 也就是“平方根”运算函数。同样,"-gamma 0.5" 等价于使用 "-evaluate pow 2" 求平方。通过一些特殊的渐变处理,你可以用这个方法把一条线性渐变变换成一段复杂的圆弧。 |

  magick -size 20x300  gradient: -rotate 90 \
          -evaluate Pow 2 -negate -evaluate Pow 0.5 \
          -flop \( +clone -flop \) +append  eval_circle_arc.png

[IM 输出] [IM 输出]
对于想弄清楚原理的人,上面第二行等价于 FX 表达式 'sqrt(1-u^2)'。它生成一段四分之一圆弧,随后被Flop 翻转,并追加拼接 在一起,形成一段半圆弧。尽管它需要更多单独的(较小的)步骤,但也比使用 FX 表达式 快得多。另请参见更高级的多项式函数

对数

'Log ' 函数(在 IM v6.4.2-1 中加入)同样作用于归一化值(为避免出现无穷大而加上 1.0),并将给定的常量用作对数的底。因此(使用归一化值时)实际公式为……

    value = log(value*constant+1.0)/log(constant+1.0)

例如…… |

  magick gradient.png -evaluate Log 10  eval_log.png

[IM 输出] [IM 输出]
这看起来可能与前面的 Pow 求值方法 非常相似,但并不完全相同。'Log' 在接近 '0' 时会产生明显的斜率,而 'Pow' 则会产生垂直的斜率。该值控制着斜率。对数函数还与指数函数密切相关,而指数函数目前仅以 Sigmoidal 对比度调整 运算符的形式实现。它包含了与上面对数曲线中所见相同的斜率特征。这也解释了为何在增强低光照条件下的图像时,"[-sigmoidal-contrast](https://imagemagick.org/command-line-options/#sigmoidal-contrast)" 会是比 Gamma 调整 或"幂次"曲线更好的技术。

正弦与余弦

自 IM v6.4.8-8 起,新增了「sin 」和「cos 」方法。这些方法取图像中给定的值,并将其归一化为一个角度,使整个取值范围覆盖一整圈角度。结果被赋予 50% 的偏置,并重新缩放以再次落入正常的取值范围。该常量用作值(也即角度)的乘数,因此「N」表示该函数会在整个取值范围内绕圆「N」圈。具体来说,它(使用归一化值)定义了如下函数……

   value = 0.5 * sin( constant*value*2*PI ) + 0.5
   value = 0.5 * cos( constant*value*2*PI ) + 0.5

本质上,这些函数所做的是把图像值(通常是灰度值)重新映射到正弦/余弦曲线上。例如,这里我取一幅渐变图像,并用这些 evaluate 方法对其进行修改。

  magick gradient.png  -evaluate sin 1  eval_sin_1.png
  magick gradient.png  -evaluate cos 1  eval_cos_1.png

[IM 输出] [IM 输出]
[IM 输出] [IM 输出] [IM 输出]

由于该常量参数是一个角度乘数,赋给 evaluate 方法的值会在图像内整个渐变上生成相应数量的波峰。 |

  magick gradient.png -evaluate cos 5  -negate  eval_cos_5.png

[IM 输出] [IM 输出]
这非常适合许多任务,从生成波纹或分散效果,到生成波纹状的位移曲线。使用乘数常量「0.5」,你可以简单地把线性渐变变成正弦曲线渐变,它仍与原来保持相同的斜率。通过对结果取反(negate),你可以确保渐变也以正确的方向倾斜。 |

  magick gradient.png -evaluate cos 0.5  -negate  eval_cos.5.png

[IM 输出] [IM 输出]
这非常适合生成用于重叠照片的平滑渐变。不过,最后这两个「[-evaluate](https://imagemagick.org/command-line-options/#evaluate)」方法很少被使用,因为它们已被更通用的 Sinusoid 函数(见下文)所取代,后者提供了超出简单频率选项之外的更多控制选项。


Function,多参数 Evaluate

上述波形生成器被证明极其有用,尤其是配合畸变图像映射时。但人们发现需要对这些函数进行更精细的控制,这需要不止一个参数。为此,IM v6.4.8-9 中新增了「[-function](https://imagemagick.org/command-line-options/#function)」运算符。基本上,「[-function](https://imagemagick.org/command-line-options/#function)」就是「[-evaluate](https://imagemagick.org/command-line-options/#evaluate)」的多参数形式。不过,与 Evaluate 运算符 不同,这些运算符和数学运算符一样,上述所有函数都只作用于图像的归一化通道值(0.0 到 1.0 范围),这在多数情况下使它们更易于使用。

多项式函数

polynomial 」方法接受任意数量的值,并按照给定的确切表达式修改图像中的色值,其速度远快于 FX 运算符

-function   Polynomial   a,b,c,...

每个值都会用作从最高次到最低次的系数,以生成一个具有给定项数的多项式。例如,参数「4,-4,1」会生成与「[-fx](https://imagemagick.org/command-line-options/#fx)」表达式「 4*u^2 - 4*u + 1 」等价的多项式表达式。如果你还记得高中数学,就应该知道这个多项式函数会在输入(「u」)色值范围 0.0 到 1.0 上生成一条从 1.0 到 0.0 再回到 1.0 的抛物线曲线。也就是说,它会把黑色和白色变成「白色」,把纯灰色变成「黑色」。 |

  magick gradient.png -function Polynomial 4,-4,1  func_parabola.png

[IM 输出] [IM 输出]
你甚至可以制作复杂得多的渐变,例如四次多项式,它是使用一组「电平控制点」生成曲线电平调整的结果。这通常用于调整图像的色彩,为其赋予各种明暗效果。 |

  magick gradient.png -function Polynomial '-25, 53, -36, 8.3, 0.2' \
          func_quartic.png

[IM 输出] [IM 输出]
当然,简单的线性修改也是可以的,效果与你使用 Level 运算符 时得到的完全相同…… |

  magick gradient.png -function Polynomial '4, -1.5' func_linear.png

[IM 输出] [IM 输出]
不过请注意,你无法用「Polynomial」执行完整的阈值运算,因为那样做需要无限多个系数,尽管你可以相当接近。单个值自然就只是一个常量,会直接把该值赋给结果。换句话说,这就像「[-evaluate](https://imagemagick.org/command-line-options/#evaluate) Set」方法,在这里是赋为 33% 的灰度值。 |

  magick gradient.png -function Polynomial 0.33 func_constant.png

[IM 输出] [IM 输出]
通过将「Polynomial」与其他数学函数组合,你可以创建更加复杂的渐变修改。例如,对一个多项式取平方根,我就能在线性渐变上创建一条真正的圆弧。等价的「[-fx](https://imagemagick.org/command-line-options/#fx)」表达式为「sqrt( -4*u^2 + 4*u + 0 )」…… |

  magick gradient.png -function Polynomial -4,4,0 -evaluate Pow 0.5 \
          func_circle_arc.png

[IM 输出] [IM 输出]
上述方法的替代方案,另见 Pow Evaluate 方法

Sinusoid 函数

Sinusoid 」函数方法是「[-evaluate](https://imagemagick.org/command-line-options/#evaluate)」方法「sin」和「cos」的高级得多的版本,实际上它可以复现那些函数,但你对它如何修改图像中的色值拥有更好的控制。

-function   Sinusoid   frequency,phase,amplitude,bias

它使用如下公式实现……

  value = ampl * sin(2*PI( freq*value + phase/360 ) ) + bias

这看起来可能很复杂,但它确保了函数易于使用。只有第一个值「frequency」是必需的,其作用与上文完全相同,其余所有参数都是可选的。默认情况下它会生成一条正弦曲线。 |

  magick gradient.png -function Sinusoid 1    func_sine.png

[IM 输出] [IM 输出]
通过添加以度为单位的「phase 」参数,你可以指定曲线的起始角度。这让你能把默认的正弦曲线变成余弦曲线。 |

  magick gradient.png -function Sinusoid 1,90   func_cosine.png

[IM 输出] [IM 输出]
通过调整「frequency」和「phase」,我可以直接把线性渐变变成一条从黑到白(沿正弦曲线从最小到最大)的平滑正弦渐变。较为间接的方法请参见 Evaluate Cosine 方法。 |

  magick gradient.png -function Sinusoid 0.5,-90 func_sine_grad.png

[IM 输出] [IM 输出]
接下来的两个可选值「amplitude」和「bias」控制正弦曲线的幅度和中心线。例如,这里我制作一个在白色与灰色之间振荡的波(取反的余弦曲线),其值范围为 0.75 ±0.25,即 0.5 to 1.0,从白色开始并以白色结束。 |

  magick gradient.png -function Sinusoid 5,90,.25,.75  func_sine_bias.png

[IM 输出] [IM 输出]
请小心这最后几个参数,因为它们很容易导致波形超出色值范围的边界,从而被裁剪(除非你使用的是 HDRI 版本的 ImageMagick)。

Arcsin 函数

反正弦函数「Arcsin 」在 IM v6.5.3-0 中加入。这是为生成柱面置换图所需的一条特殊曲线。它的参数为……

-function   Arcsin   width,center,range,bias

其实现所用公式为……

  value = range/PI * asin(2/width*( value - center ) ) + bias

默认值(未指定时)为「1, 0.5, 1, 0.5」,可确保该函数居中,从而覆盖从 0,01,1 的整个颜色范围。 |

  magick gradient.png -function Arcsin 1    func_arcsin.png

[IM 输出] [IM 输出]
将所得曲线的「width 」减半,你会得到…… |

  magick gradient.png -function Arcsin 0.5    func_arcsin_width.png

[IM 输出] [IM 输出]
center 」让你可以根据输入的灰度值重新定位曲线。 |

  magick gradient.png -function Arcsin 0.4,0.7 func_arcsin_center.png

[IM 输出] [IM 输出]
range 」参数用于缩减颜色值的输出范围,而「bias 」则调整该范围的中心。 |

  magick gradient.png -function Arcsin 0.5,0.5,0.5,0.5  func_arcsin_range.png

[IM 输出] [IM 输出]
请注意函数结果中无效值的处理方式。这在将该函数用于置换时可实现更好的控制,并提供了清理这些值的手段。实际使用的值为「_bias_ ± _range_ /2」,正如你所预期的那样。请注意,若将「width 」或「range 」设为负值,函数的斜率会因该负值而翻转。 |

  magick gradient.png -function Arcsin -1    func_arcsin_neg.png

[IM 输出] [IM 输出]

Arctan 函数

Arctan 」方法在 IM v6.5.3-1 中加入。它的参数为……

-function   Arctan   slope,center,range,bias

其实现所用公式为……

  value = range/PI * atan(slope*PI*( value - center ) ) + bias

如你所见,它与「Arcsin」函数几乎完全相同,只是做了一点小改动使其更为实用。它甚至有着相同的一组默认值(未指定时)「1, 0.5, 1.0, 0.5」。这意味着,如果你指定斜率值为「1.0」,直方图变化的斜率会在纯灰附近产生 1:1 的变化(不缩放),同时使白色和黑色变得更接近灰色。例如 |

  magick gradient.png -function Arctan 1 func_arctan.png

[IM 输出] [IM 输出]
也就是说,渐变的中间部分实际上保持不变,只有黑白两端的对比度被削弱。随着曲线「slope 」变大,中心处的渐变会相应地变得更强(在中间更为压缩)。 |

  magick gradient.png -function Arctan 10 func_arctan_10.png

[IM 输出] [IM 输出]
这在许多方面都与 S形对比度 颜色修改运算符非常相似。不过「Arctan」函数永远不会真正达到纯黑与纯白的输出范围极限。它会逼近这些极限,但绝不会越过。与前面的函数(以及 S形对比度)类似,第二个参数会调整曲线相对于输入渐变值的位置。 |

  magick gradient.png -function Arctan 10,.7 func_arctan_center.png

[IM 输出] [IM 输出]
而最后两个参数中的「range 」让你可以调整所生成值的输出范围。例如,通过略微扩大这个值,你可以确保它完全覆盖所有可能值的整个范围。 |

  magick gradient.png -function Arctan 5,0.7,1.2 func_arctan_range.png

[IM 输出] [IM 输出]
不过,如果你真的想用这种方式生成一条曲线来修改整幅图像的对比度,更常见的做法是使用S形对比度运算符,它正是为此目的而设计的。「Arctan」渐变函数更典型的用法,是创建一条能迅速逼近某个特定值但不会超过该值的曲线。而正是这些极限值由「range 」和「bias 」参数控制。例如,这条曲线会修改图像中的渐变,在输入灰度 0.7 附近产生非常锐利的阈值,同时值在 0.5 与 1.0 的范围极限之间变化 |

  magick gradient.png -function Arctan 15,0.7,0.5,0.75 func_arctan_typ.png

[IM 输出] [IM 输出]
这是 S形对比度 无法生成的效果。


渐变图像上的数学运算

上面这些函数为渐变图像提供了一些非常基本的变换。但如果你想对两幅或更多渐变图像做数学运算该怎么办呢?也就是说,用一幅图像的渐变去修改另一幅渐变。为此你需要使用特殊的数学合成方法(例如「[Plus](compose.html#plus)」和「[Divide](#divide)」)。不过在开始之前,我想给你一个忠告。如果你的渐变图像是纯灰度图像、没有 alpha 通道,那么你可以直接使用数学合成方法。但如果你想把这些方法限定在某个特定通道上,或将它们应用于 alpha(透明度)通道,那么你就需要确保设置了合适的「[-channel](https://imagemagick.org/command-line-options/#channel)」,并且不带特殊的「Sync」通道标志。详见使用图像合成进行图像数学运算。通常使用数学合成方法其实并不太难。麻烦之处在于当你的渐变还带有「bias」时。也就是说,该渐变应在「50% 灰」处表示「零」值,并覆盖从 -1(黑)到 +1(白)的范围。这类图像常用于扭曲图像映射。因此,对「有偏渐变 」进行数学运算才是真正的难题,也是这里将要更具体讨论的内容。

衰减有偏渐变

例如,这里我想创建一条正弦波,但它一开始振幅很小,随后逐渐变大。这被称为对有偏渐变进行「衰减」。或者换个说法,就是把一个有偏渐变乘以另一个绝对渐变。这也正是像调幅(AM)广播中「调幅 」的工作原理!所以首先我们需要一条正弦波,它可以简单地由线性渐变生成……

  magick -size 5x300 gradient: -rotate 90   math_linear.png
  magick math_linear.png  -evaluate sine 12  math_sine.png

[IM 输出] [IM 输出]

现在,为了衰减它,我们用一个 Multiply alpha 合成,把正弦波与线性渐变相乘……

  magick math_sine.png  math_linear.png  \
          -compose Multiply -composite  math_sine_2.png

[IM 输出] [IM 输出] [IM 输出]

但若要将它用于比如水波纹置换图,波形就必须始终以正灰居中。为此,我们需要给原图加上一个 bias。恰好这就是我们用来与原图相乘的那个函数,取反后再除以二……

  magick math_linear.png -negate -evaluate divide 2  math_bias.png
  magick math_sine_2.png  math_bias.png \
          -compose Plus -composite  math_attenuated.png

[IM 输出] [IM 输出] [IM 输出]

这样我们就得到了一条线性衰减的正弦波渐变,适合用于置换图。

当然,你也可以在一条命令里完成整个过程,而且它也不必是简单的线性衰减。例如,这里我用一条取反的余弦波(而非线性渐变)来衰减高频正弦波。

  magick math_linear.png  -evaluate cos 1 -negate  math_cosine_peak.png
  magick math_sine.png  math_cosine_peak.png \
          \( -clone 0,1 -compose multiply -composite \) \
          \( -clone  1  +level 50%,0 \
             -clone  2  -compose plus -composite \) \
          -delete 0--2  math_cosine_atten.png

[IM 输出] 衰减 [IM 输出] [IM 输出]

从 IM v6.5.4-3 起,现在可以使用特殊的数学合成方法,把上述所有步骤在一个 compose 方法中完成。基本上就是认识到衰减运算即公式 Sc*Dc-.5*Sc+.5,也就是参数「1,-.5,0,.5」。 |

  magick math_sine.png  math_cosine_peak.png \
          -compose Mathematics -set option:compose:args 1,-.5,0,.5 \
          -composite  math_attenuate.png

[IM 输出]
同样的结果也可以先用多项式函数调整衰减渐变,再用 Exclusion compose 运算符合并图像来实现。 |

  magick math_sine.png \( math_cosine_peak.png -function polynomial -.5,.5 \) \
          -compose Exclusion -composite  math_poly_excl.png

[IM 输出]

乘以偏置渐变

但如果两个函数都是偏置的,即完美的灰色表示零,黑与白表示 -1 到 +1 的范围呢?这会稍微复杂一些,因为乘法可能包含负值,所以不能只是把它们相乘就指望结果正确。这需要一些注意,以确保不会把值裁剪掉,并在结果图像中得到正确取反后的曲线。诀窍是把乘法拆成多个步骤。也就是 A × B 也可以写成 A × abs(B) × sign(B)。这样就避免了乘以负值——负值无法存储在普通的渐变图像中。所以我们要做的,只是取其中一个偏置渐变,把它分成两部分,以便能恰当地应用到另一个渐变上。偏置渐变的「sign()」,也就是获取哪些部分为负的掩码,可以通过在偏置电平处对渐变使用阈值来提取。之后你可以用那张阈值图像,通过合成 Difference选择性地对另一个渐变取反。偏置渐变的「abs()」可以用 Solarize轻松提取,然后对其取反并加倍(使用 Level),得到范围为 0.0 到 1.0 的渐变绝对值。由于乘法中还需要偏置偏移量(如上面的衰减),你可以直接使用取反并减半缩放后的 solarize 输出,也就是它被转换成渐变绝对值之前的那个。那么就让我们用 magick 把一个渐变转换成这三个成分吧。

  magick math_cosine_peak.png  -threshold 50% -negate  math_m_sign.png
  magick math_cosine_peak.png     -solarize 50%        math_m_bias.png
  magick math_m_bias.png          -level 50%,0         math_m_abs.png

| [IM 输出] | 渐变的符号
白 = 负
---|---|---
[IM 输出] | [IM 输出] | 偏置偏移量
| [IM 输出] | 绝对值

现在我们有了其中一个渐变图像的这三个部分,就可以把它们与另一个渐变合并。为此,我们用绝对值相乘,重新加上偏置,然后对应当变为负的部分取反。

  magick math_sine.png   math_m_abs.png \
          -compose Multiply -composite    math_m_1.png
  magick math_m_1.png   math_m_bias.png \
          -compose Plus -composite        math_m_2.png
  magick math_m_2.png   math_m_sign.png \
          -compose Difference -composite  math_multiply.png

[IM 输出] [IM 输出] [IM 输出]
[IM 输出] [IM 输出]
符号 [IM 输出] [IM 输出]

这就是两张偏置渐变图像的完美乘法!这里再用单个命令把它全部完成……

  magick math_sine.png  math_cosine_peak.png \
          \( -clone  1  -threshold 50% -negate \) \
          \( -clone  1      -solarize 50%      \) \
          \( -clone  3      -level 50%,0       \) \
          \( -clone 0,4 -compose multiply   -composite \
             -clone  3  -compose plus       -composite \
             -clone  2  -compose difference -composite \) \
          -delete 0--2    math_multiply_2.png

[IM 输出] [IM 输出] [IM 输出]

最后一点说明。与衰减不同,这种偏置渐变的乘法是可交换的。也就是说,交换输入图像不会影响最终结果。由于上式等同于公式 2*Sc*Dc-Sc-Dc+1,从 IM v6.5.4-3 起,你可以使用参数「2,-1,-1,1」把上述复杂步骤实现为单个「[Mathematics](compose.html#mathematics)」compose 方法。 |

  magick math_sine.png  math_cosine_peak.png \
          -compose Mathematics -set option:compose:args 2,-1,-1,1 \
          -composite  math_bias_multiply.png

[IM 输出]
也就是说,这比不用这个带参数的 compose 方法所需的十几步甚至更多要简单、快速得多。碰巧的是,我一看到那个公式,就意识到这不过是「[Exclusion](compose.html#exclusion)」compose 方法的取反。奇怪但确实如此。因此,下面的命令也会生成同样的零偏置乘法。 |

  magick math_sine.png  math_cosine_peak.png \
          -compose exclusion -composite -negate  math_excl_neg.png

[IM 输出]

加上偏置渐变

随着「[Mathematics](compose.html#mathematics)」compose 方法的出现,加上偏置渐变也相对容易了。等价的 FX 公式是「u+v-0.5」,即 compose 参数「0,1,1,-.5」。例如,下面是我手工生成的一个傅里叶变换示例,它需要把 3 个偏置正弦波与一个常数 DC 值相加。

  magick math_linear.png -function sinusoid 3.5,0,.25     wave_1.png
  magick math_linear.png -function sinusoid 1.5,-90,.13   wave_2.png
  magick math_linear.png -function sinusoid 0.6,-90,.07   wave_3.png

  magick wave_1.png wave_2.png wave_3.png -background gray40 \
          -compose Mathematics -set option:compose:args 0,1,1,-.5 \
          -flatten  added_waves.png

[IM 输出] [IM 输出] [IM 输出] [IM 输出]

请注意上面我如何用「[-flatten](https://imagemagick.org/command-line-options/#flatten)」运算符配合「[-background](https://imagemagick.org/command-line-options/#background)」设置来实现多图像合成。在这里就是所有给定图像加上背景常数的「偏置和」。

频率调制

把一个函数直接应用到另一个函数的输出上,并不会产生简单的结果。原因在于,所有这些数学函数都作用于单个像素的渐变「值」,而不是作用于像素在渐变中的 x 值。例如…… |

  magick gradient.png  -evaluate sin 0.5 -normalize \
                        -evaluate cos  8  math_cos_var.png

[IM 输出] [IM 输出]
这生成了一个非常复杂的函数,本质上等同于

cos( 8 * sin( _{value}_ /2 ) )

换句话说是一个可变频率,频率随第一条正弦曲线的渐变而变化。基本上,原图像中渐变变化得越快,峰值之间的距离就越小。不过峰值的高度(振幅)不会改变。这实际上就是「频率调制」的工作方式——一个看似简单的函数产生了非常复杂的结果。


建设中

杂项图像变形技法。

以下这些效果尚未提供示例,但都是 IM 开发出来的一些基础变形,
可能会派上用场。如果你有有趣的效果,欢迎贡献。

   将图像像素化
      先把图像缩小到 10,再放大到 10,从而生成
      颜色大致平均化的色块。
      例如……
         magick input.jpg -resize 10% -sample 1000% output.jpg

  对轻微旋转的图像进行倾斜校正

    -deskew {threshold}
       把图像摆正。阈值 40% 对大多数图像都有效。

    使用 -set option:deskew:auto-crop {width} 可以自动裁剪图像。该 set
    参数是图像背景的像素宽度(例如 40)。

    从程序角度看,我们对图像运行中值滤波以消除椒盐噪声,
    从而实现自动裁剪。接着用一个模糊系数(例如 -fuzz 5%)取得
    中值滤波图像的图像边界。最后按该边界裁剪
    原始图像。代码大致如下:

      median_image=MedianFilterImage(image,0.0,exception);
      geometry=GetImageBoundingBox(median_image,exception);
      median_image-DestoryImage(median_image);

      print("  Auto-crop geometry: %lux%lu%+ld%+ld",
                geometry.width,geometry.height, geometry.x,geometry.y);
      crop_image=CropImage(rotate_image,&geometry,exception);

    参见[修剪“带噪”图像](crop.html#trim_blur)

  分割
    请查看以下脚本
       [divide_vert](../static/img/scripts/divide_vert)
       [segment_image](../static/img/scripts/segment_image)
    这些是我编写的一些简单脚本,用于把定义清晰的图像分割成
    更小的部分。我希望能把这样简单的分割函数加入
    核心库,以便实现诸如 GIF 动画的自动细分,以及
    从扫描文档中分离图像与图表之类的功能。