ImageMagick 示例 --
动画基础

索引
ImageMagick 示例前言和索引
GIF 动画和动画元数据
帧处置方法
研究动画
动画类型
循环结束 - 动画停止运行的时间
零延迟中间帧
这些示例继续之前关于 多张图像的图层 的示例页面,但这里不是将多张图像叠加在一起生成单张图像,而是将每张图像显示很短的时间以生成图像动画。以下部分提供了对动画复杂性的基本理解,特别是 GIF 动画。它介绍了用于生成动画的基本方法,以及如何研究现有动画以了解其工作原理。建议在深入了解任何后面的动画部分之前阅读此部分。

GIF 动画和动画元数据

ImageMagick 处理图像列表输出的默认方式是生成多页图像。但是,对于 GIF 图像格式,它采用“GIF 动画”的特殊形式。

  magick -delay 100  -size 100x100 xc:SkyBlue \
          -page +5+10  balloon.gif   -page +35+30 medical.gif  \
          -page +62+50 present.gif   -page +10+55 shading.gif  \
          -loop 0  animation.gif
[IM Output]
以下是一个更高级的“闪烁”示例,它使用了一个名为“star_field”的 shell 脚本。该脚本是根据我在生成 随机星场 方面的实验而开发的。

  star_field 70x46  stars1.gif
  star_field 70x46  stars2.gif
  star_field 70x46  stars3.gif
  magick rose:  -compose Screen \
          \( -clone 0 stars1.gif -composite \) \
          \( -clone 0 stars2.gif -composite \) \
          \( -clone 0 stars3.gif -composite \) \
          -delete 0 -set delay 25 -layers Optimize rose_sparkle.gif
  rm stars[123].gif
[IM Output]
基本上,会生成三个随机星场,并调整到合适的大小,然后使用“屏幕” alpha 合成将它们叠加到我们的图像(IM 内置的“rose:”)上,以使用给定的星体模式使图像变亮。然后,整个过程通过 IM 通用 GIF 动画优化器运行。上面看起来可能很复杂,因为它使用了一些我尚未介绍的 IM 高级功能,但结果是一个相对简单但经过良好优化的三帧动画。您还可以查看一些使用简单的 shell 脚本为 失真动画 创建的更复杂的动画。还有一些额外的 IM 设置专为 GIF 动画而创建,了解这些设置是进入 GIF 动画世界的第一步…
-dispose {方法}
以下图像应该如何处理 GIF 动画的先前结果。有效选项是 'Undefined'、''、'前一帧' 和 '背景'。(有关设置的说明,请参见下文)
-loop {数字} GIF 动画在停止运行之前要循环遍历图像序列的次数。它是一个输出“图像写入”设置,因此可以在命令行的任何位置设置,但只使用最后一个这样的设置。通常,默认情况下设置为零(无限循环),但是如果任何读入的图像具有不同的值,则该设置将设置为该图像的值。因此,我建议您在创建 GIF 动画时始终设置 “-loop”,在所有图像都被读入之后。有关详细信息,请参见下文的 循环结束
-delay {时间}   设置时间延迟(以 1/100 秒为单位),以在绘制读入或创建的图像之后暂停,这些图像是在定义此设置之后读入或创建的。您可以通过指定“x”缩放比例来指定时间延迟的不同比例(以每秒滴答数表示)。例如,“10x1”是 10 秒滴答数,而“10x100”是 10 个百分之一秒滴答数。基本上,“x”等效于分数“/”符号。例如,如果您指定“1x160”,则会设置一个适合每秒 160 帧的延迟。
GIF 动画延迟必须以百分之一秒为单位指定,才能正常工作,这就是为什么这是默认时间单位。'x' 因子主要用于生成其他更像电影的格式,例如 MNG 和 AVI。
-set dispose {方法}
-set delay {时间}
虽然前面的选项设置将在给出该选项之后设置新创建的图像或读入的图像的图像属性,但“-set”选项是一个运算符,它允许您设置已在当前图像序列中的所有图像的图像属性。这使您能够在加载或修改图像后更改整个动画或单个帧的设置。
-page {w}x{h}+{x}+{y}
这使您可以设置要读入的图像的偏移位置。由于这是一个设置选项,因此它只将您提供的几何图形应用于跟随该设置的图像。它不会影响已经读入内存的图像。

如果没有给出,或者使用“+page”关闭,则读入图像的偏移量将被保留。如果图像没有偏移量,它将被定位在“+0+0”处,即工作画布或“页面”的左上角。

它还可以通过指定宽度“x”高度来定义更大的工作画布。仅使用序列中第一个图像的宽度和高度页面设置来设置整个 GIF 动画画布大小,所有其他页面大小设置在最终写入动画时将被忽略。当读入 GIF 动画时,画布大小将设置为动画中的所有帧。MNG 动画可以保存帧偏移量,但不会保存画布大小。第一个图像的大小定义了整个动画的画布大小。
GIF 图像格式不能为画布上的图像指定负偏移量。如果您尝试使用负偏移量,IM 将在写入 GIF 文件时将其重置为零。

大于图像画布的正偏移量是可以接受的,但可能会导致图像在 magick 显示时不会出现在画布绘制区域中。GIF 动画显示程序如何处理这是未定义的。建议谨慎使用。
-repage {w}x{h}+{x}+{y}
这与“-page”完全相同,只是它是一个图像运算符而不是一个设置。这意味着您可以使用它来更改或重置已读入内存的图像或动画帧的“页面几何图形”。更简单的“+repage”形式,只将当前图像序列中所有图像的“页面几何图形”重置为零偏移量,以及图像的实际大小。当您从动画中提取单个帧时,此操作至关重要(请参见下文的 拼接示例)。但是,“+repage”会破坏存储在每个图像中的大量定位信息,因此您可能还需要将此信息提取到单独的文件中以备后用。请参见下文的 动画列表信息

重要提示

不要将未完成处理的中间动画直接保存到 GIF 中。如果您想分步骤处理动画,可以使用 IM 内部格式 MIFF 作为临时文件格式。我再说一遍…
不要使用 GIF 作为中间文件格式,而应使用 MIFF
如果您犯了将动画保存到 GIF 的大错误,那么您只会使生成的动画变得更糟,因为 IM 现在会自动执行 颜色量化 以减少存在的颜色数量。不仅如此,它还对每个帧完全独立地执行此操作,使得任何进一步的处理,特别是任何 GIF 优化都变得更加困难。解决这个问题是一个复杂的多级问题,将在下一部分 动画优化 中介绍。

帧处置方法

创建 GIF 动画的人首先遇到的问题是“-dispose”设置。这并不奇怪,因为它是一个复杂的设置。更糟糕的是,许多动画程序(包括许多 Web 浏览器)并不总是能够正确处理 GIF 的处置元数据设置。但是,使用正确的处置方式可以对动画效果和优化产生重大影响。首先要记住的是,在 ImageMagick 中,几乎所有特殊的动画选项都是针对图像读取的设置。也就是说,它们应用于在设置给出后读取的图像。-loop”设置是唯一一个通常在动画完成后(就在动画保存之前)使用的设置。-dispose”的基本任务是定义图像在显示了其“-delay”时间段后如何被移除。也就是说,您需要在读取该帧的图像之前给出图像的“-dispose”和“-delay”设置。但操作是在该图像显示之后应用的。这有点违反直觉,但在 IM 对图像的操作方式上是有意义的。如果您记住这一点,您应该不会遇到任何问题。“加号”形式的这些选项(与 IM 中大多数其他设置一样)会阻止将该设置应用于正在读取的任何图像。这意味着,如果您没有指定设置,帧图像将继续使用从图像读取的设置(如果有)。这在您稍后想要读取 GIF 动画以进行进一步处理时可能很重要。或者在将一个 GIF 动画合并到另一个 GIF 动画时(最难的动画技术)。

处置无- 每个帧按顺序叠加

GIF 动画的默认“-dispose”设置是“Undefined”,大多数动画程序将它视为“None”处置设置。基本上,这告诉计算机只保留该特定帧叠加的任何内容。或者更准确地说,“什么也不做”。但是请注意,整个画布始终在动画序列结束时被清除,然后循环并重复。例如,以下是一个标准的“无处置”动画...

  magick -delay 100 -dispose None \
              -page 100x100+5+10  balloon.gif  \
              -page +35+30 medical.gif  \
              -page +62+50 present.gif  \
              -page +10+55 shading.gif  \
          -loop 0  anim_none.gif
[IM Output]
这种处置技术非常适合那些不涉及任何形式的透明度的动画,例如绘制在实心或图案背景上的动画。

  magick -dispose none  -delay 100 \
                -size 100x100 xc:SkyBlue +antialias \
                -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  \
          -loop 0  canvas_none.gif
[IM Output]
请注意,这种技术只能向动画添加可见的颜色,它永远无法使动画的任何部分再次变为透明。(参见下面的“叠加动画”。要处理透明度,还需要使用其他类型的处置方法。

处置先前- 保留背景画布

Previous”处置方法比较简单。当当前图像完成时,将画布恢复到图像叠加之前的样子。如果前一帧图像也使用了“Previous”处置方法,那么结果将与前一帧图像之前相同。等等,等等,等等。例如,在这个动画中,每个后面的帧都会回到第一个图像(它有一个“None”处置设置),然后叠加与该帧相关的图像。结果是一个背景画布,上面只叠加了每个帧图像,持续时间只与该图像相同...

  magick -dispose none  -delay 0 \
                -size 100x100 xc:SkyBlue +antialias \
                -fill DodgerBlue -draw 'circle 50,50 15,25' \
          -dispose previous -delay 100 \
                -page +5+10  balloon.gif  \
                -page +35+30 medical.gif  \
                -page +62+50 present.gif  \
                -page +10+55 shading.gif  \
          -loop 0  canvas_prev.gif
[IM Output]
注意第一个图像使用了“-dispose”方法“None”。这很重要,否则“前一帧”将完全回到第一帧之前存在的原始空画布。还要注意,我在上面的动画中使用了“-delay”值为“0”。这意味着在将第一帧叠加到“背景画布”上之前不要等待。如果没有它,您将看到一个短暂的延迟,只显示画布图像,上面没有任何内容。当然,我仍然需要为后面的图像设置更长的“-delay”,否则它们会以眨眼的速度出现和消失,并且会顺便消耗掉许多观看者的 CPU 周期。“Previous”处置方法的使用可能会导致一些 Web 浏览器出现轻微的闪烁或暂停,尤其是在速度较慢的机器上。尽管这种情况在如今很少见,但闪烁本身仍然存在,我认为它是一个 bug。有关更多细节,请参见下面的“零延迟帧”。很少有动画使用处置先前类型的动画,原因是计算机很难对其进行优化。问题就在于计算机应该选择哪一帧作为背景图像?对于我们人类来说,弄清楚要使用的最佳图像很简单,但对于计算机来说,很难决定。动画中要使用的最佳背景图像可能并非旨在显示,例如在当前示例中,因此可能不存在于该动画的非优化版本中。

处置背景- 清除到背景

虽然前两种“-dispose”方法比较简单,但“Background”可能最难理解。当特定帧的时间延迟结束时,被该帧叠加的区域会被清除。不是整个画布,而是被叠加的区域。完成后,生成的画布将传递给动画的下一帧,并被该帧的图像叠加。例如,这里我们只是用下一帧替换每一帧。

  magick -delay 100 -dispose Background \
              -page 100x100+5+10  balloon.gif  \
              -page +35+30 medical.gif  \
              -page +62+50 present.gif  \
              -page +10+55 shading.gif  \
          -loop 0  anim_bgnd.gif
[IM Output]
因此,您可以确切地看到发生了什么,让我们在动画中添加一个初始画布图像,这样您就可以看到“Background”实际上如何从动画显示中“处置”该帧。

  magick -delay 100 -dispose none \
                -size 100x100 xc:SkyBlue +antialias \
                -fill DodgerBlue -draw 'circle 50,50 15,25' \
          -dispose background \
                -page +5+10  balloon.gif  \
                -page +35+30 medical.gif  \
                -page +62+50 present.gif  \
                -page +10+55 shading.gif  \
          -loop 0  canvas_bgnd.gif
[IM Output]
正如您所看到的,当每个叠加的帧被处置时,该帧的区域会被清除为透明,然后下一个图像会被叠加。这就是这种 GIF 处置方法的重要性,因为它是 GIF 动画清除任何像素的唯一方法,无论动画帧的历史如何。清除像素的另一种方法是使用“Previous”返回到像素被清除的帧。但这依赖于了解动画序列的历史,这使得计算机更难以优化。
有人认为,而不是将叠加区域清除为透明颜色,这种处置应该将它清除为存储在 GIF 动画中的“背景”颜色元数据设置。事实上,旧的“Netscape”浏览器(版本 2 和 3)就是这么做的。但它也无法正确实现“Previous”处置方法。

另一方面,初始画布也应该从格式的“背景”颜色设置,但这也没有完成。但是,所有现代 Web 浏览器都会清除最后被叠加到透明的颜色区域,因此这现在已成为公认的做法,也是 IM 现在遵循的做法。
在 IM 6.2.6-1 版本之前,IM 的“-coalesce”和“-deconstruct”操作不会处理使用“Background”处置将像素变为透明的动画,与所有主要的 Web 浏览器一致。有关示例和详细信息,请参见“动画 Bug”。

但是,当没有应用或意图清除像素时,这些功能可以正常工作。现在,这个问题在“-coalesce”中已得到修复,并且创建了“-layers OptimizeFrame”方法来替换“-deconstruct”作为 GIF 动画帧优化功能。

研究动画

在我们继续学习 GIF 动画的基础知识、类型、优化和处理技术之前,我们需要一些研究现有动画的技术。

识别- 关于动画的信息

现在,动画包含大量信息,这些信息打包在每个单独的帧中。您可以使用默认的 IM“identify”命令来查看其中的一些信息。

  magick identify canvas_prev.gif
[IM Text]
如果您没有看到像上面这样的输出,您的 IM 稍微旧一点,您真的应该将安装的 ImageMagick 版本升级到最新版本。如果您不这样做,您将错过 IM 在处理和控制 GIF 动画方面取得的许多新进展。
正如您所看到的,为第二帧和后面的帧保存的实际图像只有 32x32 像素,但所有帧都位于一个 100x100 像素的“虚拟画布”上,在这个更大的画布上有一个“虚拟偏移”。要查看更多存在的各种元数据,您需要使用一些更专业的“百分比转义格式”来让 IM 输出它。

  magick identify -format "%f canvas=%Wx%H size=%wx%h offset=%X%Y %D %Tcs\n" \
           canvas_prev.gif
[IM Text]
这清楚地显示了不仅是画布大小、图像大小和偏移量,还有每个单独帧使用的处置和时间延迟。注意第一帧是如何具有不同的处置和时间延迟,这是正确使用后面的“Previous”处置方法所必需的。

连接- 将动画拆分为帧

正如您在上面看到的,ImageMagick 默认情况下会尝试将多个图像保存到一个文件中,如果该文件格式允许的话。但是,正如“写入多图像列表”中所讨论的,IM 允许您使用“+adjoin”设置来告诉它将每个图像保存到磁盘作为单独的独立图像。例如,这里我们读取一个 GIF 动画并将动画序列中的各个帧图像输出。

  magick canvas_prev.gif -scene 1 +adjoin  frame_%03d.gif
[IM Output] [IM Output] [IM Output] [IM Output] [IM Output]
如果您要检查上面的实际图像,您会发现,尽管大多数 Web 浏览器显示更大的 100x100 区域,每个子帧都显示在其中。实际上,大多数实际显示的图像实际上只有 32x32 像素,正如前面“识别”命令中显示的那样。也就是说,大多数区域只是画布,上面没有绘制任何内容,称为图像的“页面几何”或“虚拟画布”。动画的第一帧定义了更大的“画布”,而其他所有帧都定义了在这个更大的画布上的“偏移”位置。此额外信息保存在通过“+adjoin”设置保存的帧中。因此,您可以轻松地重新构建 GIF 动画。不仅每个独立帧图像中都保存了页面信息,而且任何延迟、循环和 GIF 处置设置也被保存。
这意味着要重建动画,您只需要读取所有图像。

  magick frame_???.gif  anim_rebuilt.gif
[IM Output]
但是,有时您不想保存此页面几何信息。例如,如果您想将各个帧用于其他项目。您可以使用“+repage”选项来重置页面大小和偏移量,以删除“虚拟画布”信息,只留下实际图像。
通常,在提取动画子图像时,您通常还会重置图像的延迟和处置设置,以确保它们不会干扰编辑和显示。例如,这里我删除了不需要的虚拟画布和偏移量,并重置了时间延迟和处置。

  magick canvas_prev.gif  +repage  -set delay 0   -set dispose None \
          +adjoin  repage_%03d.gif
[IM Output] [IM Output] [IM Output] [IM Output] [IM Output]
当然,如果您丢弃了元数据,您需要某种方法来记录和编辑这些数据。有关提取子图像并以可用于重建动画的形式保存动画元数据的脚本,请参见下面的“动画列表信息”。

合并- 将帧完全填满

在典型的动画中,以子帧的形式查看动画通常不是很有用。一方面,高度优化的动画可能包含许多非常小的部分,没有任何视觉指示它们如何组合在一起。它也可能包含许多其他为了 压缩优化 添加的“噪音”,以减小动画的整体文件大小。例如,仅通过查看动画的单个子帧,很难弄清楚动画实际上做了什么。

  magick script_k.gif  +repage  +adjoin  script_k_%02d.gif
[IM Output] [IM Output] [IM Output] [IM Output] [IM Output] [IM Output] [IM Output] [IM Output] [IM Output] [IM Output] [IM Output]
"-coalesce" 操作基本上将图像转换为动画在正确 处置 上一帧并叠加下一子帧后应该呈现的样子。也就是说,它不是一个动画序列,其中每一帧仅表示叠加到上一帧“处置”帧的更改。此运算符在每个点创建动画的完整视图,有点像真正的胶片,而不是动画序列。这样的序列被称为 合并动画,它更容易研究、编辑、修改和重新优化。例如,这里将生成与上面显示的相同“混乱”动画序列的蒙太奇,但这次我们将对序列进行“-coalesce”,这样你就可以看到实际发生了什么。

  montage script_k.gif -coalesce \
          -tile x1 -frame 4 -geometry '+2+2' \
          -background none -bordercolor none coalesce_k_montage.gif
[IM Output]
[IM Output]
正如你所看到的,结果就像动画的胶片,让你可以清楚地看到之前的片段是如何组合在一起形成一个手写的字母“K”的。从 IM 6.2.6 版开始,“magick montage”命令理解“-coalesce”的使用,让你可以创建像胶片一样的动画帧图像,就像上面显示的那样。此版本还包含对 coalesce 的修复,任何 GIF 动画工作都应该至少是此版本(或者最好是最新版本)。
下一部分示例将介绍一种更好的用于检查动画的蒙太奇技术。
合并图像序列的“-dispose”设置实际上与 合并动画 无关。但是,为了让用户安心,“-coalesce”运算符会将每帧的“-dispose”设置分别设置为“None”或“Background”,以便合并的图像序列能够继续正确地进行动画(如上所示)。
具有“Background”处置的帧意味着下一帧需要清除至少一个或多个像素才能正确显示。

因此,“-coalesce”添加了“Background”处置的动画不能保存为简单的 叠加动画(见下文)。

从技术上讲,你可以将合并图像序列的所有处置设置设置为“Background”或“Previous”来生成 清除帧动画(见下文)。不过,并非所有动画都能以这种形式很好地优化。
"-coalesce" 运算符还有一些非动画用途。有关这些用法的示例,请参见 合并和渐进式平化

动画帧蒙太奇- "gif_anim_montage" 脚本

虽然“+adjoin”运算符允许你从动画中提取实际的图像,而“-coalesce”允许你查看动画的最终帧,但这两种方法都省略了有关动画的大量信息。通过对动画图像进行一些非常仔细的操作,你可以显示帧,以便不仅显示实际的帧,还显示这些帧在更大画布上的位置。这是一种显示动画的方法。

  magick -dispose Background   script_k.gif  -alpha set \
          -compose Copy -bordercolor black -border 1x1 -compose Over \
          -coalesce  -bordercolor none   -frame 4x4+2+2 \
          -bordercolor none -border 2x2 +append  script_k_parts.gif
[IM Output]
在这里你可以清楚地看到动画是如何工作的。每个子帧图像都处于能够添加到所有先前叠加的位置。结果是缓慢增长的图像。每个帧也比它所处的位置“虚拟画布”要小得多。我在 GIF 动画的开发和调试过程中经常使用这种显示技术,因此我将其转换为一个 shell 脚本“gif_anim_montage”,并将其扩展到还列出动画中每一帧上方的一些细节。

  gif_anim_montage   script_k.gif   script_k_frames.gif
[IM Output]
请注意,在不同的帧中使用的计时变化,以暂停,就像笔从页面上抬起并重新定位一样。具有可变计时的动画可能是最有趣的动画,但也是最难处理的动画,正如你将在后面的 IM 示例页面中看到的那样。“gif_anim_montage”脚本还包含特殊选项“-u”,它还会在合并动画下方覆盖一个半透明的副本。这让你可以看到新的子帧如何修改显示的动画。

  gif_anim_montage  -u  script_k.gif  script_k_frames.png
[IM Output]
当然,它包含半透明像素,因此需要“PNG”图像格式,或者你可以使用脚本提供的许多“背景”选项之一,允许你对动画的最终摘要图像使用 GIF 甚至 JPEG 格式。其他选项允许你定义要使用的行数或列数,以及设置各种不透明的背景,或者使用红色框而不是默认的黑色框。此脚本将在接下来的几页 IM 示例中被大量使用。欢迎提出建议和意见。

动画列表信息- 用于构建动画的选项

正如我提到的,使用“+adjoin”和“-coalesce”,以及“+repage”,都是提取和查看 GIF 动画的有用方法。但是,它们在过程中都会破坏有关原始动画的信息。你可以使用 IM 的“magick identify”命令(带“-verbose”选项)查看有关帧、时间延迟、帧处置等的额外信息。但是,我(以及可能大多数其他用户)发现此命令的输出非常庞大,实际上无法直接使用。这就是我编写另一个特殊 shell 脚本的原因。 “gif2anim”脚本将分离动画的各个帧,但也会准确地计算出你需要哪些 IM“magick”选项才能从这些图像重建动画。你可以将“gif2anim”视为动画反汇编器,它会以 IM 选项的形式提供动画的摘要。例如,让我们解码我们一直在使用的动画示例,以恢复创建它时使用的原始“magick”设置,以及使用的单个图像...

  gif2anim canvas_prev.gif
[IM Text]
[IM Output]
[IM Output]
[IM Output]
[IM Output]
[IM Output]
默认情况下,“gif2anim”脚本使用相同的基文件名作为单个图像和“.anim”选项文件。因此,上面命令生成的动画序列文件名为“canvas_prev.anim”,单个帧图像为“canvas_prev_001.gif”到“canvas_prev_005.gif”。如果你更仔细地检查结果,你会发现它实际上成功地重新创建了我第一次创建此 GIF 动画时使用的原始选项(参见 处置先前动画)。此外,虽然它对实际生成动画并不重要,但叠加帧的大小和计时也作为注释列出,以方便研究。你也可以使用“-l”标志将结果列出到屏幕,而不是将其保存到文件中。也就是说,只是输出动画序列文件,而不是保存它,或者动画的单个帧图像。

  gif2anim -l canvas_prev.gif
给定一个“.anim”文件和单个帧图像,可以使用一个补充脚本“anim2gif”来重建动画。

  anim2gif canvas_prev.anim
[IM Output]
anim2gif”默认情况下会使用“_anim.gif”后缀重新创建 GIF 动画。你可以看到,生成的“canvas_prev_anim.gif”动画看起来和工作方式与原始动画完全一样。此脚本只是将“动画序列文件”中使用的特殊字符串“BASENAME”替换掉,删除所有注释,然后将剩下的转换选项传递给“magick”命令。换句话说,它将上述文件视为一种带注释的“转换”脚本。使用特殊字符串的原因是,这样你就可以指定与“.anim”文件本身不同的基文件名。这样你就可以使用一组完全不同的帧图像(例如,原始图像的修改版本)来从旧动画中重新创建一个不同的动画。这是一个非常有用的功能,它将在更复杂的动画处理中使用。(有关示例,请参见 并排追加动画)。与“gif2anim”一样,“anim2gif”脚本也包含许多有用的选项,可以帮助你处理和修改动画。这些选项中的一些将在后面使用。例如,参见 追加动画。此外,由于“.anim”文件是纯文本,你可以使用它获取动画的解码图像,以调整 GIF 的元数据,例如计时、位置、动画的重复部分,或者向动画添加新帧和图像。毕竟,这是我最初编写脚本的原因,早在我开始使用 IM 示例之前很久。目前,“gif2anim”对于检查动画序列以查看发生了什么以及在帧之间应用的计时最有用。

处置图像- 帧的 GIF 处置形式

此特殊的“-layers”方法“Dispose”显示了帧在时间延迟结束并应用了 GIF 处置方法后,但在叠加下一帧图像之前的外观。换句话说,这显示了 GIF“-dispose”方法设置对帧的实际影响,让你可以准确地找出动画中出现了什么错误。例如,以下是如何在应用各个帧处置方法后,三个 处置方法示例动画 的外观。请记住,这三个动画中的每一个都包含一个设置为“None”的“-dispose”设置的“画布图像”,然后叠加了四个较小的图像,然后通过各种 GIF 处置方法进行处置。“None”处置动画...

  magick canvas_none.gif -layers Dispose canvas_none_dispose.gif
  gif_anim_montage canvas_none_dispose.gif canvas_none_dispose_frames.gif
[IM Output]
'Previous' 处置动画...

  magick canvas_prev.gif -layers Dispose canvas_prev_dispose.gif
  gif_anim_montage canvas_prev_dispose.gif canvas_prev_dispose_frames.gif
[IM Output]
'Background' 处置动画...

  magick canvas_bgnd.gif -layers Dispose canvas_bgnd_dispose.gif
  gif_anim_montage canvas_bgnd_dispose.gif canvas_bgnd_dispose_frames.gif
[IM Output]
如果你研究上述内容,你可以准确地看到三种 GIF 处置方法中的每一种如何从动画中清除该帧叠加的图像。请注意,这三个动画的第一帧始终设置为“None”处置,因此将保持不变。处置方法对后面帧的影响很重要。
-layers Dispose” 操作只会生成丢弃帧的“合并”序列。它不会重置丢弃设置本身,因此结果可能无法正确动画化。要使上述动画正确,请将所有丢弃方法设置为“previous”或“background”,或者您可以在保存之前优化动画。
GIF 丢弃方法后最后一帧的外观通常对 GIF 动画没有影响,因为整个画布在动画重复(循环)之前被完全清除。如果它没有“循环”而是在动画序列结束时停止,则不会应用最后一帧的丢弃。

换句话说,如上所示的最后一帧(丢弃后)的外观,甚至最后一帧的实际丢弃设置,对 GIF 动画没有任何影响。在动画的 帧优化 期间,IM 通常会将此设置为与前一帧相同,以便在尝试找出合适的丢弃方法时。

解构- 报告帧差异区域

ImageMagick 中优化动画的传统方法是“-deconstruct”其“-coalesce”形式。不再推荐此方法。您应该改用 通用 GIF 优化器。此操作符将获取合并的图像序列(动画帧在显示时实际显示的样子),并将第二个及之后的图像与前一个图像进行比较。然后,它将该图像替换为已更改像素的最小矩形区域。任何像素更改都会被计算在内,无论它是颜色更改(覆盖)还是清除(擦除)。这非常简单,对于典型的 覆盖动画,它将为该动画生成最佳的 帧优化。但是,覆盖动画 动画只使用“None”丢弃方法。例如,让我们看看上面生成的 合并前一个动画,它恰好形成一个 覆盖动画,并将其通过“-deconstruct”操作符运行。

  magick canvas_prev.gif   -coalesce     coalesce.gif
  magick coalesce.gif     -deconstruct   deconstruct.gif
  gif_anim_montage  coalesce.gif     coalesce_frames.gif
  gif_anim_montage  deconstruct.gif  deconstruct_frames.gif
[IM Output]
[IM Output] ==>
==> [IM Output]
如果你还记得,“以前丢弃动画”会将每个帧清除到最后一个非以前丢弃的帧,在本例中是初始背景画布。如你所见,“-deconstruct”返回了从一个合并帧到下一个合并帧之间发生更改的区域。从而生成一个优化的 覆盖动画,它不需要任何特殊的丢弃设置。这远没有我最初手工生成的动画那么好,但本身也很有用。
不幸的是,“-deconstruct”完全不了解 GIF 动画的“-dispose”设置。因此,如果你在需要从一帧到下一帧清除像素的动画上尝试这样做,例如我们上面创建的(并在左边显示的)“背景丢弃”动画,它将失败得很惨。 [IM Output]
在这里,我们拿刚刚显示的动画,并将其通过“-coalesce”和“-deconstruct”循环运行。

  magick canvas_bgnd.gif  -coalesce  -deconstruct  deconstruct_erase.gif
[IM Output]
如你所见,“-deconstruct”慢慢地破坏了动画。基本上,“-deconstruct”被设计为简单地找到图像层之间的差异。它从未被设计为正确地优化动画,并且在需要使用各种丢弃技术来清除(擦除或透明化)先前叠加的像素的动画中会失败。

帧比较- 更详细的帧比较

在 IM v6.2.6-2 中,添加了一些额外的 GIF 帧比较方法。这些方法在内部需要用于正确优化动画,但被认为足够有用,因此可以将其提供给命令行和其他 API 接口。

Compare_Any

-layers”方法“CompareAny”实际上与“-deconstruct”完全相同。实际上,“-deconstruct”操作符只是“CompareAny”方法的功能别名。让我们再次看看“deconstruct”或“CompareAny”对“背景丢弃”动画的实际图像结果。

  magick canvas_bgnd.gif  -coalesce  canvas_bgnd_coal.gif
  gif_anim_montage canvas_bgnd_coal.gif canvas_bgnd_coal_frames.gif

  magick canvas_bgnd_coal.gif  -layers CompareAny   magick compare_any.gif
  gif_anim_montage compare_any.gif compare_any_frames.gif
[IM Output] ==>
==> [IM Output]
如你所见,第二个及之后的图像,是包含所有已更改像素的最小矩形区域,无论它是新像素颜色的叠加,还是将旧像素清除为透明度。

Compare_Clear

-layers”方法“CompareClear”将显示包含所有需要从一帧到下一帧清除的像素的最小矩形区域。

  magick canvas_bgnd_coal.gif -quiet -layers CompareClear compare_clear.gif
  gif_anim_montage compare_clear.gif compare_clear_frames.gif
[IM Output]
请注意,由于第一帧和第二帧之间没有像素被清除,因此生成了一个特殊的 丢失的图像。“-quiet”设置用于告诉 IM 不要对此图像发出任何警告。如果所有后续帧都成为“丢失的图像”,那么 GIF 动画永远不会清除像素,并且该动画可以归类为 覆盖动画

Compare_Overlay

最后一个“-layers”比较方法“CompareOverlay”返回自前一帧以来叠加(添加或更改颜色,但未清除)的像素区域。

  magick canvas_bgnd_coal.gif  -layers CompareOverlay  magick compare_overlay.gif
  gif_anim_montage compare_overlay.gif compare_overlay_frames.gif
[IM Output]
这类似于 IM 特定的“ChangeMask”alpha 合成方法。但是,它只返回更改图像的像素,而不是更改的矩形区域。另请参见 透明度优化
无论“-layers”比较方法,还是“-deconstruct”操作符,都不会查看或修改使用的图像 GIF 丢弃方法。结果只是一系列图像,并不期望将其用作动画本身。
虽然这些操作符被设计为与合并的图像序列一起使用,但它们会接受非合并的图像层序列,而不会产生错误。

在这种情况下,每个帧都会使用“Copy”alpha 合成方法叠加到先前叠加的帧上,然后比较这些帧。这种 alpha 合成方法确保层中的任何透明度也会添加到目标图像中。如果没有此方法,上面将不会找到在合并的图像序列中被清除为透明度的像素。

请注意,这与“-coalesce”操作符将用于处理显示 GIF 动画所需的“丢弃/覆盖”循环的更常见的“Over”合成方法不同。


动画类型

你找到的大多数 GIF 动画都属于一些基本的动画类型。了解这些类型可以让您了解动画是如何从一帧到下一帧显示的,并可以帮助您在处理和修改动画时采取捷径。

合并动画

合并动画”基本上是一个图像序列,它显示了动画在每次“丢弃/覆盖”循环后显示给用户时的样子。这些图像基本上就像你正在查看动画的实际“胶片带”一样。它是任何动画的简化且完全未优化的形式。这种命名约定来自 IM 的“-coalesce”操作符的名称,该操作符用于将 GIF 丢弃动画转换为未优化的“合并动画”。大多数视频格式(MPEG、AVI 等)实际上也因为其本质而属于“合并动画”。但是,这些格式通常也不包含任何透明像素,并且帧之间的动画时间延迟通常是恒定的。但是,合并的 GIF 动画可以包含透明像素,并且时间延迟从立即的“0”延迟到非常快或非常非常慢。合并动画中的任何 GIF 丢弃设置都没有任何意义,但是“-coalesce”操作符会适当地设置丢弃,这样生成的图像序列仍然可以作为有效的 GIF 动画工作。始终替换每帧所有像素的视频格式通常可以使用“None”或“Undefined”GIF 丢弃设置。这是一个动画示例,它也是本身的合并动画,再加上“gif_anim_montage”动画各个帧的显示。
[IM Output] [IM Output]
大多数不包含或使用透明度,并且对整个画布进行动画处理的动画通常以合并动画的形式保存和分发。

叠加动画

覆盖动画”是指动画的每一帧只覆盖到目前显示的动画上的新像素。换句话说,动画在任何时候都不需要将像素清除为透明度。各个帧可以包含透明度,作为背景,或者作为优化的一部分,但它永远不会将像素清除回透明度。当然,如果根本不使用透明度,那么动画保证可以变成一个简单的覆盖动画。这可能是最简单的 帧优化 动画类型,并且不需要客户端进行特殊处理。显示给用户的每一帧都可以被视为简单地“扁平化”所有先前帧的图像。任何只使用“None”GIF 丢弃方法的动画都是“覆盖动画”。例如,最后一个例子不仅是“完全合并动画”,也是“覆盖动画”,但并非所有此类“完全合并动画”都是“覆盖动画”。你可以通过对 合并 动画使用“CompareClear”层方法,并检查所有第二个及之后的图像是否为“丢失的图像”来测试动画是否可以变成覆盖动画。也就是说,从一帧到下一帧不需要清除或擦除任何像素。事实上,如果动画可以在不修改的情况下变成覆盖动画,那么 IM 的“coalesce”操作符只会在所有帧中使用“None”丢弃方法。如果不是这种情况,那么“coalesce”至少会对某些帧使用“Background”丢弃方法。然后,这为您提供了另一种测试“仅覆盖”功能的方法。覆盖动画可以使用透明度,但它们在透明背景上没有任何移动部分。例如,用手写字母“K”进行动画处理,就是一个覆盖动画,因为每个部分只会在透明背景上添加或更改现有部分。它从未向生成的图像添加新的透明度(除了作为第一帧的一部分)。
[IM Output] [IM Output]
[IM Output] 也就是说,这并不意味着覆盖动画无法处理移动对象,这只是意味着你需要一个非透明背景,以便你也可以“擦除”移动对象的旧位置,而无需使用透明度。例如,看看这个“将世界下载到文件夹中”动画的帧...
[IM Output]
当然,由于需要通过覆盖原始背景来“擦除”旧部分,这意味着叠加的子图像通常更大,因此 GIF 动画文件的大小通常也更大。不幸的是,使用 IM 的 优化帧 运算符可能会(也很可能)将“合并覆盖动画”转换为非“覆盖动画”,因为它试图找到更小的 GIF 文件大小。但是,通过对动画使用 分解 而不是使用 优化帧,您可以确保动画保持为简单的“覆盖动画”,但这只有在动画确实是“覆盖动画”的情况下才能实现。如果动画不是“覆盖动画”,则 分解 操作可能会出错(参见上面的 分解)。通过一些人为技巧,您仍然可以更好地优化覆盖动画,例如使用 拆分帧更新 并应用某种形式的 压缩优化,而不会破坏动画的“仅覆盖”要求。通常“覆盖动画”根本不显示透明度(它们可以使用透明度作为优化的一部分,但不会显示它)。如果未显示透明度,则该动画保证为“覆盖动画”。为什么“覆盖动画”如此重要?因为有些软件仅限于这种类型的动画。它更容易处理,因为只执行叠加,无需处理透明度,或保存前一帧以处理 GIF 处理方法。此类软件很少见,但确实存在。

清除帧动画

当动画仅使用“上一个”或“背景”GIF 处理方法时,您将获得一种非常特殊的动画类型。请注意,如果仅使用“背景”处理方法,则将显示动画的所有帧,然后在显示下一帧之前清除它们。同样,当仅使用“上一个”时,动画始终在显示下一帧之前返回到初始的已清除画布,从而产生相同的效果。如果您只使用这两种处理设置的混合,也会发生同样的情况。也就是说,仅使用这些处理方法的动画将具有完全复制要显示内容的帧。也就是说,该帧包含在该时间点将显示给用户的全部内容。这并不意味着该动画是“完全合并动画”。因为子帧可能远小于动画的虚拟画布区域,但该帧之外的所有内容将被视为透明(或背景),不包含任何重要内容。例如,看看这个奔跑的兔子动画...
[IM Output] [IM Output]
请注意,每个子帧都是显示的完整图像。不多不少。另外请注意,没有哪个帧实际需要使用动画的整个虚拟画布。最后请注意所有帧的处理方法都设置为“上一个”,正如您将在下面看到的原因,这是更合理的处理设置。但是,所有处理设置都可以设置为“背景”处理方法或这两种方法的任何混合,而不会改变最终结果。由于缺乏更好的名称,我将这种动画称为“清除帧动画”,但我还见过它被称为“上一个或背景处理动画”。动画不属于这种类型的情况是,如果动画中至少有一个非空白帧使用了“”或“未定义”(相同内容)处理方法。这种动画非常特殊,因为它可以处理动画序列中任何位置的任何透明度清除,不像 覆盖动画。但它也可以非常快地叠加到任何静态背景图像上。要做到这一点,我们需要稍微调整一下“清除帧动画”的定义。具体来说,我们需要确保所有处理方法都设置为“上一个”(这在我们示例中已经是这种情况)。如果这样做,那么您只需在前面添加一个图像(延迟为零)即可作为背景的基础。
例如,让我们把我们的兔子放在一些草地上....

  magick bunny_grass.gif bunny_anim.gif -loop 0  bunny_on_grass.gif
[IM Output]
正如您所看到的,这非常简单,以至于许多应用程序使用这种类型的 GIF 动画将符号或其他指示器(文件锁定、表情符号、星星等)添加到更大的对象中。动画这种 GIF 动画也很容易,因为应用程序只需将该区域清除为某些简单的常数背景图像,并叠加序列中的下一帧即可。无需计算处理方法,也不必跟踪“前一个”显示,除了静态不变的背景显示之外。这也是为什么“上一个”处理方法是 清除帧动画 的首选处理方法的原因。不像 覆盖动画 只是 GIF 动画的一个特殊子集,所有动画都可以保存为 清除帧动画。只需 合并 动画,然后选择性地 修剪 任何周围的透明边缘以优化帧,并重置处理方法。

  magick any_animation.gif -coalesce -trim \
          -set dispose previous   cleared_frame_animation.gif
您甚至可以重新定位该背景上的动画...

  magick bunny_grass.gif \( bunny_anim.gif -repage 0x0+5+15\! \) \
          -loop 0  bunny_on_grass2.gif
[IM Output]
因此,清除帧动画 通常包含一个在透明背景上不断变化或移动的小物体。这些可以直接在网页上使用,或用作动画符号,或与其他动画合并以生成更复杂的动画。总之,这种类型的动画是库中使用的动画部件的一种良好的风格,用于创建更大、更复杂的动画。
但是,对于 GIF 动画,向其中添加背景存在一个问题。如果您查看前面的示例,您可能会注意到快速移动动画中存在明显的、令人不安的暂停。也就是说,当动画循环时,兔子消失了一瞬间,并且背景图像被重新加载。(请参阅 零延迟帧 中的说明)虽然这对使用此技术的应用程序来说不是问题,因为它们只是不显示该“中间”帧,但如果您在 GIF 动画中添加了一个非透明背景,那么通常最好将简单的 清除帧动画 转换为 覆盖动画。也就是说,将该背景添加到动画的每一帧,而不是提供一个初始画布。您可以通过以下两种方式之一来实现:合并 上面的动画,然后删除 零延迟 背景帧,或者将原始动画 分层合成 到一个 静态背景 上。例如...

  magick bunny_grass.gif \( bunny_anim.gif -repage 0x0+5+15\! \) \
          -coalesce -delete 0 -deconstruct -loop 0  bunny_bgnd.gif
  gif_anim_montage  bunny_bgnd.gif  bunny_bgnd_frames.gif
[IM Output]
[IM Output]
现在,所有帧都显示得一样好,因为背景已重置,所以不会出现暂停。但是,该动画现在已成为一个 覆盖动画,背景由于动画对象的移动而被“重新绘制”,因此文件大小可能略大。请参阅 透明度优化 以了解上述结果的优化延续。
IM 论坛成员 el_supremo,Pete,贡献了一个 MagickWand 等效脚本,清除帧到背景示例

此示例也在 IM 论坛中进行了详细讨论,在 MagickWand 中创建清除帧 GIF 动画

混合处置动画- 多背景动画

没有什么可以阻止您在一个 GIF 动画中混合各种处理方法。事实上,向 清除帧动画 添加背景就是这么做的。对于我们人类来说,使用混合处理方法并不那么简单,但这可以让你生成一些非常复杂的动画显示。通常,它们是在对特定动画进行自动 帧优化 的一部分中创建的。请记住,结果将无法直接用作以前动画类型的特定目的。事实上,不要惊讶,如果某些 GIF 处理程序无法正确处理“混合处理动画”。这包括一些可用的 GIF 优化器。典型的“混合处理动画”示例是,一个小的移动物体导致动画背景发生一些半永久性但暂时静止的变化。球击打杠杆就是一个例子。
Simple Example wanted
类似地,涉及两个非常小的移动物体在更大的透明显示器上的动画只能通过混合处理技术来优化,以便每个物体使用单独的动画帧来移动。
FUTURE,
A more complex animation for study.
 * Start with a semi-transparent canvas
 * run a little 'previous' disposal
 * leave one frame as a new 'canvas'
 * more previous animations
 * erase that 'addition' using a 'background' disposal set as new canvas
 * continue back to start point.
This animation should thoroughly test out not only IM disposal methods
but also various browser disposal methods.
如果您想贡献一个此类动画的 IM 示例,您可以在此处获取您的姓名和主页链接!

循环的结束- 当动画停止时

通常认为,不要让动画永远循环是一个好主意,因为客户端机器在动画仍在动画时必须不断工作。因此,考虑动画实际循环多少次是一个好主意。基本上...
向您的动画添加循环限制(如果可行)
对于用作网页顶部徽标的大型动画来说尤其如此。根据动画运行的总时间,10 到 30 个循环通常足以用于大型徽标。您应该为所有此类动画添加循环限制,以对您的用户友善。“-loop”保存设置 0 表示永远循环。这通常是使用的方法,对于小型动画来说是可以的。但是,一些浏览器会在动画达到某个内部循环限制(通常为 256 个循环)时强制停止。对于 IM 示例,我通常使用“0”的“-loop”保存设置,表示“永远循环”,因为动画可能会出现在页面上的任何位置。这意味着用户从加载页面到最终查看并阅读特定 GIF 动画可能需要一些时间。如果我使用较少的“循环”次数,那么动画将不再执行它应该执行的操作,从而失去其有效性。对于顶级 Flash 页面上的大型动画,可以使用“1”的“-loop”,即正常的 GIF 动画默认设置。这意味着仅运行动画一次,然后停止。这引出了一个非常重要的问题。.. **在哪里停止?** 一些浏览器,比如旧的“Netscape”浏览器,会重新显示动画的第一帧。但是,大多数现代浏览器只会停止在最后一帧,忽略该帧无用的 处理 设置。但是,特定应用程序的具体行为取决于应用程序本身,因为没有真正的标准。实际上,即使是“循环”元数据的使用本身也是非标准的,只是旧的“Netscape”浏览器实现的功能,所有后来的浏览器都将其复制。因此,如果您希望动画在某个特定帧上停止,例如包含您的公司名称或徽标的帧,那么最好将该帧设置为动画的第一帧和最后一帧。但是,其中一个帧应设置“零延迟”,以免影响动画的整体循环时间长度。因此,为了总结一下...
如果循环受限,则将“停止”帧添加为第一帧和最后一帧。

零延迟中间帧

我们已经看到了使用“零延迟”帧的情况,关于 清除帧动画。我还用它们来解释 之前和背景处置。这些特殊帧实际上比人们想象的要常见得多。它们不仅代表了在动画中添加背景“画布”的方法(就像我在上面使用它们那样),而且对于一些更复杂的 帧优化 技术也是必需的,比如 帧加倍拆分帧更新。另一种(非常古老的 PNG 格式之前的)用法是允许你创建包含超过 256 色限制的静态 GIF 图像!也就是说,每一帧提供 256 种颜色,下一帧提供下一组 256 种颜色,所有这些都以零延迟完成,并且在最后没有循环。感谢 TLUL创建未量化的 GIF 的讨论中指出了这一点。这些“零延迟中间帧”并不意味着要展示给用户。它们只是用于在 GIF 图像中创建特殊效果,而这些效果在没有它们的情况下是不可能实现的,或者比没有它们时更优化。总之...
零延迟帧是中间帧
它们不应向用户可见
ImageMagick 不仅会在动画中创建这样的帧作为其自动“OptimizePlus”的一部分,而且还提供了一种使用“RemoveZero”层方法来删除它们的方法。注意它们,因为它们通常会使你处理动画变得复杂。好吧,它们很重要,那又怎样呢?因为许多应用程序不喜欢它们,或者错误地处理它们。他们认为“零延迟帧”是件坏事,即使你出于某种原因故意将它们添加到动画中。以下是我所知或被告知“做错了事”的应用程序摘要...
Gimp 不会保存“零延迟帧”,它们总是为任何具有零时间延迟的帧添加一个最小的延迟。:-(
 
FireFox 会在这样的帧上稍作非零暂停。这大概是为了让没有任何时间延迟的动画不会占用计算机的所有 CPU 周期。但“firefox”仍然不会放松这种限制,即使动画有一个总的非零显示时间。
 
Internet Explorer 有 6 毫秒的最小时间延迟,并且忽略任何小于此时间的延迟。Internet Explorer 8 版本也会失败(立即重新启动循环),如果任何图像帧超出第一个帧设置的动画边界。我认为这是一个重大错误。
另一方面,ImageMagick 的“RemoveZero”层方法确实做了正确的事情,并且如果所有图像都具有“零时间延迟”,则不会删除任何帧。事实上,如果该层方法看到一个完全没有时间延迟的动画,它会给出警告。这让我们想到了另一个经验法则...
永远不要保存一个没有任何“延迟”的 GIF(循环)动画
这样做是一种非常糟糕的做法,也是上面大多数“有 bug”的应用程序之所以那样做,而不是它们应该做的事情的原因。如果你看到它们,请向所有者投诉。也请向应用程序开发人员投诉,以便他们正确地处理零延迟帧。即使这意味着根本不显示该帧,只是将其用作准备显示下一帧。毕竟它们只出现在屏幕上 时间!