ImageMagick 示例 --
多图像层

索引
ImageMagick 示例前言和索引
层简介
附加图像 (-append)
多个图像对的合成
多个图像的分层
图像分层示例

评估序列多图像合并
平均值最小/最大值中值像素加法乘法
多项式 - 使用多项式合并多图像
将多个图像叠加到一起以生成更大的“合成”图像通常称为使用图像“分层”。这些示例涉及将多个图像“层”组合在一起以生成最终更大更复杂的图像。

图像分层简介

正如我们之前提到的,ImageMagick 不仅处理单个图像,还处理图像序列或列表。这允许您在两种非常特殊的图像处理技术中使用 IM。例如,您可以将列表中的每个图像视为时间中的单个帧,因此整个列表可以被视为一个动画。这将在其他 IM 示例页面中探讨。参见动画基础。或者,您可以将序列中的每个图像视为一组透明投影胶片的图层。也就是说,每个图像都表示最终图像的一小部分。例如:第一层(最底层)可以表示背景图像。在其之上,您可以有一个模糊的透明阴影。然后下一层图像包含投射该阴影的对象。在此之上,有一层带有覆盖该对象的一些文本。也就是说,您可以有一系列图像或“层”,每个层都为更复杂的图像添加一个片段。每个图像层可以独立于任何其他层进行移动、编辑或修改,甚至可以保存到多图像文件(例如 TIFF:、MIFF: 或 XCF:) 或作为单独的图像,以便将来处理。这就是图像分层的意义所在。只有在创建所有图像层后,您才能扁平化马赛克合并所有分层图像到单个最终图像中。

追加图像

追加可能是处理多个图像提供的多图像操作中最简单的。基本上,它将内存中当前的图像序列连接成一列或一行,没有任何间隙。"-append" 选项垂直追加,而加号形式 "+append" 水平追加。例如,这里我们将一组字母图像并排追加在一起,形成一个花哨的单词,类似于“字体”的单个“字形”或字母是如何连接在一起的。

  magick font_A.gif font_P.gif font_P.gif font_E.gif font_N.gif \
          font_D.gif font_E.gif font_D.gif +append  append_row.gif
[IM Output]
以上与字体处理方式类似(以非常基本的方式)。与真实的字体不同,您不仅限于两种颜色,还可以从单个字符图像生成一些非常花哨的彩色字母。许多这些“图像字体”可在 WWW 上下载。在Anthony 的图标库中可以找到一小部分,在文本和计数器的字体中,这也是我找到上述蓝色气泡字体的地方。还要注意 "+append" 操作符是如何作为最后一个操作完成的,在将您要追加的所有图像都添加到当前图像序列之后。这非常适合例如将标签追加到图像上……

  magick rose: -background LawnGreen label:Rose \
          -background white  -append append_label.jpg
[IM Output]
请注意,"-background" 颜色用于填充未填充的任何空间。当然,如果所有图像的宽度都相同,则不会为这种填充留下空间。从 IM v6.4.7-1 开始,可以使用 "-gravity" 设置指定应如何将图像组合在一起。因此,在垂直追加中,'Center' 设置会相对于最终结果图像将图像居中('North' 或 'South' 设置也会这样做)。

  magick rose: -background LawnGreen label:Rose \
          -background white -gravity center -append \
          append_center.jpg
[IM Output]
当然,任何 'East' 重力设置都将图像与右侧对齐。

  magick rose: -background LawnGreen label:Rose \
          -background white -gravity east -append \
          append_east.jpg
[IM Output]
使用 "+append" 时,可以实现类似的垂直对齐。
在 IM v6.4.7 之前,对齐追加图像要困难得多,并且通常涉及使用 "-flop" 进行右侧对齐。或者使用 "-extent" 或 "-border" 调整图像宽度以进行居中对齐的追加。

例如,这将适用于旧版本的 IM 6.3.2……

  magick rose: -background SkyBlue label:Rose \
          -background White -gravity center -extent 200x \
          -append -trim +repage   append_center_old.jpg
[IM Output]
您还可以在同一个命令中使用多个追加操作,而不会对操作的结果产生冲突或混淆(这在 IM v6 之前并非如此)。

  magick font_{0,0,6,1,2}.gif +append  dragon_long.gif \
          -background none   -append   append_multi.gif
[IM Output]
我们将每一行图像追加在一起,然后在下面追加一个更大的图像。这非常简单和直接。通过使用括号,您可以在较大图像之后仅追加数字。例如,这里先将所有数字追加在一起,然后再将它们垂直追加到我们在数字之前读取的龙图像上。

  magick dragon_long.gif  '(' font_{0,0,6,2,9}.gif +append ')' \
          -background none   -append   append_parenthesis.gif
[IM Output]
当与 UNIX shell 一起使用时,上面的括号必须用引号括起来或用反斜杠 ('\') 转义,否则 shell 会将其解释为完全不同的东西。
由于仅涉及两个图像,因此我们可以使用 "+swap" 或 "-reverse" 而不是使用括号。

追加图像数组

您可以进一步操作以创建整个图像数组,并按行或列构建它们。

  magick \( font_1.gif font_2.gif font_3.gif +append \) \
          \( font_4.gif font_5.gif font_6.gif +append \) \
          \( font_7.gif font_8.gif font_9.gif +append \) \
          \( -size 32x32 xc:none  font_0.gif +append \) \
          -background none -append   append_array.gif
[IM Output]
从技术上讲,第一组括号不是必需的,因为还没有读取任何图像,但它使整个内容看起来统一,并显示了命令的意图,即创建图像数组。另请参阅蒙太奇连接模式,了解创建相同大小图像数组的另一种方法。
"-append" 操作符只会追加实际的图像,并且不使用虚拟画布(图像页面)大小或图像偏移量。但是,虚拟画布信息似乎处于一种奇怪的状态,画布大小加在一起,偏移量设置为某个未定义的值。

这可能被视为一个错误,这意味着在保存之前或在这些信息可能变得重要的操作中使用图像之前,应使用 "+repage" 重置输入图像或结果的虚拟画布。

这种情况可能会在操作的未来扩展中得到修复。因此建议谨慎操作,尤其是在重新追加平铺裁剪图像时。

带重叠的追加

在 IM 论坛上,一位用户请求了一种简单的方法来以某种重叠方式追加图像。提供了许多解决方案。这是最简单的解决方案之一,重叠量在一个位置给出。

  magick granite: rose: -gravity east -background none \
          \( -clone 1 -chop 30x0 \) \( -clone 0,2 +append \) \
          -delete 0,2 +swap -composite append_overlap.gif
[IM Output]
上面不需要任何图像定位计算,通常涉及表示更通用解决方案的图像大小。请参阅下面的处理图像层。它所做的是在追加结果到第一个图像之前切掉重叠的部分,从而生成最终图像大小。然后将原始图像(使用重力)合成到顶部以生成实际的重叠。它可以很容易地修改为垂直重叠,甚至可以相对容易地修改为从右到左重叠。

压缩追加

追加图像的另一种方法是压缩。 "-smush" 操作符的工作方式与追加操作符(见上文)非常相似,但它接受一个参数,表示图像之间需要多少空间(或反空间)。例如,让我们用它来更简单地显示前面的示例。

  magick granite: rose: -background none -gravity Center \
          +smush -20 smush_overlap.png
[IM Output]
效果很好,但这并不是操作符的实际设计目的,并且速度可能要慢得多。smush 的实际用途是尽可能地将“形状图像”移动到一起。例如,这里我生成字母“A”和“V”并将其“压缩”在一起,并在它们之间留出尽可能小的空间。

  magick -background none -pointsize 72 \
          -fill red label:A -fill blue label:V \
          +smush 0 smush_append.png
[IM Output]
请注意,两个字母是如何比追加更紧密地追加在一起的,利用了图像“形状”的空隙。上面的间隙是由两个字母的反锯齿边缘像素造成的。也就是说,"-smush" 的设计目的,尽管它需要大量的计算,因此比追加(见上文)慢得多。参数是该最终位置的偏移量,通常是正值以生成间隙,但可以是负值以创建重叠。

  magick -background none -pointsize 72 \
          -fill red label:A -fill blue label:V \
          +smush -15 smush_offset.png
[IM Output]
如果使用非常大的负值,图像可能会以未记录的方式被裁剪。

多个图像对的合成

合成是用于将两个单独的图像合并在一起的低级操作。几乎所有分层技术最终都会分解为一次合并两个图像,直到只剩下一个图像为止。因此,让我们首先了解如何执行图像对的低级合成。

使用合成命令

使用 ImageMagick 将两个图像组合在一起的传统方法是通过 "magick composite" 命令。此命令一次只能组合两个图像,并将每个操作的结果保存到文件中。当然,这并不能阻止您一次一个地使用它来分层多个图像……

  magick -size 100x100 xc:skyblue composite.gif
  magick composite -geometry  +5+10 balloon.gif composite.gif composite.gif
  magick composite -geometry +35+30 medical.gif composite.gif composite.gif
  magick composite -geometry +62+50 present.gif composite.gif composite.gif
  magick composite -geometry +10+55 shading.gif composite.gif composite.gif
[IM Output]
由于 ImageMagick 在打开输出图像之前读取所有输入图像,因此您可以输出到其中一个输入图像。这允许您像上面显示的那样,反复处理同一图像,而不会出现问题。

不要对像“JPEG”这样的有损图像格式执行此操作,因为格式错误是累积的,并且基础图像会很快退化。
您还可以调整叠加图像的大小以及使用 "-geometry" 设置对其进行定位。

  magick -size 100x100 xc:skyblue comp_resize.gif
  magick composite -geometry 40x40+5+10  balloon.gif comp_resize.gif comp_resize.gif
  magick composite -geometry      +35+30 medical.gif comp_resize.gif comp_resize.gif
  magick composite -geometry 24x24+62+50 present.gif comp_resize.gif comp_resize.gif
  magick composite -geometry 16x16+10+55 shading.gif comp_resize.gif comp_resize.gif
[IM Output]
"magick composite" 命令还有一些其他优点,您可以使用 "-compose" 选项控制图像绘制到背景的方式,其相对位置受 "-gravity" 设置影响。您还可以 "-tile" 叠加层,使其仅覆盖背景图像,而无需指定平铺限制。这只有在使用 "magick composite" 时才可用。这种方法的主要缺点是您使用了多个命令,并且 IM 必须将工作图像写入管道或磁盘,以便下一个命令再次读取。要查找更多使用 "magick composite" 命令将图像叠加到其他图像上的示例,请参阅 "通过叠加图像进行注释" 和 "使用重力进行图像定位"。

Convert 的合成运算符

"-composite" 运算符可在 "magick" 命令中使用。有关更多详细信息,请参阅 IM 中的图像合成。这允许您执行与上述相同操作,但所有操作都在一个命令中完成。

  magick -size 100x100 xc:skyblue \
          balloon.gif  -geometry  +5+10  -composite \
          medical.gif  -geometry +35+30  -composite \
          present.gif  -geometry +62+50  -composite \
          shading.gif  -geometry +10+55  -composite \
          compose.gif
[IM Output]
首先创建一个 画布图像,颜色为 "skyblue",然后将后续每个图像分层到该画布上的给定位置。现在 "-geometry" 是一个非常特殊的运算符,它不仅为下一个 "-composite" 操作设置叠加位置,还会 "-resize" 当前图像序列中的最后一个图像(且仅限最后一个图像)。

  magick -size 100x100 xc:skyblue \
          balloon.gif  -geometry 40x40+5+10   -composite \
          medical.gif  -geometry      +35+30  -composite \
          present.gif  -geometry 24x24+62+50  -composite \
          shading.gif  -geometry 16x16+10+55  -composite \
          compose_geometry.gif
[IM Output]
请注意,建议您避免 "-geometry" 的这种“调整大小”副作用,即使它很方便。基本上,因为它更像是向后兼容的效果,并且在某些情况下可能会产生其他影响。以下是更详细的建议...

  magick -size 100x100 xc:skyblue \
          \( balloon.gif -resize 40x40 \) -geometry +5+10   -composite \
          \( medical.gif               \) -geometry +35+30  -composite \
          \( present.gif -resize 24x24 \) -geometry +62+50  -composite \
          \( shading.gif -resize 16x16 \) -geometry +10+55  -composite \
          compose_resize.gif
[IM Output]

绘制多个图像

同样使用 "magick",您还可以使用 绘制图元 将图像叠加到其工作画布上。

  magick -size 100x100 xc:skyblue \
          -draw "image over  5,10 0,0 'balloon.gif'" \
          -draw "image over 35,30 0,0 'medical.gif'" \
          -draw "image over 62,50 0,0 'present.gif'" \
          -draw "image over 10,55 0,0 'shading.gif'" \
          drawn.gif
[IM Output]
当然,您也可以为叠加的图像指定调整大小...

  magick -size 100x100 xc:skyblue \
          -draw "image over  5,10 40,40 'balloon.gif'" \
          -draw "image over 35,30  0,0  'medical.gif'" \
          -draw "image over 62,50 24,24 'present.gif'" \
          -draw "image over 10,55 16,16 'shading.gif'" \
          drawn_resize.gif
[IM Output]
“绘制的”图像也可以在叠加过程中 旋转、缩放和仿射扭曲。尽管这可能难以按照您想要的方式工作。绘制的图像受 "-gravity" 影响,就像文本一样。

分层多个图像

图像的真正分层需要方法将多个图像组合在一起,而无需单独合成每对图像。这就是各种 -layers 运算符方法发挥作用的地方。分层图像的顺序可能很重要,因此了解特殊的 图像序列或列表运算符 是一个好主意。请注意,“分层图像”实际上与处理“动画帧”相同。因此,建议您还查看 动画基础知识动画修改,以了解涉及处理单个“图层”或“帧”的技术。实际上,动画通常使用相同的 -layers 运算符来处理图像。

扁平化 - 到背景图像上

"-layers flatten" 图像列表运算符(或其快捷方式 "-flatten")基本上会将给定的每个图像 "合成" 到背景上,以形成单个图像。但是,图像位置是使用其当前的 虚拟画布或页面 偏移量指定的。例如,这里我创建了一个漂亮的画布,并指定了我想要叠加到该画布上的每个图像。

  magick -size 100x100 xc:skyblue \
          -fill dodgerblue -draw 'circle 50,50 15,25' \
          \( -page +5+10  balloon.gif \)   \( -page +35+30 medical.gif \)  \
          \( -page +62+50 present.gif \)   \( -page +10+55 shading.gif \)  \
          -layers flatten  flatten_canvas.gif
[IM Output]
从 IM v6.3.6-2 开始,"-flatten" 运算符仅是 "-layers 'flatten'" 方法的别名。

因此,"-flatten" 选项可以被视为同名 "-layers" 方法的快捷方式。
您不需要像上面那样创建初始画布,而是可以让 "-flatten" 为您创建一个。画布颜色将是当前的 "-background" 颜色,其大小由第一个图像的 虚拟画布 大小定义。

  magick \( -page 100x100+5+10  balloon.gif \)   \( -page +35+30 medical.gif \)  \
          \( --page +62+50        present.gif \)   \( -page +10+55 shading.gif \)  \
          -background dodgerblue  -layers flatten  flatten_page.gif
[IM Output]
虽然 "-gravity" 设置会影响使用 "-geometry" 设置定义的图像放置,但它不会影响使用 "-page" 设置设置的 虚拟画布偏移量 进行的图像定位。这是此类偏移量定义的一部分。有关更多详细信息,请参阅 几何形状与页面偏移量

如果需要使用 "-gravity" 进行放置,请查看上述多图像合成方法,或查看可以同时处理这两种定位方法的特殊 图层合成 方法。
如果任何图像未出现在定义的虚拟画布区域中,则将根据需要将其裁剪或忽略。例如,这里我们使用了较小的画布大小,导致后面的图像没有完全显示在该画布上。

  magick \( -page 75x75+5+10  balloon.gif \)   \( -page +35+30 medical.gif \)  \
          \( -page +62+50 present.gif \)   \( -page +10+55 shading.gif \)  \
          -background dodgerblue  -flatten  flatten_bounds.gif
[IM Output]
展平 的正常用途是将多个图像“图层”合并在一起。也就是说,您可以生成较大图像的各个部分,通常使用 括号 将图像运算符限制在正在生成的单个“图层”图像上,然后将最终结果展平在一起。例如,一个典型的用途是创建一个 阴影图像 图层,原始图像将在其上展平。例如...

  magick balloon.gif \( +clone  -background navy  -shadow 80x3+5+5 \) +swap \
          -background none   -flatten   flatten_shadow.png
[IM Output]
请注意,因为我希望阴影在原始图像下方,所以我需要 交换 这两个图像并将它们放置在正确的顺序。
不建议使用 展平 添加生成的 阴影图像,因为生成的阴影图像可能具有负图像偏移量。

阴影图像 部分中所述,建议的解决方案是使用稍后将介绍的更高级的 图层合并 技术。
因为 虚拟画布 仅由大小组成,所以生成的图像将具有该大小,但没有虚拟画布偏移量,因此您无需担心最终图像中存在的任何偏移量。这种使用虚拟画布定义叠加图像的画布的方式意味着您可以使用它为图像添加周围边框。例如,这里我设置了图像的大小和虚拟偏移量以“填充”图像到特定大小。

  magick medical.gif -set page 64x64+20+20 \
          -background SkyBlue   -flatten   flatten_padding.gif
[IM Output]
当然,还有更好的方法可以 填充图像,以便 IM 自动将图像居中在更大的区域中。
奇怪的是,完全相同的处理也可以用于“裁剪”或 裁切 图像到小于原始图像的虚拟画布。但是,在这种情况下,您需要使用负偏移量来定位“裁剪”位置,因为您正在偏移图像而不是定位裁剪“窗口”。

  magick logo:  -repage 100x100-190-60  -flatten  flatten_crop.gif
[IM Output]
当然,视口裁剪 也能更好地做到这一点,而无需额外处理画布生成和叠加 "-flatten" 也执行的操作。如果图像仅部分包含在该查看窗口中,它也不会“扩展”图像本身以覆盖整个视口。 "-flatten" 运算符的一个常见误用是 移除图像的透明度。也就是说,去除图像可能具有的任何透明度,但将其叠加在背景颜色上。但是,当涉及多个图像时,此方法将不起作用,因此不再推荐。

马赛克 - 画布扩展

"-layers mosaic" 运算符(或其 "-mosaic" 快捷方式)更像是 展平运算符 的扩展画布版本。与其仅根据初始图像的画布大小创建初始画布,马赛克运算符 会创建一个足够大的画布以容纳所有图像(仅在正方向)。例如,这里我甚至没有设置适当的 虚拟画布,但是 "-mosaic" 运算符将计算出容纳所有图像图层所需的画布大小。

  magick \( -page +5+10  balloon.gif \)   \( -page +35+30 medical.gif \)  \
          \( -page +62+50 present.gif \)   \( -page +10+55 shading.gif \)  \
          -background dodgerblue  -layers mosaic  mosaic.gif
[IM Output]
在 IM v6.3.6-2 上,"-mosaic" 运算符仅是 "-layers 'mosaic'" 的别名。

因此,"-mosaic" 选项可以被视为同名 "-layers" 方法的快捷方式。
请注意,"-mosaic" 和 "-flatten" 仍然会创建一个从“原点”或 0,0 像素开始的画布。这是图像“虚拟画布”或“页面”定义的一部分,因此您可以确保这两个运算符的最终图像都不会有虚拟偏移量,并且整个画布将完全以实际像素数据定义。另请注意,"-mosaic" 仅扩展正方向(底部或右侧边缘)的画布,因为顶部和左侧边缘固定在虚拟原点。当然,这意味着 "-mosaic" 仍然会裁剪具有负偏移量的图像...

  magick \( -page -5-10  balloon.gif \)   \( -page +35+30 medical.gif \)  \
          \( -page +62+50 present.gif \)   \( -page +10+55 shading.gif \)  \
          -background dodgerblue  -mosaic  mosaic_clip.gif
[IM Output]

合并 - 创建新的图层图像

"-layers merge" 运算符几乎与之前的运算符相同,并且是在 IM v6.3.6-2 中添加的。它仅创建一个足够大的画布图像以容纳所有给定图像及其各自的偏移量。像 马赛克 一样,它也会扩展画布,但不仅在正方向,而且在负方向。基本上,这意味着在将图层图像合并在一起时,您无需担心裁剪、偏移或其他方面。所有图像都将相对于彼此的位置合并。输出不包括或确保原点是扩展画布的一部分。因此,图层合并 的输出可能包含“图层偏移量”,该偏移量可能是正数或负数。换句话说..图层合并 合并图层图像以生成新的图层图像。因此,如果您在完成时不希望出现该偏移量,您可能希望在最终保存之前包含 "+repage" 运算符。例如,以下是我们之前使用过的相同图层图像集...

  magick \( -page +5+10  balloon.gif \)   \( -page +35+30 medical.gif \)  \
          \( -page +62+50 present.gif \)   \( -page +10+55 shading.gif \)  \
          -background dodgerblue  -layers merge  +repage layers_merge.gif
[IM Output]
如您所见,图像仅足够大以容纳所有相对于彼此放置的图像,而我丢弃了相对于虚拟画布原点的结果图像偏移量。这种在不裁剪或不使用额外的不需要的空间的情况下保留相对位置的能力是使此变体如此强大的原因。让我们再次尝试一下,为一个图像提供负偏移量...


  magick \( -page -5-10  balloon.gif \)   \( -page +35+30 medical.gif \)  \
          \( -page +62+50 present.gif \)   \( -page +10+55 shading.gif \)  \
          -background dodgerblue  -layers merge  +repage layers_merge_2.gif
[IM Output]
正如您所看到的,“气球”并没有被裁剪,只是被移到了远离其他图像的位置,以保持其与其他图像的相对距离。当然,上面示例中的“+repage”操作符会移除最终图像中的绝对虚拟画布偏移,只保留图像之间相对的位置。偏移被移除是因为网页浏览器通常难以处理图像偏移,尤其是负图像偏移,除非它是GIF动画的一部分。但是,如果我不移除该偏移,所有图像都将保留在其在生成的单层图像的虚拟画布上的正确位置,允许您继续处理并将更多图像添加到合并的图像中。通常,您会使用“-background”颜色“None”,使合并图像的未用区域透明。当应用于单个图像时,图层合并将用纯色背景替换图像中的任何透明度,但会保留图像的原始大小,以及图像中的任何偏移。但是,图像的虚拟画布大小可能会调整为“最佳匹配”该图像的大小和偏移。该操作符最初的目的是允许用户更轻松地将多个失真图像合并成一个统一的整体,而不管各个图像的偏移如何。例如,当对齐照片以形成更大的“全景”时。您可以简单地从一个中心未失真的基础图像(没有偏移)开始,并使用此操作符将其他图像叠加到该起始点周围(使用负或正偏移),这些图像已对齐并失真以匹配该中心图像。有关通过失真图像对齐公共控制点来使用此操作符的其他示例,请参阅3D等距照片立方体3D透视盒。使用此操作符的其他示例是生成一系列简单的重叠照片
The operation "-layers trim-bounds" can be used to ensure all
images get a positive offset on a minimal canvas size, while retaining there
relative positions, and without actually layer merging the images into one
final image.

This lets you then perform further processing of the images before they are
actually merged, such as placing more images relative to the that image group
but looking up the resulting virtual canvas bounds.

However if images have a transparency, it is probably a good idea to trim
that transparency from images first, making the ideal usage...

  -alpha set -bordercolor none -border 1x1 -trim -layers trim-bounds

This minimizes the image layers including any and all transparent areas of
actual image data, while ensuring everything is contained on a valid
virtual (positive) canvas of minimal size.

合并合成 - 渐进分层

-layers coalesce”图像操作符(或其“-coalesce”快捷方式)实际上是为将GIF动画转换为一系列图像而设计的。例如,有关详细信息,请参阅合并动画。但是,它与“-flatten”密切相关,并且在这方面对多层图像具有非常有用的效果。
例如,对单个图像使用合并,将执行与使用扁平化和“-background”颜色“None”或“Transparency”完全相同的工作。也就是说,它将用透明像素“填充”图像的画布。

  magick \( -page 100x100+5+10 balloon.gif \) -layers coalesce  coalesce_canvas.gif
[IM Output]
在处理由多个图层组成的图像时,可以使用合并生成图像的“渐进分层”。但是要做到这一点,我们需要采取一些预防措施,以禁用操作符的任何“GIF动画”处理。

   magick \( -page 100x100+5+10 balloon.gif \)   \( -page +35+30 medical.gif \)  \
           \( --page +62+50       present.gif \)   \( -page +10+55 shading.gif \)  \
           -set dispose None  -coalesce  miff:- |\
     montage - -frame 4 -tile x1 -geometry +2+2 \
             -background none -bordercolor none  coalesce_none.gif
[IM Output]
在上面,我们“-set”所有“-dispose”设置到“None”。这有效地告诉“-coalesce”只需将每个帧叠加到先前叠加结果的顶部。结果是第一个图像只是图像画布的“填充”,带有透明背景。下一个图像是带有该图层叠加的先前图像。以此类推。图像序列的“渐进”扁平化。因此,序列中的最后一个图像将与使用透明背景进行正常的“-flatten”相同。如果您使用了“-dispose”设置“Background”,则可以获得完全不同的效果。在这种情况下,“-coalesce”只会“填充”每个图像的画布,就好像它们是完全独立的图像一样!

  magick \( -page 100x100+5+10 balloon.gif \)   \( -page +35+30 medical.gif \)  \
          \( --page +62+50       present.gif \)   \( -page +10+55 shading.gif \)  \
          -set dispose Background  -coalesce  miff:- |\
    montage - -frame 4 -tile x1 -geometry +2+2 \
            -background none -bordercolor none  coalesce_bgnd.gif
[IM Output]
但是请注意,与扁平化马赛克合并不同,“-coalesce”操作符不会使用当前的“-compose” Alpha合成设置。它仅使用“Over”合成方法,因为这是GIF动画处理所需的。在下一组示例中,我们将介绍如何使用不同的“-compose”方法与更标准的图像分层操作符。

合成方法和分层

三种分层方法:扁平化马赛克合并;将使用“-compose”设置来确定用于依次叠加每个图像的合成方法。因此,您可以将这些函数视为一个多图像“-composite”操作符,它能够设置指定颜色的初始“-background”画布。但是,使用除默认的Alpha合成Over”之外的任何内容都需要在应用之前进行思考,否则您将得到意外的结果。您可能还需要考虑“-background”颜色对这些操作符用于生成起始画布的影响,每个图像(包括第一个)都在该画布上进行合成。例如,让我们将每个后续图像放置在先前图像的下方,使用“DstOver”...

  magick \( -page 100x100+5+10 balloon.gif \)   \( -page +35+30 medical.gif \)  \
          \( --page +62+50       present.gif \)   \( -page +10+55 shading.gif \)  \
          -background none  -compose DstOver  -flatten  flatten_dstover.gif
[IM Output]
这里背景被设置为透明,否则您只会看到结果中的背景画布,因为所有其他图像都将被放置在此初始画布的“下方”!这确实提供了一种用特定颜色“清空”图像的方法,如调整大小到现有图像的画布中所示。这是一个更实用的例子。而不是首先将图像与背景画布分层,这在某些图像处理情况下很笨拙且不自然,您可以仅从上到下或从前景到背景的顺序生成图像。

  magick rose: -repage +10+10 \
          \( +clone -background black -shadow 60x3+5+5 \) \
          \( granite: -crop 100x80+0+0 +repage \) \
          -background none  -compose DstOver -layers merge layer_dstover.gif
[IM Output]
前三行中的每一行都生成一个图层图像,最后一行将所有图层合并到先前图层的下方,有效地反转了顺序。

正如您所看到的,上面图像处理比您通常在阴影生成中看到的更简单、更清晰,只需按顺序(使用透明起始画布)将每个图像下移。

当然,我也可以很容易地反转图像列表。

  magick rose: -repage +10+10 \
          \( +clone -background black -shadow 60x3+5+5 \) \
          \( granite: -crop 100x80+0+0 +repage \) \
          -reverse -layers merge layer_reverse.gif
[IM Output]
但是请记住,这只会重新排序现有图像,而不会影响分层方法创建的“起始背景画布”。合成方法也可用于产生一些有趣的效果。例如,如果您绘制三个圆圈,然后使用“Xor”合成方法将它们叠加,则您将获得一个不寻常且复杂的符号,并且工作量最少。

  magick -size 60x60 \
          \( xc:none -fill blue   -draw 'circle 21,39 24,57' \) \
          \( xc:none -fill red    -draw 'circle 39,39 36,57' \) \
          \( xc:none -fill green  -draw 'circle 30,21 30,3'  \) \
          -background none  -compose Xor   -flatten  flatten_xor.png
[IM Output]

图层合成 - 合并两个图层列表

使用IM v6.3.3-7,“-layers”方法“Composite”被添加,允许您将两个完全独立的图像集组合在一起。要在命令行上执行此操作,需要一个特殊的“null:”标记图像来定义第一个目标图像列表在哪里结束以及叠加的图像列表在哪里开始。但这是此方法唯一真正的复杂之处。基本上,第一个列表中的每个图像都与第二个列表中的对应图像合成,有效地将这两个列表合并在一起。第二个列表可以使用几何偏移相对于第一个列表全局定位,就像使用正常的合成操作符一样(见上文)。重力也使用第一个图像的画布大小来进行计算。在此“全局偏移”之上,还保留了图像的各个虚拟偏移,因为每对图像都合成在一起。还处理了一种特殊情况。如果其中一个图像列表仅包含一个图像,则该图像将与另一个列表中的所有图像合成。此外,在这种情况下,将保留较大列表的图像元数据(例如动画时间),即使它不是合成的目标端。
此分层操作符通常用于合成两个动画时,可以将其视为一种时间分层图像列表。因此,它在示例的动画修改部分中得到了更好的示例。因此,有关更多详细信息,请参阅多图像 Alpha合成

处理图像图层

使用上面各种图层操作符叠加多个图像是一种非常通用的技术。它允许您分别处理大量图像,然后在完成后将它们全部组合成一个统一的整体。到目前为止,我们已经展示了以多种不同方式合并(合成或分层)多个图像的各种方法。在这里,我提供了一些关于如何使用这些技术的更实用的例子。

缩略图的分层

您还可以使用此技术以各种复杂的方式合并多个缩略图。在这里,我添加了柔和边缘到图像中,当您阅读和定位它们时,您可以在平铺画布上生成相当不错的图像组合。

  magick -page +5+5    holocaust_tn.gif \
          -page +80+50  spiral_stairs_tn.gif \
          -page +40+105 chinese_chess_tn.gif \
          +page \
          -alpha Set -virtual-pixel transparent \
          -channel A -blur 0x10  -level 50,100% +channel \
          \( -size 200x200 tile:tile_fabric.gif -alpha Set \) -insert 0 \
          -background None -flatten  overlap_canvas.jpg
[IM Output]

图像的计算定位。

可以在多种方式中设置虚拟画布偏移(页面)。更具体地说,您可以“-set”设置每个图像的属性,甚至可以为每个图像计算不同的位置。例如,这里我读取了一组大型图像(所有相同大小的小图标图像)并将它们排列成一个圆圈。


  magick {balloon,castle,eye,eyeguy,ghost,hand_point,medical}.gif \
          {news,noseguy,paint_brush,pencil,present,recycle}.gif \
          {shading,skull,snowman,storm,terminal,tree}.gif \
          \
          -set page '+%[fx:80*cos((t/n)*2*pi)]+%[fx:80*sin((t/n)*2*pi)]' \
          \
          -background none -layers merge +repage image_circle.png
[IM Output]
上面示例的关键在于“-set page”操作,它使用标准化的图像索引(FX表达式 't/n')为每个单独的图像创建一个从 0.0 到略小于 1.0 的值。然后将此值映射到使用FX表达式作为百分比转义在半径为 80 像素的圆圈中定位图像(通过角度)。计算出的位置是图像的左上角(而不是其中心,尽管这只是一个简单的调整),然后合并以生成新图像。定位不考虑偏移量是正数还是负数,这是合并图层运算符的功能。也就是说,我们生成了一个所有图像彼此相对的新图像。最终的“+repage”删除了合并图层图像的最终结果负偏移量,因为这不再需要并且在查看结果图像时可能会导致问题。请注意,第一个图像(结果中最右边的图像)位于其他每个图像的下方。如果您希望分层真正循环,以便最后一个图像位于第一个图像下方,您可能需要将第一个图像分成两半,并将上半部分放在序列的末尾,以便第一个图像的上半部分覆盖最后一个图像,而下半部分保持在第二个图像下方。这种技术功能强大,但它只能将图像定位到整数偏移量。如果您需要更精确的图像亚像素定位,则需要将图像扭曲(平移)到精确的亚像素位置,而不仅仅是调整其虚拟偏移量。

增量计算的位置

在设置图像属性时,您可以使用 FX 表达式访问其他图像的一些图像属性。这意味着您可以设置每个图像的位置,相对于先前图像的计算位置。例如,这将每个图像的位置设置为前一个图像的右侧。也就是说,前一个图像的位置加上其宽度。

  magick rose: netscape: granite: \
          \
          +repage -set page '+%[fx:u[t-1]page.x+u[t-1].w]+0' \
          \
          -background none -layers merge +repage append_diy.png
[IM Output]
每个图像都附加到前一个图像的位置,方法是查找该位置并添加该图像的宽度。这个先前的位置实际上刚刚计算出来,因为 IM 循环遍历每个图像并设置“page”(虚拟偏移量)属性。结果是 DIY 追加运算符 等效项,并且您可以从中开发自己的变体。您应该注意,整个序列实际上由在第一个图像的位置计算期间设置的“u[-1].w”移动。这应该是当前图像序列中最后一个图像的宽度。但是,该整体位移由最终的“+repage”丢弃。您可以使用一些额外的计算来忽略此偏移量,但在上面不需要。
当使用图像索引(如“u[t]”)时,所有图像选择器“u”、“v”和“s”都引用相同的图像,根据给定的“[index]”。因此,最好使用“u”(第一个或第零个图像)作为此索引行为的助记符(以及在发生更改时)。

有关更多信息,请参阅FX,DIY 图像运算符
以下是一个示例。每个图像相对于前一个图像偏移,使用该图像的位置和宽度,以便计算重叠追加

magick font_[0-9].gif \
        -set page '+%[fx:u[t-1]page.x+u[t-1].w-8]+%[fx:u[t-1]page.y+4]' \
        -background none -layers merge +repage append_offset.gif
[IM Output]
访问其他图像的属性的能力还包括其他图像的像素数据。这意味着您可以创建一个特殊图像,其中颜色值表示其他图像的“映射位置”。当然,该“映射”图像也将被定位,并且需要在执行覆盖之前将其移除。创建特殊“映射位置”图像是否有用是另一回事。这只是另一种可能性。

图像的两阶段定位

您可以简化图像处理,将其分成两个步骤。一个步骤可用于生成、扭曲、定位和向图像添加内容,最后一步将它们全部合并在一起。例如,让我们从照片商店中较大的原始图像创建宝丽来缩略图,分别处理每个图像(保持该方面独立且简单)。

  center=0   # Start position of the center of the first image.
             # This can be ANYTHING, as only relative changes are important.

  for image in ../img_photos/[a-m]*_orig.jpg
  do

    # Add 70 to the previous images relative offset to add to each image
    #
    center=`magick xc: -format "%[fx: $center +70 ]" info:`

    # read image, add fluff, and using centered padding/trim locate the
    # center of the image at the next location (relative to the last).
    #
    magick -size 500x500 "$image" -thumbnail 240x240 \
            -set caption '%t' -bordercolor Lavender -background black \
            -pointsize 12  -density 96x96  +polaroid  -resize 30% \
            -gravity center -background None -extent 100x100 -trim \
            -repage +${center}+0\!    MIFF:-

  done |
    # read pipeline of positioned images, and merge together
    magick -background skyblue   MIFF:-  -layers merge +repage \
            -bordercolor skyblue -border 3x3   overlapped_polaroids.jpg

[IM Output]
上面的脚本看起来很复杂,但实际上并非如此。它只是在循环中生成每个缩略图图像,同时使用Extent进行居中填充(使用)并裁剪每个图像,以便图像的“中心”位于虚拟画布上的已知位置。它实际上可以计算该位置,尽管这可能需要临时文件,因此最好确保它对于所有图像都位于已知位置。然后平移图像(使用相对的“-repage”运算符,请参阅画布偏移量),以便生成的每个图像都恰好位于前一个图像右侧 60 像素。也就是说,每个图像的中心都相隔固定距离,而不管图像的实际大小如何,由于纵横比和旋转,图像的大小可能已发生变化。此脚本的另一个主要技巧是,您无需将每个“图层图像”保存到临时文件中,而只需使用MIFF:文件格式将图像写入管道。一种称为MIFF 图像流的方法。这之所以有效,是因为“MIFF:”文件格式允许您将多个图像简单地连接到单个数据流中,同时保留所有图像元数据,例如其虚拟画布偏移量。此技术为许多其他脚本提供了一个良好的起点。可以生成或修改图像,并且可以根据需要以任何方式计算最终大小和位置。另一个示例是脚本“hsl_named_colors”,它获取在 ImageMagick 中找到的命名颜色的列表,并将它们排序到 HSL 色彩空间中这些颜色的图表中。您可以在颜色规范中查看其输出。其他可能性包括...
  • 使用任何类型的缩略图(或其他内容),或者只是直接使用原始的小缩略图。
  • 生成图像,使第一个图像居中,其他图像排列在第一个图像的左右下方,就像金字塔一样。
  • 通过将图像放置在彼此相对的特定 X 和 Y 坐标上,将图像定位到弧线、圆圈和螺旋线上。例如:PhD 圆圈日落花斐波那契螺旋
  • 根据图像的颜色定位图像。例如:书籍封面
  • 按一天中的时间或提交时间定位图像。例如:日落之年
基本上,您可以在虚拟画布上自由定位图像,然后可以简单地让 IM 确定容纳所有图像所需的最终画布大小。

地图上的图钉

这是一个典型的分层示例,在特定位置的地图上放置彩色图钉。[IM 输出] 左侧是“图钉”图像。图钉的末端位于位置+18+41。我还有一个威尼斯地图的图像,并且想要在地图上的各个点放置图钉。例如,“学院美术馆”位于像素位置+160+283。要使图钉与该位置对齐,您需要从地图位置减去图钉末端的位置。这为我们的“图钉”图像产生了+142+242的偏移量。以下是使用分层图像的结果

  magick map_venice.jpg    -page +142+242 push_pin.png \
          -flatten  map_push_pin.jpg
[IM Output]
此示例来自 IM 论坛讨论使用 Convert 分层图像让我们进一步自动化此过程。我们有一个文件列出了我们想要在地图上放置的每个图钉的位置和颜色。文件中的位置名称未使用,只是对列出的像素位置的参考注释。
[Data File]
让我们读取此文本文件,以便在循环中创建“图钉”。


  pin_x=18  pin_y=41

  cat map_venice_pins.txt |\
    while read x y color location; do

      [ "X$x" = "X#" ] && continue   # skip comments in data

      x=$(( x - pin_x ))    # magick x,y to pin image offsets
      y=$(( y - pin_y ))

      # magick 'color' to settings for color modulate (hue only)
      # assumes a pure 'red' color for the original push pin
      mod_args=$(
         magick xc:$color -colorspace HSL txt: |
           tr -sc '0-9\012' ' ' |\
             awk 'NR==1 { depth=$3 }
                  NR==2 { hue=$3;
                          print  "100,100,"  100+200*hue/depth
                        }'; )

      # re-color and position the push pin
      magick push_pin.png -repage +${x}+${y} -modulate $mod_args miff:-

    done |\
      # read pipeline of positioned images, and merge together
      magick map_venice.jpg  MIFF:-  -flatten  map_venice_pins.jpg

[IM Output]
请注意,它假设原始图钉颜色为红色(色相为 0),并使用调制运算符将其重新着色为其他颜色,并进行相应的缩放计算。请注意,用于无操作色相更改的调制参数为 100,它在 200 的值上循环(一种伪百分比值)。未来:透视扭曲地图,调整图钉大小以适应地图上的“深度”,计算由于扭曲引起的图钉位置变化,并将其“钉”到扭曲的地图上。上面使用了称为MIFF 图像流的方法,每个图像都在循环中单独生成,然后“管道化”到“分层”命令以生成最终图像。另一种方法(通常在 PHP 脚本中使用)是使用“生成命令”技术,该技术使用 shell 脚本生成要运行的长“magick”命令。图像扭曲动画中的脚本使用此技术。这两种方法都避免了生成临时图像的需要。

阴影层

在重叠图像集中正确处理半透明阴影效果实际上比看起来要困难得多。仅将照片与阴影叠加会导致阴影应用两次。也就是说,两个重叠的阴影变得非常暗,而在现实中,它们不会像重叠图像那样以完全相同的方式重叠。图像的各个部分应该只是阴影或没有阴影。也就是说,阴影应该只应用一次到图像的任何部分。除非您有两个独立的光源,否则您不应该获得更暗的区域,这会使事情变得更加困难。Tomas Zathurecky < tom @ ksp.sk > 接受了处理分层图像中阴影效果的挑战,并开发了图像累加器技术来解决此问题。基本上,我们需要一次将每个图像添加到堆栈的底部。当我们添加新图像时,所有先前图像的阴影都需要使新图像变暗,然后才能将其添加到堆栈中。但是,只需要添加落在新图像上的阴影。不落在新图像上的阴影需要忽略,直到以后落在其他图像或背景(如果有)上。这是一个示例...

  magick \
    \( holocaust_tn.gif -frame 10x10+3+3 \
          -background none  -rotate 5 -repage +0+0 \) \
    \
    \( spiral_stairs_tn.gif -frame 10x10+3+3 \
          -background none -rotate -15 -repage -90+60 \) \
    \( -clone 0   -background black -shadow 70x3+4+7 \
       -clone 1   -background black -compose DstATop -layers merge \
       -trim \) \
    \( -clone 2,0 -background none  -compose Over -layers merge \) \
    -delete 0--2 \
    \
    \( chinese_chess_tn.gif -frame 10x10+3+3 \
          -background none -rotate 20 -repage +60+90 \) \
    \( -clone 0   -background black -shadow 70x3+4+7 \
       -clone 1   -background black -compose DstATop -layers merge \
       -trim \) \
    \( -clone 2,0 -background none  -compose Over -layers merge \) \
    -delete 0--2 \
    \
    \( +clone -background black -shadow 70x3+4+7 \) +swap \
    -background none -compose Over -layers merge +repage \
    layers_of_shadows.png
[IM Output]
上面的程序看起来很复杂,但实际上非常简单。第一张图像用于启动一个累积的图像堆栈(图像索引 #0)。请注意,如果不想使用第一张图像来初始化堆栈,我们可以实际从单个透明像素(“-size 1x1 xc:none”)开始。现在,要将新图像添加到图像堆栈的底部,我们每次都应用相同的操作集...
  • 首先将缩略图图像读入内存,并应用任何旋转、相对位置(可能是负数)。如果需要,您也可以在此处对图像应用其他缩略图操作,尽管在本例中,这些操作已经执行过了。新图像形成图像索引 #1。

  • 现在我们获取之前的图像堆栈(#0),并生成一个具有适当颜色、模糊度、偏移量和环境光百分比的阴影。
  • 此阴影叠加在新图像(#1)上,因此只保留落在新图像“ATop”上的阴影。我们还可以(可选)对结果应用裁剪操作,以去除阴影操作添加的任何额外空间,从而形成图像 #2。
  • 现在我们只需将新图像(#2)添加到累积的图像堆栈(#0)中。
  • 并删除所有以前的中间图像,除了最后一个。
要添加更多图像,我们基本上只需重复上述操作块。在所有图像都添加到堆栈后,只需对累积的图像堆栈执行正常的阴影操作即可。删除任何剩余的图像偏移(许多网页浏览器都不喜欢)。使用合并,我可以自动处理虚拟偏移,特别是负偏移,允许您简单地将图像放置在相对于先前图像位置的任何位置。它还可以正确应用会生成具有负偏移量的较大图像的阴影。
现在,以上方法可以正确处理多层图像阴影,但在阴影偏移时,实际上所有图像的偏移量都相同!真正应该发生的是,随着阴影落在越来越深的图像上,阴影应该变得更加偏移,也更加模糊。也就是说,顶部的图像应该在背景上产生非常模糊的阴影,而最底部的图像则不然。这实际上更难做到,因为您不仅需要跟踪图像堆栈,还需要跟踪随着图像堆栈变大,阴影变得“模糊”的程度。因此,您确实需要两个累加器。图像堆栈(如上所述)和阴影累加,当我们添加更多图像时。例如,以下是一组相同的图像,但阴影随着深度变得更加模糊。

  magick xc:none xc:none \
    \
    \( holocaust_tn.gif -frame 10x10+3+3 \
          -background none  -rotate 5 -repage +0+0 \) \
    \( -clone 1   -background black -shadow 70x0+0+0 \
       -clone 2   -background black -compose DstATop -layers merge \
       -clone 0   -background none  -compose Over    -layers merge \) \
    \( -clone 2,1 -background none  -compose Over    -layers merge \
                  -background black -shadow 100x2+4+7 \) \
    -delete 0-2 \
    \
    \( spiral_stairs_tn.gif -frame 10x10+3+3 \
          -background none -rotate -15 -repage -90+60 \) \
    \( -clone 1   -background black -shadow 70x0+0+0 \
       -clone 2   -background black -compose DstATop -layers merge \
       -clone 0   -background none  -compose Over    -layers merge \) \
    \( -clone 2,1 -background none  -compose Over    -layers merge \
                  -background black -shadow 100x2+4+7 \) \
    -delete 0-2 \
    \
    \( chinese_chess_tn.gif -frame 10x10+3+3 \
          -background none -rotate 20 -repage +60+90 \) \
    \( -clone 1   -background black -shadow 70x0+0+0 \
       -clone 2   -background black -compose DstATop -layers merge \
       -clone 0   -background none  -compose Over    -layers merge \) \
    \( -clone 2,1 -background none  -compose Over    -layers merge \
                  -background black -shadow 100x2+4+7 \) \
    -delete 0-2 \
    \
    \( -clone 1 -background black -shadow 70x0+0+0 \
       -clone 0 -background none -compose Over -layers merge \) \
    -delete 0-1 -trim +repage \
    layers_of_deep_shadows.png
[IM Output]
仔细观察结果。阴影的偏移量和模糊度在图像的不同部分是不同的。在相邻图层之间的图像之间非常薄,但在落在图像上时或甚至更深处的背景上非常厚。当然,在这个例子中,阴影偏移可能太大,但结果看起来非常逼真,给图层带来了更好的深度感。请注意我们如何将阴影操作分成两个步骤。当将累积的阴影(图像索引 #1)应用于新图像(#2)时,我们只添加环境光百分比,没有任何模糊或偏移(在这种情况下为“70x0+0+0”)。然后将新图像添加到累积的图像堆栈(#0)中。但在将新图像(#2)的阴影直接添加到累积的阴影(#1)之后,同样没有模糊或偏移,然后我们才模糊和偏移所有阴影,以形成新的累积阴影图像。换句话说,随着堆栈越来越厚,累积的阴影图像变得越来越模糊和偏移。只有较深图像的阴影没有累积太多效果。此程序本质上将阴影的应用与增量的阴影累加器分离。这允许您控制诸如……
  • 逼真的阴影(如上所示):70x0+0+0 和 100x2+4+7
  • 恒定阴影(作为基本示例):70x2+4+7 和 100x0+0+0
  • 恒定模糊,但累积偏移:70x2+0+0 和 100x0+4+7
  • 恒定和渐进偏移:60x0+4+7 和 100x0+1+1
  • 累积环境光效果:80x0+0+0 和 95x2+4+7
它们中的大多数可能不切实际,但在其他情况下可能看起来不错。此外,在“-compose ATOP”合成之前设置“-background”颜色将允许您定义阴影的颜色(实际上是彩色环境光)。您甚至可以使用不同的颜色来表示最终落在最终背景层上的阴影(最后的“-background black”设置),或者完全将其关闭以使其看起来像图像根本没有位于任何背景之上(即悬浮在空中)。它非常通用。
Tomas Zathurecky 继续开发了另一种处理分层图像阴影的方法,通过将分层图像列表作为一个整体进行处理。我自己以前从未想过这种方法。这种方法的优点是可以将整个图像列表作为一个整体进行处理,而不必一次累积一个图像,并一遍又一遍地重复相同的操作块。首先,让我们再次看看更简单的“恒定阴影”问题。

  magick \
    \( holocaust_tn.gif -frame 10x10+3+3 \
          -background none  -rotate 5 -repage +0+0 \) \
    \( spiral_stairs_tn.gif -frame 10x10+3+3 \
          -background none -rotate -15 -repage -90+60 \) \
    \( chinese_chess_tn.gif -frame 10x10+3+3 \
          -background none -rotate 20 -repage +60+90 \) \
    \
    -layers trim-bounds \
    \
    \( -clone 0--1 -dispose None -coalesce \
       -background black -shadow 70x2+4+7 \
       xc:none +insert null: +insert +insert xc:none \) \
    -layers trim-bounds -compose Atop -layers composite \
    \
    -fuzz 10% -trim \
    -reverse -background none -compose Over -layers merge +repage \
    coalesced_shadows.png
[IM Output]
第一组操作符只是生成分层图像列表。它可以是单独的程序循环,如前所示。然后,操作以“-layers trim-bounds”开始,这是一个边界裁剪操作,它扩展所有图像的虚拟画布以包含所有图像,并确保所有偏移量都为正。然后将其克隆、合并并进行阴影处理,以创建单独的渐进阴影列表。现在,我们可以使用图层合成将阴影和原始图像列表合并在一起。这里的问题是,在合并之前,我们不仅需要添加一个特殊的“null:”标记图像来分隔这两个列表,还需要添加一个特殊的空白图像“xc:none”来偏移阴影列表。这样每个阴影图像将“ATop”叠加在原始列表的下一个图像上。剩下的就是从下到上(反转)顺序合并现在已正确阴影的图像。
处理“深阴影”需要图层计算

  magick \
    \( holocaust_tn.gif -frame 10x10+3+3 \
          -background none  -rotate 5 -repage +0+0 \) \
    \( spiral_stairs_tn.gif -frame 10x10+3+3 \
          -background none -rotate -15 -repage -90+60 \) \
    \( chinese_chess_tn.gif -frame 10x10+3+3 \
          -background none -rotate 20 -repage +60+90 \) \
    \
    \( -clone 0--1 \
       -set page '+%[fx:page.x-4*t]+%[fx:page.y-7*t]' -layers merge \) \
    -layers trim-bounds +delete \
    \
    \( -clone 0--1 \
       -set page '+%[fx:page.x-4*t]+%[fx:page.y-7*t]' \
            -dispose None -coalesce \
       -set page '+%[fx:page.x+4*t]+%[fx:page.y+7*t]' \
            -background black -shadow 70x2+4+7 \
       xc:none +insert null: +insert +insert xc:none \) \
    -layers trim-bounds -compose Atop -layers composite \
    \
    -fuzz 10% -trim \
    -reverse -background none -compose Over -layers merge +repage \
    coalesced_deep_shadows.png
[IM Output]
您可以看到与以前使用的相同的一组块,但使用更复杂的计算来设置初始边界裁剪,并在稍后计算“渐进阴影列表”所需的偏移量。但是,阴影目前不会随着深度而变得更模糊。
使用 IMv7 的“magick”命令,以上操作将变得简单得多,这将允许您直接在“-shadow”的参数中使用“fx 计算”,这不仅可以让您根据深度计算更大的阴影偏移量,还可以让您根据深度使阴影更加模糊。

定位扭曲透视图像

对齐扭曲的图像可能很棘手,这里我将研究如何对齐此类图像以匹配非常特定的位置。这里我有两张图像,突出显示每张图像上的特定点。
[IM Output] [IM Output]
第二张图像是 65% 半透明的,这允许您在将其合成到蓝色图像上时透视它,以便您可以查看标记点是否对齐。标记的控制点本身分别位于坐标 59,26(蓝色)和 35,14(红色)。如果您只是将两张图像叠加,则只需减去偏移量并“合成”这两张图像,产生 +24+12 的偏移量。

  magick align_blue.png align_red.png -geometry +24+12 \
          -composite align_composite.png
[IM Output]
请注意,此偏移量可能是负数!这是我们很快将要处理的内容。这仅在坐标是整数像素坐标时才有效。如果匹配坐标是子像素位置(在照片蒙太奇中通常是这种情况),则简单的合成将不起作用。如果涉及任何类型的失真(对于现实生活中的图像也很常见),它也不会很好地工作。这就是我们将要探讨的问题。
扭曲图像时,您需要确保这两个像素保持对齐。最好的方法是使用您想要对齐的点作为扭曲控制点。这将确保它们被正确放置。

  magick align_blue.png \
          \( align_red.png -alpha set -virtual-pixel transparent \
             +distort SRT '35.5,14.5  1 75  59.5,26.5' \
          \) -flatten  align_rotate.png
[IM Output]
由于扭曲会生成具有“画布偏移”的“图层图像”,因此您不能简单地使用合成来叠加图像(级别太低),而是需要使用扁平化操作符,以便它使用扭曲生成的偏移量来定位它们。请注意,我还向“像素”坐标添加了 0.5 的值。这是因为像素具有面积,而数学点则没有,因此,如果您想对齐像素的中心,则需要在像素内中心的“点”位置添加 0.5。有关更多信息,请参阅图像坐标与像素坐标。上述问题的另一个问题是叠加的图像被蓝色背景画布图像“剪裁”,就像合成操作符所做的那样。也就是说,可以说“蓝色”图像在合成过程中为结果提供了“剪裁视口”。为了防止这种情况,我们使用图层合并,它会自动计算一个足够大的“视口”画布,以容纳所有组合在一起的图像。

  magick align_blue.png \
          \( align_red.png -alpha set -virtual-pixel transparent \
             +distort SRT '35.5,14.5  1 75  59.5,26.5' \
          \) -background none -layers merge +repage  align_rotate_merge.png
[IM Output]
由于“合并”的结果,图像将具有“负”偏移量(以便保留图像的图层位置)。为了显示结果,我需要丢弃该偏移量,因为许多浏览器无法处理图像中的负偏移量。我在保存最终图像之前使用“+repage”来执行此操作。如果我要进行进一步处理(无需在网络上显示结果),我会保留该偏移量(删除“+repage”),以便图像位置保留在正确且已知的位置以供以后处理。
现在,如果要进行更复杂的扭曲(例如透视),则上面显示的相同技术也适用。

  magick align_blue.png \
          \( align_red.png -alpha set -virtual-pixel transparent \
             +distort Perspective '35.5,14.5  59.5,26.5
                       0,0 32,4    0,%h 14,36    %w,%h 72,53  ' \
          \) -background none -layers merge +repage  align_perspective.png
[IM Output]
此技术的难题在于,您使用内部控制点来定位透视扭曲。也就是说,图像内部的一个点和边缘周围的 3 个点。这可能难以控制实际的透视形状,因为任何控制点的微小移动都可能导致“自由角”大幅移动。如果您使用大量“已注册点”来获得更精确的“最小二乘拟合”来定位图像,则这种情况甚至更糟。在这种情况下,您感兴趣的点可能不在用于扭曲图像的任何控制“已注册”点附近。另一种方法是简单地按照我们需要的方式扭曲图像,然后确定我们需要如何平移生成的图像以对齐我们感兴趣的点。为了使此方法有效,我们需要知道“感兴趣点”由于扭曲而移动的方式。这是扭曲和定位图像(尤其是现实生活中的图像)的真正问题。例如,这里我使用所有四个角扭曲图像以生成特定的(据称是所需的)扭曲形状,但此时我不会尝试对齐控制点,只应用扭曲……

  magick align_blue.png \
          \( align_red.png -alpha set -virtual-pixel transparent \
             +distort Perspective '0,0  10,12  0,%h 14,40
                               %w,0 68,6  %w,%h 63,48 ' \
          \) -background none -layers merge +repage  align_persp_shape.png
[IM Output]
如您所见,虽然红色图像被扭曲了,但红色控制点的位置与我们要对齐的蓝色控制点相差甚远。您不能仅仅测量这两个点,因为红色点不太可能位于精确的像素位置,而是会涉及子像素偏移。我们需要首先计算红色点的确切位置。为此,我们可以重新运行上面启用了详细模式的扭曲以获取透视正向映射系数。然后,可以根据透视投影扭曲中所述计算这些系数。

  magick align_red.png  -define distort:viewport=1x1  -verbose \
          +distort Perspective '0,0  10,12  0,%h 14,40
                                %w,0 68,6  %w,%h 63,48 ' null:
[IM Text]
我们想要的只是扭曲使用的计算系数。因此,我们不需要目标图像,因此我们只使用“null:”图像文件格式输出。我们还告诉扭曲它正在生成的新的图像仅为一个像素大小,使用扭曲视口。这样,它会执行扭曲准备和详细报告,然后只扭曲单个“目标”像素,然后将其丢弃。这可以节省大量处理时间。实际上,如果扭曲没有使用源图像元数据(用于百分比转义“%w”和“%h”)作为其计算的一部分,我们甚至不需要源图像“align_red.png”。在这种情况下,我们也可以对输入图像使用单个像素“null:”图像。对于此信息收集步骤,我们实际上并不关心虚拟像素、背景或任何其他内容,因此我们无需担心设置这些功能。


现在我们可以获取失真信息,我们需要从输出的第 3 行和第 4 行提取 8 个透视系数。然后可以使用这些系数将红色控制点映射到其新的失真位置,并从中减去蓝色控制点,以便获得所需的实际平移量,以使标记的红色坐标与蓝色坐标对齐。

  bluex=59; bluey=26
  redx=35; redy=14

  magick align_red.png  -verbose \
             +distort Perspective '0,0  10,12  0,%h 14,40
                               %w,0 68,6  %w,%h 63,48 ' null: 2>&1 |\
    tr -d "',"  |\
      awk 'BEGIN   { redx='"$redx"'+0.5;   redy='"$redy"+0.5';
                     bluex='"$bluex"'+0.5; bluey='"$bluey"'+0.5; }
           NR == 3 { sx=$1; ry=$2;  tx=$3; rx=$4; }
           NR == 4 { sy=$1; ty=$2;  px=$3; py=$4; }
           END { div =  redx*px + redy*py + 1.0;
                 dx = ( redx*sx + redy*ry + tx ) / div;
                 dy = ( redx*rx + redy*sy + ty ) / div;
                 printf "red point now at %f,%f\n", dx, dy;
                 printf "translate shape by %+f %+f\n", bluex-dx, bluey-dy; }'
[IM Text]
上面使用了“tr”文本过滤器从输出中删除额外的引号和逗号。然后它使用“awk”程序提取系数,并执行将红色标记“前向映射”以匹配蓝色标记所需的浮点数学运算。请注意,我又向控制点的“像素坐标”添加了 0.5,以确保计算使用像素的中心。参见 图像坐标与像素坐标。现在我们知道了失真图像所需的平移量,我们有两种方法可以将该平移添加到失真中。可以通过适当地修改透视投影的系数(不容易)。或者我们可以只将平移量添加到原始图像的目标坐标的每一个中(非常容易)。以下是后一种方法的结果(将平移添加到目标坐标中)...

  magick align_blue.png \
          \( align_red.png -alpha set -virtual-pixel transparent \
             +distort Perspective '0,0   31.408223,15.334305
                                   0,%h  35.408223,43.334305
                                   %w,0  89.408223, 9.334305
                                   %w,%h 84.408223,51.334305 ' \
          \) -background none -layers merge +repage  align_persp_move.png
[IM Output]
在右侧,我已经裁剪并缩放了控制点周围的结果,以显示它们完美对齐!

  magick align_persp_shape.png -crop 19x19+50+17 +repage \
          -scale 500%   align_persp_shape_mag.png
[IM Output]
如您所见,我们对两个像素进行了完美的对齐,没有任何亚像素溢出到任何一侧。即使是最小的错位也会显示为中心像素两侧的不对称颜色。这种缩放甚至显示了由于透视失真导致红色十字左右两侧之间存在轻微的不对称差异。也就是说,这种像素级视图测试的准确性如何。
使用失真进行文本定位 中探讨了一个类似但更简单的问题。

评估序列 - 直接多图像合并方法

"-evaluate-sequence" 方法旨在以非常特定的方式将多个相同大小的图像合并在一起。在某些方面,它是 评估和函数运算符 与我们上面看到的多分量 合成 技术的混合。许多提供的方法甚至可以使用普通的多图像分层合成技术来执行,但并非所有方法都可以。该运算符使用与“-evaluate”相同的方法,因此您可以使用“-list Evaluate”获取它们的列表。尽管其中一些(例如“Mean”和“Medium”)在与该运算符一起使用时才真正有用。

多个图像的平均值

从本质上讲,较旧的“-evaluate-sequence mean”和较新的“-evaluate-sequence mean”都将创建所有提供的图像的平均值。例如,以下是使用玫瑰图像的所有 翻转和翻转 版本计算的平均值。

  magick rose: -flip rose: \( -clone 0--1 -flop \) \
          -evaluate-sequence mean  average.png
[IM Output]
对同一固定场景的数百张图像进行平均,可以用来消除大多数瞬态效应,例如移动的人,使它们变得不那么重要。但是,经常受到瞬态效应影响的区域可能会留下“幽灵般的模糊”,这可能很难去除。由于视频序列在查看单个帧时非常嘈杂,因此您可以将多个连续但不变的帧平均在一起,以产生更好、更清晰、更锐利的结果。亚利桑那大学的 Matt Leigh 报告说,他使用了这种技术来提高显微镜图像的分辨率。他拍摄了同一“目标”的多个图像,然后将它们全部平均起来,以提高结果的信噪比。他建议其他人也可能发现它对此目的有用。将两个图像平均在一起的另一种方法是使用“composite -blend 50%”图像操作,它可以处理两个不同大小的图像。有关更多详细信息,请参见 将两个图像混合在一起 的示例。 IM 讨论论坛 讨论了 每次平均 10 帧的序列,以便平均数千张图像,而不会占用计算机内存(使其变得非常缓慢)。与此相关并包含相关数学的是讨论 不要一次加载所有图像。使用“mean”的另一种方法是使用较新的 Poly 运算符,它可以分别对每个图像加权。

多个图像的最大/最小值

Max”和“Min”方法将从图像序列中获取最大(较亮)值和最小(较暗)值。同样,它们基本上等同于使用 亮化和暗化合成方法,但使用多个图像。通过正确选择背景画布颜色,您可以使用 Flatten 运算符 以及等效的合成方法。

  magick rose: -flip rose: \( -clone 0--1 -flop \) \
          -evaluate-sequence max  max.png
[IM Output]

  magick rose: -flip rose: \( -clone 0--1 -flop \) \
          -evaluate-sequence min  min.png
[IM Output]
警告:这不是像素(按强度)的选择,而是值的选取。这意味着输出图像可能会导致来自不同图像的单个红色、绿色和蓝色值,从而产生在任何输入图像中都找不到的新颜色。如果您需要按强度选择最大/最小像素,请参见 按强度亮化合成方法

按强度计算的中值像素

-evaluate-sequence Median”将查找其强度为所有给定图像的中值像素的像素。也就是说,对于每个位置,它都会收集并排序每个图像的像素强度。然后它将选择落在序列中间的像素。它也可以用作简单地对图像集合的像素进行平均的替代方法。例如,可以通过将图像与两个上下“限制”图像结合使用来实现此目的。由于像素将是中间强度,因此您将获得原始图像的像素,或者“限制”图像的像素。换句话说,您可以使用它来“裁剪”原始图像的强度。奇怪但真实。对于偶数个图像,将选择更亮一侧的中间像素。因此,对于只有两个图像,此运算符将等效于逐像素“按强度亮化”。关键是每个像素完全来自一个图像,并按强度排序。每个像素的确切颜色完全来自给定图像中的一个,因此不会生成新的颜色。例如,以下是使用玫瑰图像的所有 翻转和翻转 版本计算的中值强度的像素。请注意,它不像以前那么平滑,但可能会获得清晰的边界,因为它基于像素的强度。

  magick rose: -flip rose: \( -clone 0--1 -flop \) \
          -evaluate-sequence median  median.png
[IM Output]

添加多个图像

Add”方法当然会简单地将所有图像加在一起。

  magick ... -evaluate-sequence add ...
这是使用 Flatten 将所有图像 Plus 合成 的更快(更直接)版本...

  magick ... -background black -compose plus -layers flatten ...
请注意,以这种方式添加图像很容易超出图像的量子范围,因此可能会被“裁剪”,除非您使用 IM 的 HDRI 版本。这就是为什么通常使用 平均值或 Mean 的原因,因为它将平等地划分所有图像,以确保结果图像不会被裁剪。另一种方法是使用较新的 Poly 运算符,它可以分别对每个图像加权。

减去多个图像

Subtract”方法从第一个图像中减去每个图像。或者至少它应该这样做。在内部,它的参数已交换,它从下一个图像中减去先前的结果。啊!但是通过利用 线性燃烧合成方法 的一个特性,您可以从第一个图像中减去第二个及后续图像。基本上通过 反转 除第一个图像之外的所有图像,并将“white”(反转的零)设置为起始背景颜色,然后您可以使用 Flatten 将所有图像从第一个图像中减去。

  magick ...  \
         -negate \( -clone 0 -negate \) -swap 0 +delete \
         -compose LinearBurn -background white -flatten \
         ...

乘/除多个图像

Multiply”和“Divide”被“-evaluate-sequence”接受为方法,但它们会生成意外和奇怪的结果,因为它们使用的是图像的实际颜色值而不是规范化的颜色值,就像“-evaluate”一样。结果,乘法和除法的比例过大。这可以归类为错误。同时,您最好使用 Multiply 的等效“flatten”方法,该方法按预期工作。

  magick ... -background white -compose multiply -layers flatten ...

Poly - 使用多项式合并多个图像

与“-evaluate-sequence”密切相关,特别是与“mean”方法(图像平均)相关的是“-poly”运算符(在 IM v6.8.0-5 中添加)。此运算符为内存中的每个图像提供两个数字的列表,一个用于为每个图像提供乘法权重,另一个用于为每个图像提供幂指数。这允许您将图像列表合并,就像每个图像都是多项式方程的变量输入一样。每个图像的颜色值都被视为 0 到 1 的归一化值。对于每一对值,图像颜色(归一化)首先由第二个“幂”指数进行幂运算,然后由第一个数字进行加权(乘法)。如果指数为“1”,则该值仅乘以给定的权重。但是,如果指数为“0”,则权重将成为最终值,产生归一化的颜色常量加法(值从 0.0 到 1.0)。可以在当前图像序列中提供单个像素图像,并用于添加特定颜色,每个通道具有不同的归一化颜色值。(使用权重和指数 = 1.0)。或者您可以提供“NULL:”图像(或任何其他垃圾图像),并使用 0.0 的指数。这只会将给定的加权因子添加为常量。最终图像由第一个图像(及其大小和其他元数据)生成,就像 FX DIY 运算符 一样。例如...

  magick rose: granite: null: -poly '1,1 2,1 -1.0,0' poly_rose.png
[IM Output]
这采用“rose:”(使用权重 1 和幂 1 未修改),在此基础上添加“granite:”图像的两倍颜色值(权重=2),最后使用“null:”图像减去值 1,使用指数 0(忽略图像输入)和权重值 -1.0。生成的图像等效于...
rose + 2.0*granite - 1.0
rose + 2.0*(granite-0.5)

换句话说,玫瑰图像被叠加了一个带有噪声的花岗岩纹理(带有50%灰色的偏色)。这实际上与非常强的“亮光”合成照明效果完全相同,但对花岗岩叠加层进行了非常明确的权重设置。与其他多图像操作相比,关键的区别在于能够单独为每个图像加权,但在单个图像处理操作中执行所有计算,而无需额外的中间图像。这避免了在 ImagMagick 的非HDRI 版本中对最终结果的任何量子舍入、裁剪或其他影响。(参见量子效应)。例如,它可以用于执行大量图像的加权平均,例如对较小的图像组进行平均,然后对这些组进行平均。