ImageMagick 示例 --
动画优化
这些示例开始使用基本动画处理,尝试优化动画的最终显示和文件大小。这对于复杂的 GIF 动画尤其重要,其中可以使用较小的子帧叠加,以及三种控制动画处理方式的处置方法。
如您所见,“
动画使用背景处置进行了完美的帧优化。此操作符将适用于所有 GIF 动画,并且通常会返回可能的最佳简单“处置和帧优化”。
现在,关于任何类型的简单帧优化(例如 IM 提供的)的一些坏消息……虽然“
有时,图像的最佳优化根本不涉及叠加任何像素!例如,右侧是一个简单的动画,由 nixscripter 贡献。如果我们查看它的帧,我们可以看到它没有得到很好的优化。但请注意,动画的每隔一帧只是重复的。
帧优化后,我们得到了一个非常特殊的 GIF 处置序列。
发生的情况是,IM 选择使用“前一帧 GIF 处置来恢复第一张图像,而不是叠加原始帧。由于恢复的帧保持原样,因此没有更改的像素。因此,子帧叠加减少到零。不幸的是,IM 或 GIF 格式都不允许您拥有大小为零的图像,因此改为使用一个特殊的透明像素最小图像。此图像称为丢失的图像,因为它在“
字节的动画文件。因此,让我们尝试对此动画进行简单的帧优化。
等等,什么也没发生!IM 能达到的最佳优化是根本没有任何变化!上面这个动画的合并版本是否是其最优化的版本?好吧,对于这个动画本身来说……是的,这确实是通过纯帧处理优化所能达到的最佳简单优化!不太好。问题在于,GIF 动画要“清除”或“擦除”前面帧绘制的像素,需要使用“背景”处理方法。尽管在某些特殊情况下也可以使用“前一帧”处理方法。但是“背景”处理只能清除刚刚覆盖的区域。由于第一帧完全覆盖了整个图像,因此整个图像都会被清除。即使动画中只有一小部分需要清除其像素。结果,需要覆盖第二帧的全部内容,即使该帧的大部分内容之前刚刚显示过!这种糟糕的“两难境地”一直持续到动画的其余部分,没有产生任何基本的帧优化。我确实说过这个动画很难进行帧优化。
通过复制第一帧,动画的大小从
字节减少到
字节。因此,即使动画现在有五帧,但由于子帧图像叠加大小的大幅减少,其整体大小现在要小得多。复制实际上将处理方法的像素清除功能与下一帧执行的像素叠加功能分离开来。处理和叠加都是作为 GIF 动画程序同一帧更新的一部分完成的,因此不应该注意到速度或质量的损失。这是一种复杂且棘手的技术,GIF 动画设计人员或 GIF 优化程序很少见到或理解,但如果需要,其好处是值得的。然而,子帧图像大小的减少只会持续很短一段时间,因为后面的帧也必须清除下一帧的像素,因此帧会再次变大以继续清除后面的像素。也就是说,因为像素清除总是导致帧变大,而不是变小。所以让我们尝试复制所有帧(最后一帧除外,它永远不需要复制),看看这对最终图像有什么影响……
如您所见,虽然我们几乎拥有两倍的帧数,但所有图像的大小都小得多,生成的动画大小为
字节,结果更小,尽管没有我们执行的第一次单帧复制那么大的节省。为了便于您了解发生了什么,'
也就是说,IM 给你的结果与我们之前的帧复制示例相同。因此,GIF 文件的大小仍然为
字节。但是“
现在我们有了动画的原始合并形式。有关删除额外帧的另一种方法,请参阅下面的“
请记住,添加的中间帧与周围用户显示的帧(具有非零时间延迟的帧)不同。这不是简单的“帧复制”,而是将两个距离较远的微小更改分开。添加中间帧不是一个可以自动化的简单步骤。尽管有可能开发出一种智能启发式方法来生成这些中间帧,但并不总是清楚应该做什么,更不用说是否应该做了。如果您想尝试生成这样的启发式方法,请给我发邮件。所以让我们在添加这些额外帧后尝试标准帧优化……
添加这些“零延迟中间帧”使此动画能够比原始未优化的动画更好地进行帧优化,生成一个
字节的动画。但是对于这种情况,它不如使用自动帧复制技术(参见上面的“
此动画现在每个帧更新有两个额外的“零延迟中间帧”。第一个填充旧孔洞,第二个清除一个将包含透明像素的区域,最后恢复不应该清除的像素。结果是针对此特定问题动画可能实现的最优帧优化,最终文件大小为
字节。也就是说,我们的 4 帧动画通过添加 6 个额外的零时间延迟帧而变得更小!超过原始帧数的两倍。很奇怪但却是真的!当然,如果 GIF 动画程序能够真正识别零延迟中间帧的本质,即动画真实帧之间的中间更新,那就太好了。但即使如此,当更新高度分离且非常小时,额外帧造成的轻微暂停也很少可见。
当然,如果动画的两个分离部分实际上没有关联,那么它们不需要时间同步。另一种选择是,与其添加额外的帧,不如将动画拆分为两个完全独立的动画,您可以在网页上一起显示它们。请参阅拆分动画。但是,此特定动画无法拆分为单独的时间不相交的动画。首先,距离较远的更改需要时间同步。其次,四个发生更改的区域在水平和垂直方向上都重叠。这意味着简单的 HTML“表格”无法将子动画重新组合成一个完整的整体,除非使用某种 CSS 技巧。你能证明我错了?未来:在“动画处理”中引用一个关于“两个距离较远的物体”动画的更好的例子,例如涉及两个分别移动的物体。
这再次将动画恢复到仅用户可见的帧,简化了动画。当然,在移除零延迟中间帧之后,很难重新添加它们,因为更改信息已丢失。因此,动画之后可能无法很好地进行帧优化。毕竟,优化是此类帧的主要目的之一。
我故意将动画保留在 IM 内部 MIFF:文件格式中,因为这确保了原始图像在未经修改的情况下得以保留,并使用 PNG:文件格式显示帧,以便您可以看到其中包含的所有半透明像素!这不仅对于具有半透明像素的动画很重要,而且对于具有大量颜色的动画也很重要。一旦图像序列保存到 GIF 中,您生成良好颜色优化的机会就会从良好变为困难。
结果看起来与我们想要的完全不同。默认的 50% 透明度处理使动画看起来像一个收缩的“蛋”。绝对不是我想用这个动画实现的目标。如果这种类型的透明度处理是可以接受的,那么这就是在继续其他优化之前应用它的方法...
但是,对于像这样的动画应用阈值并不是一个好的解决方案,因为它确实破坏了我试图实现的透明效果。
这消除了动画中的所有透明度,但代价是只能让动画在特定背景颜色下工作。但是,如果您正在为特定的网页创建动画,这可能是可以接受的。但是请注意,对于具有清晰轮廓的图像,使用这样的抖动图案可能会在锐利边缘产生“点状”轮廓。因此,不建议用于一般情况。另一种解决方案是尝试生成一些透明和不透明像素的图案,以试图保留图像的半透明度。为此,IM 提供了大量可以解决此问题的抖动选项。未来:一些指向关于透明度抖动的待创建部分的链接,例如量化和抖动。请注意,使用单色抖动 alpha 通道的明显第一个解决方案并不简单,可能需要一些高级多图像合成才能正确执行。
结果还可以,但看起来像是物体溶解而不是传送。
因此,如您所见,处理 GIF 动画中的半透明度有很多可能性。
请注意,我没有将动画直接保存到 GIF 格式,而是先将其保存到 MIFF 格式文件中,“
如您所见,动画中的每个图像都有非常大量的颜色。每个帧不仅具有不同的颜色数量,而且第一帧和第三帧在颜色方面非常相似,尽管并不完全相同。但是 GIF 文件格式每个帧最多只能保存 256 种颜色,ImageMagick 将其保存到 GIF 格式时,采用了最快且最笨拙的方式...它减少了动画中每个帧的颜色数量(一个称为颜色量化的过程)...
由于每个帧中减少的颜色数量略有不同,因此 IM 还需要为动画中的每个帧提供单独的颜色映射。这意味着 GIF 文件有一个“全局颜色表”,它始终存在,但还有三个单独的“本地颜色表”。“
个局部颜色表,比图像中存在的帧数少一个,正如我预测的那样。每个帧不仅具有不同的颜色集,还具有略微不同的颜色模式(图像抖动模式),如 错误校正抖动的问题 中所述。通常,IM 的此默认操作 颜色量化和抖动 非常出色,非常适合图片,尤其是真实照片。事实上,动画的各个帧通常看起来很棒。所有问题都出现在我们尝试将这些单独的颜色减少的帧串联成单个动画序列时。
上面示例中的红色区域显示了两个差异区域的两个实心正方形区域,正如您所预料的那样。但它还显示了勾勒出两帧背景的颜色差异带。这些表示背景渐变边缘的“翻滚”抖动模式,其中使用了不同的彩色像素来表示完全相同的背景。这也是显示使用不同的颜色集和抖动模式造成的背景干扰最少的帧对。实际的连续帧差异要糟糕得多,产生了近乎实心的红色差异。
由于动画中的如此多像素在一帧到下一帧之间是不同的,因此当我们尝试 帧优化 动画时,根本无法优化也就不足为奇了……
但是,动画帧不变部分之间的像素颜色差异大多数实际上都相当小。如果情况并非如此,那将不是一个很好的 颜色减少。这意味着通过要求 IM 放松其颜色比较,您可以要求它忽略细微的颜色差异。这是通过设置适当的 模糊因子 来完成的。
如您所见,通过添加一个小 模糊因子,IM 现在将忽略仅略有不同的像素,从而产生合理的 帧优化。您需要多少模糊因子取决于 IM 在颜色减少原始图像时遇到了多少麻烦。在这种情况下,并不是很多,因此只需要一个非常小的因子。如果一个小模糊因子产生了可接受的结果,则只需将其设置为您的 帧优化 和 透明度优化。请记住,您仍然需要为每一帧处理一个单独的颜色表,这是下一个讨论点。另请注意,帧优化 决定对第二帧使用“先前处理”。也就是说,在显示第二帧后,在覆盖之前将图像恢复到上一帧处理(第一图像)。这导致覆盖图像大小比始终不使用处理时要小。如果您只需要一个简单的 叠加动画,并且始终仅使用 无处理,则可以使用旧的 分解 运算符(也称为 图层比较任何)来生成它。
现在,如果您使用“
为了简化操作,IM 还提供了一个特殊的选项“
这导致在生成的图像中产生了
个“局部”(或额外的不需要的)颜色表。我将在接下来的优化部分中使用动画的单一颜色表版本,尽管您实际上可以在动画优化的任何时间点执行此操作,尤其是在最终保存之前。由于颜色表优化,动画在我们的直接转换的 GIF 中为
字节,现在在使用“
字节。动画具有的帧数(和“局部颜色表”)越多,节省的空间就越大。现在,由于对动画的任何修改通常都会删除每个图像的保存调色板,因此在将动画保存为 GIF 之前,“
正在建设中
但是请注意,在我们迄今为止研究的所有技术中,都可能存在一个抖动模式,该模式从一个叠加层更改为另一个叠加层。像素的翻滚看起来像电视静电。
现在让我们使用“
如您所见,子帧现在具有较大的透明区域,这些区域不会影响最终的动画结果。需要更改像素的区域仍然会叠加,但不会更改的区域已变为透明。这包括动画对象内部,也留下了相当可怕的“孔”。由于较大的恒定透明彩色区域(理论上)会更好地压缩,因此生成的“杂乱”动画要小得多,将文件大小从帧优化的结果
字节减少到
字节。对于非常小的努力,这是一个相当大的节省。请注意,优化方法不需要是合并动画,并且子帧的大小保持不变,以保留此帧和后续帧的处置需求。因此,任何节省都只是在相同数量的动画像素的压缩率方面有所提高,而不是实际保存到文件中的像素数量。因此,应在完成任何所需的帧优化后,将其作为最终优化步骤之一执行。
动画优化简介
优化动画并不容易,尤其是 GIF 动画,它具有颜色限制,以及不同的帧处置技术的选项,以及能够从一帧到下一帧使用较小的“子帧”叠加。在优化动画时,应尝试按照以下顺序进行优化。然而,这不是我们查看这些优化技术的顺序。对于 GIF 动画,帧优化是最基本的优化技术,并且可以从中获得最大的收益。因此,我们将首先对其进行探讨。用户可能难以处理的优化方面可能是颜色优化,这是由 GIF 动画的颜色限制引起的。其中一个方面单个全局颜色表必须作为保存为 GIF 之前的最后一步完成,否则您可能会丢失操作员对最终 GIF 文件保存的影响。ImageMagick 的通用 GIF 优化器
“-layers
”方法“Optimize
”将使用我们将在下面详细讨论的一些技术,尝试在一个合理的步骤中优化 GIF 动画。目前,此选项等效于(按顺序)…此时,您可以立即保存 GIF 动画。这些是可以在大多数动画序列上应用的相当安全的优化步骤,但是不能保证它会导致 GIF 动画变小。对于原始视频序列,情况尤其如此,其中透明度优化通常会导致 LZW 压缩率下降。但是对于大多数涉及卡通图像的 GIF 动画,“Optimize
”操作符应该会产生一个良好且经过良好优化的动画。但是,该操作符仍在开发中,将来可能会包含其他标准优化步骤,例如…- alpha 通道的 50% 阈值,就像 IM 在保存到 GIF 文件格式时通常所做的那样,以删除半透明像素。如果您愿意,您仍然可以提前自己进行半透明处理以覆盖此操作。有关更多详细信息,请参阅GIF 布尔透明度。
- 某种颜色优化技术。确切是什么,还有待确定,并且可能会根据动画和涉及的颜色数量进行选择。欢迎提出建议。
- “
+map
”操作的单个全局颜色表。
Optimize
”最终成为 IM 通用 GIF 动画优化器,以便 IM 用户能够快速轻松地使用。在此之前,请谨慎使用它,尤其是在脚本中,因为它会发生变化。当然,对于特定动画而言,许多优化步骤可能不值得付出努力。此选项也可能变得非常慢。这是计划,也是此 IM 示例部分所期望的目标。帧优化
帧优化基于叠加较小的子图像,而不是整个图像的完整叠加。这显然会产生较少的像素数量,从而产生较小的磁盘文件,以便发送到网络。此外,叠加较小的帧意味着客户端计算机不必在更改屏幕上的像素方面做太多工作。但是,GIF 格式中提供了不同的处置方法来处理显示的最后一帧,这会导致不同的叠加大小。不仅如此,还可以将叠加拆分为多个部分或更新操作,从而带来更复杂但更优化的动画。由于执行帧优化的复杂性,任何现有的帧优化通常都会首先使用“-coalesce
”操作删除。请参阅合并示例。当然,这意味着任何可能存在的手动优化也会被删除,因此建议谨慎操作。基本帧优化
“-deconstruct
”方法将为 GIF 动画生成基本帧优化。但是,正如上一节的分解示例所示,当涉及透明像素时,此操作符并非适用于所有 GIF 动画。具体来说,当动画将任何彩色像素清除为透明时。也就是说,它仅适用于叠加动画。“-layers
”方法“OptimizeFrame
”旨在成为 GIF 帧优化器,它将尝试使用任何 GIF 处置方法找到最小的子帧叠加图像。结果通常是混合处置动画,尽管通常它也会生成清除帧动画或纯叠加动画,如果确定这是特定动画的最佳解决方案。请记住,输入动画必须是“合并动画”,因此它由一系列完整的图像帧组成,所有帧的大小相同,没有任何画布偏移。当然,合并动画中任何现有的处置方法都完全无关紧要,并且会被“OptimizeFrame
”方法忽略。例如,让我们尝试使用上一节中创建的处置前一帧动画。
|
![]() |
||
![]() ![]() ![]() ![]() |
-layers OptimizeFrame
”正确地将我们的动画恢复到其原始帧优化形式,使用前一帧处置。此优化甚至可以正确处理更难以处理的背景处置动画…
|
![]() |
||
![]() |
现在,关于任何类型的简单帧优化(例如 IM 提供的)的一些坏消息……虽然“
OptimizeFrame
”返回 IM 可以计算出的给定动画的最佳帧优化,但有一些特殊情况它效果不佳。这些包括…- 需要像素清除(恢复为透明)的动画,但帧叠加过大,无法有效清除需要清除的小区域像素(请参阅下面的移动孔洞动画)。
- 涉及两个或多个彼此远离的小变化区域的动画。这些实际上非常常见,并且难以进行帧优化。(请参阅下面的分割帧更新)
- 具有非常复杂的背景且长时间(超过 3 帧)保持静止,然后稍微更改然后再保持静止一段时间,等等,等等……或者一个静止的背景在很短的时间内变得非常模糊。对于任何计算机算法来说,在这种复杂的情况下找出“最佳”帧优化几乎是不可能的(即:什么应该被视为静止背景?)。只有人类凭借他们对所见内容的直观理解,才能在这种情况下生成良好的优化帧叠加序列。
无像素覆盖- 每隔一帧重复图像
![[动画]](../images/paddleball.gif)
帧优化后,我们得到了一个非常特殊的 GIF 处置序列。
|
![]() |
||
![]() |
-crop
”“错过”实际图像数据时也会被广泛使用,从而产生相同的结果。此图像实际上仅保留帧的元数据,例如:处置方法、时间延迟和循环迭代。因此,它是动画的重要组成部分,即使它是“空”的。因此,通过叠加一个最小的单个透明像素,IM 在此动画中节省了大量空间(和时间)。移动孔洞动画- 难以帧优化
这是一个 GIF 动画的极端案例,通过任何正常的优化方法都无法很好地进行帧优化。此动画基本上由一个简单的、不变的背景图像组成,但背景上有一个透明的“孔洞”,该孔洞从一帧到下一帧改变位置。为了创建它,我需要制作一个合并的图像序列,在其中使用图层 Alpha 合成在一个固定的背景图像中剪出一个孔洞。我还使用了“+antialias
”开关以确保仅使用四种颜色:三种蓝色和透明度。因此,我们不需要处理颜色优化问题。![[IM 文本]](moving_hole_size.txt.gif)
|
![]() |
||
![]() |
帧加倍- 一种对“孔洞”进行帧优化的方案
然而,并非一切都失去了希望。通过向动画添加一些额外的帧,您可以为“OptimizeFrame
”方法提供一些空间,以便更好地利用 GIF 处理方法。例如,我们通过复制第一张图像来添加一个额外的帧,但将其时间延迟设置为零,以免改变动画的整体时间安排。
|
![]() |
||
![]() |
![[IM 文本]](moving_hole_size.txt.gif)
![[IM 文本]](moving_hole_dup_size.txt.gif)
|
![]() |
||
![]() |
![[IM 文本]](moving_hole_double_size.txt.gif)
背景
' 帧与前一帧完全相同,对显示内容没有任何更改。但是,它定义了在叠加下一帧图像之前需要清除的动画区域。然后,以下“无
”帧填充需要更改的像素,以及前一帧处理也清除的像素。在上面的动画中,这意味着需要塑造新孔洞的像素,以及用于填充先前“孔洞”的像素。结果更小,但没有那么多,因为添加额外的帧确实有其自身的成本。至少每个添加的帧也没有自己的调色板,否则这个动画实际上会变得更大,因为额外的调色板的大小!图层优化加- 自动帧复制优化
我很高兴地说,从 6.2.7 版本开始,IM 现在可以自动进行帧复制优化,作为其正常的帧优化处理的一部分。但是,添加帧以使动画更小是一个非常激进的举动,因此它被赋予了自己的单独的“-layers
”方法“OptimizePlus
”。例如,让我们让 IM 进行帧复制优化……
|
![]() |
||
![]() |
![[IM 文本]](moving_hole_oplus_size.txt.gif)
OptimizePlus
”只有在生成的动画(3 帧)的当前帧和下一帧的像素数减少时才会进行帧复制,因此我们可以让 IM 决定是否进行帧复制。由于“-layers
”方法“OptimizePlus
”在创建帧优化 GIF 动画时添加了额外的帧,它还会删除任何不必要的或额外的帧,这些帧对最终动画没有任何更改(根据需要合并延迟时间)。也就是说,它还会自动执行“RemoveDups
”(参见下文)。“OptimizeFrame
”方法不会这样做。删除重复帧- 合并连续的重复图像
不幸的是,如果您合并此动画,您也会获得上述添加的所有额外帧。为了让您从合并的动画中删除此类无用的重复帧,提供了一种“RemoveDups
”方法。它将动画中的每一帧与其下一帧进行比较,如果它们相同(颜色相似度由当前的模糊因子设置),则删除第一帧。此外,为了确保动画中的任何时间安排都不会丢失,这两个帧的时间延迟也会合并。例如……
magick moving_hole_oplus.gif -coalesce -layers RemoveDups gif:- |\ gif_anim_montage - moving_hole_oplus_rmdups_frames.gif |
![[IM Output]](moving_hole_oplus_rmdups_frames.gif)
RemoveZero
”方法。分割帧更新- 分别更新两个距离较远的更改
正如您在帧复制中所看到的,通过将“像素清除”与新像素的叠加分开,我们可以减少单个帧叠加的整体大小。但是此动画仍然会产生一些非常大的叠加,这些叠加主要由从一帧到下一帧实际上没有发生变化的像素组成。也就是说,主要叠加帧仅更新两个相当小的区域,这两个区域彼此相距甚远,从而产生单个大型叠加图像。与其尝试同时更新这两个更改,同时还包括这两个区域之间所有未更改的像素,不如分别更新每个区域。也就是说,我们将帧更新分为两个阶段,每个阶段对应一个分离的更改区域。在这种情况下,我们可以先填充孔洞,然后创建新孔洞作为单独的更新。实际上,这两个分离更改的顺序并不重要(除了可能与处理有关),但您应该尝试对此进行逻辑上的考虑。也可能一个更改比另一个更改更容易创建。例如,在这里我插入额外的帧以填充旧孔洞,作为对“挖掘”新孔洞的单独更新。这是更容易生成的中间帧,也是最符合逻辑的动作顺序。当然,您不需要为最后一帧执行此操作,因为该帧在动画循环之前就被丢弃了。
|
![]() |
||
![]() |
|
![]() |
||
![]() |
![[IM 文本]](moving_hole_split_opt_size.txt.gif)
OptimizePlus
”图层方法)。但是添加“零延迟中间帧”并不会阻止您也使用“帧复制”技术……
|
![]() |
||
![]() |
![[IM 文本]](moving_hole_split_oplus_size.txt.gif)
当然,如果动画的两个分离部分实际上没有关联,那么它们不需要时间同步。另一种选择是,与其添加额外的帧,不如将动画拆分为两个完全独立的动画,您可以在网页上一起显示它们。请参阅拆分动画。但是,此特定动画无法拆分为单独的时间不相交的动画。首先,距离较远的更改需要时间同步。其次,四个发生更改的区域在水平和垂直方向上都重叠。这意味着简单的 HTML“表格”无法将子动画重新组合成一个完整的整体,除非使用某种 CSS 技巧。你能证明我错了?未来:在“动画处理”中引用一个关于“两个距离较远的物体”动画的更好的例子,例如涉及两个分别移动的物体。
删除零延迟帧- 删除中间更新
当然,有时您不感兴趣或想要从动画中删除这些添加的中间帧,只留下实际显示给用户一段时间内的帧。您不能只是合并动画并使用“RemoveDups
”方法,因为并非所有“中间帧”都与周围帧相似,因此不是重复帧。但是,由于这些类型的帧具有零时间延迟,因此您可以使用另一种特殊的“-layers
”方法“RemoveZero
”,它将删除任何具有零时间延迟的帧。此方法还会删除使用帧复制和“OptimizePlus
”技术添加的帧。例如……
magick moving_hole_split_oplus.gif -coalesce -layers RemoveZero gif:- |\ gif_anim_montage - moving_hole_split_rmzero_frames.gif |
![[IM Output]](moving_hole_split_rmzero_frames.gif)
帧优化结果和总结
让我们总结一下我们对移动孔动画的优化... 如您所见,通过使用一些复杂的帧处理,在 IM 和一些人工干预的帮助下,我们能够将“移动孔”动画的帧优化到几乎原来的一半大小,尽管帧数是原来的近三倍。当然,不同动画的结果可能会有很大差异,但我们用于帧优化的技术是相同的。它只需要一点小心和预先考虑,人类擅长这一点,而计算机不擅长。![]() ![]() |
问题在于,IM 不仅应该考虑当前正在查看的帧集中的像素数量,还应该考虑添加的额外帧的总体大小,以及可能获得的整体压缩结果,以便在决定如何优化图像帧时做出决定。 另一方面,IM 也不会考虑可能产生的像素数量的节省结果,而不仅仅是直接相关的帧。也就是说,由于帧加倍或使用的处理方法,后续帧的大小也可能更小。当选择是使用“前一图像处理”方法时,这一点尤其正确,该方法可以在动画序列的后面显着减少像素数量,而不是在下一帧中立即减少。这里做出好的选择通常需要人工输入。 因此,我无法保证 IM 会为特定动画做出最佳优化选择。但是,它当然会尝试在不使用递归的情况下做出选择。也就是说,仅使用其决策的直接像素计数。 递归算法,一种做出选择然后查看由此选择产生的动画的最佳最终大小(包括后续的递归选择)的算法,可以产生保证的最佳优化。但是它也可能是一个非常慢的操作符,对于大型动画来说,可能需要数年时间才能做出最终决定。它还需要包含压缩优化选择,因为这些选择可能会影响最终结果。换句话说,虽然这样的算法可以保证最佳优化,但它是以巨大的计算成本为代价的。 当然,对动画试图实现的目标有深入了解的人类通常会在复杂的动画中做得更好,就像您上面在拆分帧更新中看到的那样。 如果您想尝试创建一个递归 GIF 优化操作符,请尽管尝试。我将尽我所能提供帮助。它将击败市场上几乎所有其他 GIF 优化程序。此外,大多数 GIF 动画开发人员可能会非常感谢您的努力(金钱方面)。 |
半透明处理
GIF 文件格式不允许使用半透明像素(请参阅GIF 布尔透明度)。这是一个事实,在您正确优化动画或甚至将其保存为 GIF 格式之前,您需要以适合动画的方式处理可能存在的任何半透明像素。默认情况下,如果您不处理这些像素,IM 将使用 50% 的阈值将这些像素转换为完全透明或完全不透明。但是,这可能不是处理问题的最佳方法,尤其是在包含大面积半透明像素的图像中,例如阴影效果。例如,我想创建一个星际之门阿斯加德传送动画,该动画可以将几乎任何子图像作为被传送的对象。
magick -channel RGBA -fill white \ \( medical.gif -repage 100x100+34+65 -coalesce -set delay 200 \) \ \( +clone -motion-blur 0x20+90 -blur 0x3 -colorize 100% \ +clone -colorize 30% +swap -composite -set delay 10 \) \ \( +clone -roll +0-20 -blur 0x3 -colorize 30% \ -motion-blur 0x15+90 -motion-blur 0x15-90 -set delay 10 \) \ \( +clone -colorize 30% \ -motion-blur 0x30+90 -blur 0x5 -crop +0+10\! \) \ \( +clone -motion-blur 0x50+90 -blur 0x2 -crop +0+20\! \) \ \( +page -size 100x100 xc:none -set delay 200 \) \ -set dispose background -coalesce -loop 0 teleport.miff gif_anim_montage teleport.miff teleport_frames.png |
![[IM Output]](teleport_frames.png)
好的,我有一个动画序列。如果我尝试将其直接保存为 GIF,IM 将只对所有这些半透明像素进行阈值处理。
|
![]() |
magick teleport.miff -channel A -threshold 50% +channel \ ...do further processing now... teleport.gif |
使用上述 DIY 的额外优势在于您可以控制阈值水平。例如,使用“10% ”去除几乎所有存在的半透明像素,使用“90% ”将它们全部设为不透明。
|
![]() |
保留上述动画中所有特殊效果的最佳整体解决方案是只需添加纯色背景。
|
![]() |
一个简单的解决方案是使用扩散像素有序抖动技术,该技术可以仅限于 alpha 通道,以去除半透明像素。
|
![]() |
使用半色调将产生更好的效果,使透明度图案更大胆。
|
![]() |
但是对于此特定动画,我发现使用用户设计的抖动映射生成垂直线(来自水平线抖动图案)会产生一种增强传送动画并去除半透明像素的效果。
|
![]() |
颜色优化
处理半透明像素只是 GIF 文件格式的第一个限制。下一个是动画中每个颜色表最多 256 色的限制。您可以为每个帧提供单独的颜色表。这意味着单个动画可以包含超过 256 种颜色。但是,即使那样也可能并不总是好的主意。如果您只想快速了解可用的颜色优化选项的摘要,我建议您跳到视频到 GIF转换的示例,在该示例中,动画的颜色问题最严重。GIF 颜色问题
GIF 动画在处理颜色方面尤其存在问题,因为它首先不允许半透明颜色,然后每个帧或全局颜色限制为 256 色。最后,除非一个帧中用于像素的颜色在下一帧中也与相同颜色匹配(当图像的那一部分没有改变时!),否则您最好的帧优化将无法很好地工作。这看起来可能是一个简单的问题,但颜色减少本身就是一个极其复杂的领域,需要在 IM 示例中拥有自己的完整部分。颜色问题实际上是您在万维网上找到的大多数 GIF 动画都是卡通类型或外观非常糟糕的原因。尤其是在从动画的较大版本调整大小后。在调整动画大小中,可能需要花费比实际调整大小过程本身更多的精力来进行颜色优化。在这里,我假设您拥有动画的原始源代码。但这并不总是可能的,因此,如果您正在优化修改后的 GIF 动画,则可能需要格外小心。但是,如果您有一个颜色过多的动画,您需要记住的第一件事是...
不要直接保存到 GIF 格式,
使用 MIFF 文件格式, 或 分开的 PNG 图像。
一旦保存到 GIF,您就失去了对 GIF 颜色优化工作的控制,并且您可能得到一个外观非常糟糕的 GIF 动画,使用各种帧优化技术无法很好地优化它。使用 MIFF 文件格式, 或 分开的 PNG 图像。
速度动画- 颜色过多的动画
首先,我们需要生成一个具有大量颜色的 GIF 动画,以便我们能够真正测试颜色优化中涉及的问题。
|
![]() |
||
![]() |
speed.miff
”。这保留了最初创建(或修改)动画的所有方面,包括 GIF 元数据、时间延迟以及图像的所有颜色,而不会失真。仅在保留原始动画后,我才将原始动画直接转换为 GIF 格式。这样我就可以展示上面代码的意图以及为什么我称之为“speed”。这样做也是为了提供一个基线 GIF 动画进行研究和以后的比较。所以让我们看看我们原始动画的各种细节..
|
![]() |
|
![]() |
|
![]() |
|
![]() |
magick identify
”命令无法告诉您 GIF 文件有多少个此类本地颜色表,因为信息过于特定于格式,并且对于 IM 通常执行的图像处理并不重要。但是,更具体的“Giftrans
”程序可以告诉您使用了多少个低级本地颜色表...
正如您所看到的,此动画有 ![[IM Text]](speed_ctables.txt.gif)
颜色优化前进行帧优化?
如上所示,将动画直接保存为 GIF 格式可以正常工作,但您会发现从一帧到下一帧会有相当多的颜色差异,这对以后的 帧优化(如您稍后将看到的)不利。为了防止颜色差异导致此类问题,您可以在保存动画之前进行 帧优化,从而避免引入帧与帧之间的颜色差异。但是请注意,在颜色减少之前进行帧优化会改变颜色减少的动态。优化后的子帧中,通常较少的静态不动区域会出现,这意味着该帧的颜色量化可以降低这些颜色的重要性,从而减少颜色数量。模糊颜色优化
但是,有时您在将原始动画保存为 GIF 格式之前无法访问它。如果您从 WWW 下载了原始动画,则尤其如此。这意味着您已经拥有一个包含所有这些 GIF 颜色失真的动画,这会导致以后优化时出现问题。现在,由于从一帧到下一帧使用了一组略微不同的颜色,并且对动画中的每一帧使用了不同的像素模式,因此每一帧都可以视为一个完全不同的图像。例如,让我们比较第一帧和第三帧,它们共享大量相同的背景图像……
|
![]() |
![]() ![]() |
如果您的源图像使用 JPEG 图像格式存储,则此类图像差异也是一个问题。此格式使用有损压缩方法,即使在 100% 质量下,也会导致图像中出现轻微的颜色差异。但是,差异通常仅限于差异区域周围的光晕,而不是整个图像。 我只能说,除非您计划将一个单一图像用作所有帧的静态背景图像,否则请避免在动画中使用 JPEG 图像。 |
magick speed.gif -layers OptimizeFrame speed_opt2.gif gif_anim_montage speed_opt2.gif speed_opt2_frames.gif |
![[IM Output]](speed_opt2_frames.gif)
magick speed.gif -fuzz 5% -layers OptimizeFrame speed_opt3.gif gif_anim_montage speed_opt3.gif speed_opt3_frames.gif |
![[IM Output]](speed_opt3_frames.gif)
magick speed.gif -fuzz 5% -deconstruct speed_opt4.gif gif_anim_montage speed_opt4.gif speed_opt4_frames.gif |
![[IM Output]](speed_opt4_frames.gif)
生成单个全局颜色表
现在,由于每一帧都有一组不同的颜色,因此 IM 被迫保存图像,为每一帧提供一个单独的颜色表:第一帧的一个全局颜色表,以及后面帧的 3 个局部颜色表。例如,这里我使用了非常简单的程序“Giftrans
”程序来报告创建了多少帧颜色表。对于完全合并(或类似胶片条)的动画,为每一帧提供单独的颜色表是完全正常且合理的,在这种情况下,这不是问题。也就是说,对于非常不同的图像的幻灯片放映,单独的颜色表将产生最佳外观结果。因此,这是 IM 的正常工作行为。但是,所有这些额外的颜色表都非常昂贵,因为每个颜色表都可以占用大量空间。对于图像中的每一帧,最多 768 字节(256 种颜色×每种颜色 3 字节或 3/4 千字节)。不仅如此,GIF 压缩不会压缩这些颜色表,只会压缩像素数据!如果为单独的颜色表分配这么多文件空间是一个问题,尤其是在图像颜色变化不大的情况下,就像大多数 GIF 动画一样,那么您可以让 IM 仅使用所需的全局颜色表,而不是添加任何局部颜色表。---要删除局部颜色映射,所有图像都必须变为调色板类型,并且都使用相同的调色板,对于命令行,您可以通过设置“-map image”来定义命令调色板,您不能使用-colors,因为这适用于单个图像。命令行解决方案是一个特殊的“+map
”选项,它对一个公共调色板进行全局颜色减少,并将该调色板添加到所有图像中。注意,对图像的任何更改都可能使调色板无效,因此,虽然颜色减少应该在执行 GIF 帧和/或压缩优化之前完成,但公共调色板需要放在最后,就在保存之前。如果“+map
”不需要减少图像中的颜色数量,它不会执行此操作或抖动颜色,只需在所有图像中添加一个公共调色板。---如果所有帧都使用相同的颜色调色板,则 IM 可以生成一个单个全局颜色表。在 IM 中,颜色调色板仅通过从使用此类调色板的图像格式中读取它们或使用“-map
”颜色减少运算符为其分配一个来分配给图像。有关更多详细信息,请参阅 使用预定义颜色映射进行抖动。生成此单个颜色表的一种方法是简单地“-append
”将所有帧连接在一起,然后使用“-colors
”命令将颜色数量减少到最小子集(小于 256,或者如果您想要更小的颜色表,则更小)。然后可以使用“-map
”将生成的颜色表应用于原始图像。例如,这里将图像减少为一组 64 种颜色。这使用特殊的 MPR 内存寄存器 将生成的颜色映射分配给“-map
”命令。
|
![]() |
Giftrans
”检查生成的动画,您会发现该图像现在使用单个“全局”颜色表,而不是为每一帧提供单独的颜色表。![]() ![]() |
在将图像连接在一起之前,我使用了“-background ”颜色“None ”,允许您将其用于非合并动画,并且不会增加额外的不需要的颜色。特殊的“ -quantize ”设置“transparent ”颜色空间用于确保 IM 不尝试在其颜色映射中生成半透明颜色。这是无用的,因为我们将结果保存为 GIF,GIF 无法处理半透明。最后,我将颜色减少到 63 种,以留出空间用于透明颜色。一些动画需要透明度,而另一些(如这个)可能以后仍然需要透明度才能进行 压缩优化。 |
+map
”,它将在所有帧上生成一个公共颜色映射(256 种颜色),并全局应用它。这比上面的 DIY 方法简单得多。
|
![]() |
![[IM Text]](speed_map_ctables.txt.gif)
![[IM Text]](speed_size.txt.gif)
+map
”运算符后为 ![[IM Text]](speed_map_size.txt.gif)
+map
”运算符必须是最后一个操作。请记住
删除局部颜色映射应该是保存为 GIF 格式之前的最后一个优化。
有序抖动,去除“静态”


... small number of colors ...对于较小的静止区域进行帧优化,您甚至可以获得矩形静止区域,看起来更糟糕。...有序抖动...目前,请参阅更实用且细节较少的视频到GIF,优化总结。
压缩优化
将动画保存为GIF格式后,通过处理半透明像素并使用颜色和帧优化,还可以通过迎合GIF压缩算法来获得一些较小的文件大小缩减。GIF文件格式可使用的LZW压缩或游程长度压缩,如果找到较大区域的恒定颜色或重复出现的像素序列,则压缩效果会更好。透明度优化
如您在帧优化中所见,叠加的图像通常只是重复已显示的内容。也就是说,它正在叠加GIF处置方法应用后已存在的相同颜色的像素。但是为什么要费心重复这些像素呢?如果您已经在图像中使用透明度,则可以使用透明像素颜色。但是,将这些区域转换为透明度,可以获得更大面积的统一透明像素。与使用需要匹配正在叠加的相同区域的不同颜色的混合相比,这可以更好地压缩。例如,这是一个简单的帧优化,叠加动画...
![]() |
![]() |
-layers
”方法“OptimizeTransparency
”(IM v6.3.4-4中添加)来替换任何更改显示结果的像素为透明度。
|
![]() |
||
![]() |
![[IM Text]](bunny_bgnd_size.txt.gif)
![[IM Text]](bunny_bgnd_opttrans_size.txt.gif)
FUTURE: link to a 'remove background' from animation当然,与大多数其他“
-layers
”方法(比较或优化)一样,您可以指定模糊因子进行调整,“颜色有多相似”。这使您可以处理颜色抖动不良的动画,尽管如果您研究了上面的颜色优化,则不应该出现此问题。免费的动画GIF工具“InterGIF
”也提供了与上面显示的相同类型的透明度压缩优化,但无法支持“模糊因子”以使“接近”的颜色变化也变为透明。我不推荐它,除非在IM不可用时作为替代方案。LZW 优化- (非 IM)
某些应用程序可以进一步优化动画中图像的压缩率,使其更小。但是,这样做需要专门了解GIF图像文件格式通常使用的LZW压缩。基本上,如果LZW压缩算法已经处理了特定的像素序列,它就不会费心将其转换为透明像素,因为这样做不会提高图像的压缩效果。听起来很奇怪,但确实有效。不幸的是,ImageMagick不会这样做,因为这是一个非常复杂的过程,需要大量的技能和资源才能获得合理的启发式方法,以在一般情况下产生良好的结果。但是,我可以使用“Gifsicle
”应用程序的最高“-O2
”优化级别,为您提供此技术的实用示例。
|
![]() |
LZW压缩优化将图像从简单的透明度优化后的![]() ![]() 有损 LZW 优化- (非 IM)另一种压缩改进方法涉及稍微修改像素颜色本身以“接近颜色匹配”,以便增加图像中颜色引用的重复次数。重复的模式自然会更好地压缩,因此可以产生更高的压缩率。先前“Gifsicle”应用程序的一个分支,称为giflossy,也生成一个“gifsicle ”程序,但该程序可以选择以细微的方式修改图像(它是“有损的”)以进一步减小GIF图像的大小,尤其是在动画中。
|