ImageMagick 示例 --
颜色量化和抖动

索引
ImageMagick 示例前言和索引
颜色缩减简介 (涉及的内容)
图像中的颜色 (图像使用的颜色)
颜色量化 (减少图像中的颜色数量)
错误校正抖动 (或伪随机抖动)
阈值抖动方法
有序模式抖动 (使用平铺阈值映射)
DIY 抖动模式和阈值映射 (以您自己的方式抖动图像)
减少颜色数量或替换特定颜色是图像处理中非常复杂和困难的一步,本示例页面涵盖了该主题。这包括确定使用哪些颜色(颜色量化)以及如何在图像上放置这些颜色(抖动和图案)。它还包括位图或双色图像的生成,甚至处理布尔(开/关)透明度。这一点非常重要,以至于颜色缩减或量化通常会在后台自动进行,以便 ImageMagick 可以执行其最初的主要任务,将图像从一种文件格式转换为另一种文件格式,例如 GIF、XPixmap 和 XBitmap 格式。了解工作原理可以使您更好地控制该过程,从而提高存储在特定图像文件格式中的结果图像的质量。

颜色缩减简介

颜色缩减是 ImageMagick 的一个非常重要的方面。例如,要将包含数百万种颜色的 JPEG 或 PNG 图像转换为包含最多 256 种颜色的 GIF 图像,您确实需要能够以高效且有效的方式缩减颜色。在图像格式转换过程中,这通常会在后台自动发生,但也有一些情况下您需要手动执行此操作。减少图像中的颜色数量通常是一个三步过程。
  1. 首先,您通常需要调查图像使用的颜色。不仅要查看实际使用了多少种颜色,还要查看特定颜色使用了多少次。如果只使用一个像素使用了一种特定颜色,那么保留该颜色是没有意义的,尽管有时您仍然需要这样做。
  2. 接下来,您需要以某种方式决定要将图像限制到的最终颜色集。您可能希望 IM 尝试确定特定图像的“最佳”颜色集。在其他情况下,您可能希望使用更通用且全局的东西,该东西可以应用于任何图像。您甚至可能希望专门从将使用的颜色集中添加或删除一种颜色。
  3. 最后,您需要修改图像以仅使用您选择的颜色。最好是希望结果看起来不错,或者您可能希望它压缩、比较或优化效果很好。
为了进一步复杂化问题,这些步骤通常是相互关联的,因为一种替换颜色的方法通常只能使用特定的颜色集来应用。如果您使用的是特定的颜色集,则不需要进行任何类型的颜色调查,或者您可能需要针对特定颜色进行例外处理。基本上,虽然颜色缩减通常在后台自动处理,但至少了解正在发生的事情及其影响是很有益的。

颜色调查

这可能是最不重要的,虽然 IM 为您提供了执行调查的方法,但用户很少出于颜色缩减的目的而这样做。我将把进一步的讨论留给相关部分,提取图像颜色

颜色选择(量化)

有关良好的初始概述,请参阅 维基百科,颜色量化。有四种基本方法可以选择颜色。这四种颜色控制方法:量化预定义颜色映射均匀颜色阈值;它们都有自己的局限性,正如您将看到的。以下是如何使用这四种方法的示例...

  magick colorwheel.png +dither    -colors 32          color_quantize.gif
  magick colorwheel.png +dither -remap colortable.gif  color_predefined.gif
  magick colorwheel.png +dither   -posterize 3         color_uniform.gif
  magick colorwheel.png \
                  -separate -threshold 50% -combine     color_threshold.gif
[IM Output] ==> [IM Output] [IM Output] [IM Output] [IM Output]
最终图像中每种颜色的数量只是一组代表性数量,但在每种情况下大约为 32 种颜色(阈值除外,它只有 8 种颜色)。从这里您可以了解对每种方法的期望。所有其他方法都具有固定数量的颜色(根据运算符的参数),而不管正在缩减颜色的图像是什么。只有第一个方法(“-colors”)将实际根据当前图像内容选择颜色。由于测试图像主要是白色,因此选择了许多较浅的颜色。它使用一种称为“自适应空间细分”的技术(使用八叉树)来调查图像中的颜色。然后尝试选择一组特定颜色来最佳匹配特定图像,在给定限制内。请参阅下面的 颜色量化操作符。“-remap” 允许您为 IM 提供自己的预定义颜色集(请参阅 用户定义颜色映射)。上面使用的颜色映射“colortable.gif” 是一组 32 种颜色,专门为在旧的 X 窗口图标库 中使用而挑选,并且专为卡通图标而设计。(请参阅 AIcon 库,X 图标颜色选择 获取详细信息)。使用“-posterize” 还可以通过数学方式将每个颜色通道划分为一组颜色级别或强度,从而产生“均匀颜色映射”。也就是说,颜色映射中的每个通道都设置为一组恒定的值或强度。最后,可以对图像的所有颜色通道或特定颜色通道进行“-threshold”,实质上使每个颜色通道纯粹是布尔值或开/关。也就是说,每个颜色通道可以被赋予 0 或 MaxRGB(IM 的“Q”级别相关)的值。但是,这只会产生约 8 种颜色的最小集合。一种非常有限的颜色集。阈值也等效于“-posterize” 级别“1”,它会选择 2 种颜色。

应用颜色集

一旦您拥有了一组颜色,下一个问题就是将颜色应用于图像,以便用所选颜色集替换现有颜色。这被称为“抖动”,之所以这样命名是因为它具有“我应该选择这个还是那个?”的非此即彼特性。基本上,抖动的想法是将不同颜色的像素放置在彼此附近,以欺骗人眼看到图像中实际使用的颜色比实际更多的颜色。也就是说,由于人眼“融合”了相邻的颜色,因此图像中该区域的颜色更接近图像的原始颜色。关于抖动的最佳介绍之一是在 维基百科 上,尽管您需要跳过开头关于“音频抖动”的部分。这展示了一组出色的示例,说明在颜色集有限的情况下使用抖动像素模式的好处。颜色替换的基本样式包括...
  • 直接颜色映射(阈值和海报化)
  • 随机抖动(纯粹随机放置像素)
  • 错误校正抖动(像素的伪随机模式)
  • 有序扩散像素抖动(像素的规则模式)
  • 数字半色调(不同大小的点)
直接映射 如果给定集合中最接近的颜色是上面显示的颜色。基本上,您会得到不同区域的实心、不变的颜色。当将其应用于缓慢变化的颜色图像时,例如天空的真实照片,您会在图像中得到条带状颜色,尤其是在原本应该是平滑的色调渐变的地方,例如天空区域。结果通常被认为不是很好。通常认为直接颜色映射只有在徽标、符号、图标和卡通图像中才是可以接受的。它实际上很少是一个选择。这就是为什么您通常必须关闭正常的抖动方法,如果您不想在图像中直接映射颜色。但是抖动也有它自己的问题。一旦对图像进行抖动,颜色模式就会成为图像的一部分。一旦出现这种模式,就很难将其移除。此外,通常不建议对图像多次重新应用抖动,因为这只会降低图像质量。因此,下面大多数量化示例通常会向您展示如何为每种技术创建未抖动版本。这样做是为了让您在抖动隐藏这些信息之前看到所做的颜色选择。随机抖动 是创建的最简单的抖动方法。它也被认为是最糟糕的抖动方法。但是,它有一些特殊用途。在 IM 中,它只能使用两种颜色,因此通常仅限于特殊情况下的位图抖动。有关更多信息,请参阅下面的 带有阈值的随机抖动错误校正抖动 通常被认为是图像间抖动的最佳通用方法,因为它会产生与图像中区域的原始颜色最接近的近似值。它也是目前唯一可以对任何颜色集进行抖动的唯一方法,因此可以用于所有四种颜色缩减技术。请参阅下面的 E-抖动的工作原理 了解详细信息。但是,错误校正抖动有一些严重的 问题,尤其是在图像动画方面。最后两种抖动技术有序扩散像素数字半色调 也被认为是一种好方法,并且对动画效果很好,但目前它不能使用任何颜色集,只能使用固定的一组均匀颜色。它确实提供了一种使用模式为图像着色的方法,使您能够产生其他方法难以产生的有趣效果。
所有这些色彩还原方面的技术都很重要,通过理解它们,你可以改善图像操作的结果,超越 IM 提供的通用默认值。 值得学习。

图像中的颜色

有关图像的信息,例如使用的颜色数量和整体分布,对于试图决定最佳技术的使用方法的程序和脚本来说非常重要。 在这里,我将介绍一些你可以用来确定此类信息的方法,而不仅仅是用于颜色还原。

提取图像颜色

提取调色板

你可以使用冗长的“identify”从图像中提取调色板,使用以下任何方法,这些方法本质上都执行完全相同的事情。

  magick identify -verbose  image.png
  magick image.png miff:- | identify -verbose -
  magick image.png  -verbose -identify null:
  magick image.png  -verbose info:
如果颜色超过 1024 种,则以上任何冗长的识别方法都不会返回调色板或直方图! 因此,对于大型彩色图像,这是一种碰运气的做法,不建议使用,尽管它仍然可能有用。
然而,更好的方法是生成图像的“histogram:”,并提取结果中包含的注释。

  magick tree.gif  -format %c  -depth 8  histogram:info:-
[IM Output]
[IM Text]
info:”输出格式是在 IM v6.2.4 中添加的。 对于此版本之前的 IM 版本,请使用...

  magick tree.gif histogram:- | identify -depth 8 -format %c -
这些方法的问题在于,你获得了颜色的纯文本输出,你需要根据自己的需求解析它们。 但是,从 IM v6.2.8-8 开始,“-unique-colors”操作符会将图像转换为更小的图像,其中包含在原始图像中找到的每种唯一颜色的一个像素,所有像素都在一行中。 这意味着你可以将图像转换为更简单的调色板图像,列出每个存在的颜色。 图像的宽度返回颜色数量,如果你需要实际列出颜色,可以将其输出到“txt:”图像格式。 例如,以下是树图像的调色板。

  magick tree.gif -unique-colors -scale 1000%  tree_colors.gif
  magick tree.gif -unique-colors -depth 16  txt:-
[IM Output]
[IM Text]
这种简化的调色板对于在非常小的文件中存储生成颜色的颜色映射也是非常重要的。 这种映射对于“-remap”颜色还原操作符特别重要。(参见下面 预定义颜色映射)如果你想获得一个包含图像中颜色以及颜色数量的图像,以下是一个颜色直方图解决方案,它是在 IM 论坛讨论中开发的。

  magick rose: -colors 256 -format %c histogram:info:- |
    sed 's/:.*#/ #/' |
      while read count color colorname; do
        magick -size 1x$count xc:$color miff:-
      done |
        magick - -alpha set -gravity south -background none +append \
                unique_color_histogram.png
[IM Output] ==> [IM Output]
请注意,我必须 颜色还原图像,因为内置的“rose:”图像包含 3020 种唯一颜色,这将花费很长时间并生成非常长的图像。 上面显示的玫瑰的 GIF 图像包含相同的颜色还原集。 结果图像仍然包含相同数量的像素,尽管用额外的透明像素填充,并且正如你所看到的,它显示了绿色灰色的优势、强烈的红色,以及非常强的纯白色峰值。 这可能不是最好的通用颜色直方图方法,但它对这幅图像效果很好。
对于“histogram:”和“-unique-colors”操作符,颜色的顺序是未定义的,但似乎是按照红色、绿色,最后是蓝色通道值进行排序的。 这可能不是特定图像的最佳方法,但无法将三维颜色普遍排序为一维顺序。

提取平均颜色

可以使用“-scale”将图像缩减为单个像素,从而快速找到图像的平均颜色。 例如,以下是内置“rose:”图像的平均颜色。 我使用 FX 转义格式输出颜色,该格式返回一个可以直接在 IM 中使用的颜色字符串,无需更改。

  magick rose: -scale 1x1\! -format '%[pixel:s]' info:-
[IM Text]
使用“%[pixel:...]FX 转义 的问题在于,它可能会返回颜色名称,例如 'white' 或 'silver',而不是 RGB 值。 但是,你可以通过使用三个 FX 转义 来模拟这一点,以返回所需的位深度的实际 RGB 值。 例如...

  magick rose: -scale 1x1\! \
     -format '%[fx:int(255*r+.5)],%[fx:int(255*g+.5)],%[fx:int(255*b+.5)]' info:-
[IM Text]
从 IM v6.3.9 开始,有一些新的“-format”转义,它们可以用于提取有关图像的更具体信息,而无需解析冗长的“identify”或“info:”输出。 例如,你可以通过获取图像红色通道图像的 '%[mean]' 灰度值来获取平均红色通道颜色。

  magick rose: -channel R -separate -format '%[mean]' info:
[IM Text]

提取特定颜色

从命令行,有两种基本方法可以从图像中提取特定的像素颜色。 或者使用 FX 转义,例如“%[pixel:...]”或“%[fx:...]”(见上文)在特定像素位置...

  magick rose: -format '%[pixel:p{40,30}]' info:-
[IM Text]
或者,你可以使用“-crop”简化图像,以剪切出你可能感兴趣的单个像素,并使用前面提到的任何方法。 例如...

  magick rose: -crop 1x1+40+30 -depth 8 txt:-
[IM Text]

特定(或接近)颜色的数量

这可以用来获取特定颜色的像素数量或百分比。 你所做的是将不是该颜色的任何东西都变成黑色,然后将该颜色变成白色。 例如,让我们获取 'tree' 图像中“yellow”太阳的颜色数量。

  magick tree.gif -fill black +opaque yellow \
                   -fill white -opaque yellow \
                   -print "yellow sun pixels = %[fx:w*h*mean]\n"   null:
[IM Text]
有一个警告,如果测试的颜色本身是黑色,它将不起作用。 要处理黑色(或非常暗的颜色),请交换填充,将非黑色颜色映射到白色,然后 反转 结果以生成所有黑色像素的白色蒙版。 请记住,“-print”选项等效于使用“-format ... -write info:”,并且可以在图像处理中的任何地方使用。 然后,我使用特殊的“null:”文件格式丢弃了不需要的图像。 你也可以保存图像以供以后使用作为蒙版。 请注意,虽然这对于小图像来说效果很好,但对于更大的图像(如高分辨率数字照片),'mean' 的精度不足以获得准确的像素数量! 本质上,上面使用 'mean' 适合生成比率,但不适合精确的像素数量。 要获得精确的像素数量,最好使用具有精确像素数量的直方图 'comment' 输出(见上文)。 上面的方法还可以使用 模糊因子 选项“-fuzz”,在“-opaque”操作符之前指定 '接近' 的颜色。

比较两种颜色

所以你有两种特定的颜色,你想比较它们。 你可以使用“magick compare”获取 RMSE(标准误差)...

  magick compare -metric RMSE xc:Navy xc:blue null:
[IM Text]
这很好,因为它可以让你获得两种颜色之间的距离,包括值方面的距离,以及从黑色到白色的距离的标准化百分比。 但是,此方法不能正确处理透明度。 例如,比较 '完全透明的黑色' 与 '完全透明的白色'。

  magick compare -metric RMSE xc:'#0000' xc:'#FFF0' null:
[IM Text]
透明颜色实际上应该有零距离,因为完全透明是相同的,无论底层颜色如何。 相反,我们得到了一个 4 维超立方体距离)。 因此,上述颜色距离方法只适合比较完全不透明的颜色。
除了获取实际距离,你还可以使用 模糊因子 来检查两种颜色是否接近。

  magick compare -fuzz 20% -metric AE xc:Navy xc:Blue null:
  magick compare -fuzz 30% -metric AE xc:Navy xc:Blue null:
[IM Text]
但是请记住,如果像素不匹配(错误像素数量),则结果将为 '1'。 要获得实际的 '模糊' 因子距离,该距离将分离值,可以使用 'FUZZ' 度量。

  magick compare -metric FUZZ xc:Navy xc:Blue null:
[IM Text]
“标准化”值表明实际距离为 28.7%。 使用 模糊因子 与计算透明度涉及的 RMSE 不同。 也就是说,因为模糊因子是设计的,所以任何两种完全透明的颜色都被视为相等。 因此,'完全透明的黑色' 和 '完全透明的白色' 是完全等价的(产生 0 或没有错误像素的值)...

  magick compare -metric FUZZ xc:'#0000' xc:'#FFF0' null:
[IM Text]
另一种颜色比较方法是尝试使用适当的 模糊因子 百分比来 替换颜色。 例如...

  magick xc:Navy  -fuzz 20% -fill Blue -opaque Blue txt:
[IM Text]
由于 'Navy' 没有更改为 'Blue',因此它与 'Blue' 的差异大于 20%。 然而

  magick xc:Navy  -fuzz 30% -fill Blue -opaque Blue txt:
[IM Text]
这确实将颜色更改为 'Blue',因此我们现在知道 'Navy' 与 'Blue' 之间的距离介于 20% 到 30% 之间。 要在脚本中执行此操作,请使用类似以下内容...

  fuzz=%1
  color1="red"
  color2="#e00"

  color2=`magick xc:"$color2" -format '%[pixel:s]' info:`
  result=`magick xc:"$color1" -alpha set -channel RGBA -fuzz $fuzz \
            -fill $color2 -opaque $color2 -format '%[pixel:s]' info:`
  if [ "$result" = "$color2" ]; then
    echo "Colors match according to Fuzz Factor"
  else
    echo "Colors DO NOT match"
  fi
特殊选项“-alpha set -channel RGBA”对于允许我们对透明和接近透明的颜色进行模糊匹配非常重要。

颜色量化

颜色量化操作符

颜色量化最主要的工具,也是所有自动颜色还原内部使用的是“-colors”操作符。 它实现了“自适应空间细分”颜色还原算法,并且是一个非常好的颜色还原算法。 以下是一个典型的示例,我有一个 'colorwheel' 图像,它包含很多颜色,我们要求 IM 将颜色数量减少到只有 64 种颜色,使用各种 抖动方法.

  magick colorwheel.png  -dither None       -colors 64  colors_64_no.gif
  magick colorwheel.png  -dither Riemersma  -colors 64  colors_64_rm.gif
  magick colorwheel.png  -dither FloydSteinberg \
                                             -colors 64  colors_64_fs.gif
[IM Output] ==> [IM Output] [IM Output] [IM Output]
默认情况下,IM 会使用 '抖动' 来对图像上的颜色进行阴影处理。 这可以防止颜色在平滑过渡的渐变上发生突然变化。 如果你关闭抖动(使用 'None' 或“+dither”设置),你可以清楚地看到哪些颜色合并在一起,以生成 IM 认为最适合该特定图像的颜色集。 你还可以看到,如果未进行抖动,颜色渐变会导致突然的颜色变化。 当然,这张图像使用的颜色比大多数图像使用的颜色要多得多。 因此,虽然 64 种颜色的限制对于许多图像来说通常是可以接受的,但对于这张图像来说完全不可接受。 换句话说,颜色量化试图找到最适合特定图像的颜色集。 以下是使用极少的颜色对 IM 标志一部分进行颜色量化的示例。

  magick logo: -resize 40% -crop 100x100+105+50\! -normalize  logo.png
  magick logo.png  +dither             -colors 8  colors_8_no.gif
  magick logo.png  -dither Riemersma   -colors 8  colors_8_rm.gif
  magick logo.png  -dither FloydSteinberg \
                                        -colors 8  colors_8_fs.gif
[IM Output] ==> [IM Output] [IM Output] [IM Output]
将其与内置“rose:”照片图像的一些结果进行比较。

  magick rose: +dither             -colors 16 colors_16_no.gif
  magick rose: -dither Riemersma   -colors 16 colors_16_rm.gif
  magick rose: -dither FloydSteinberg \
                                     -colors 16 colors_16_fs.gif
[IM Output] ==> [IM Output] [IM Output] [IM Output]
正如你所看到的,卡通图像比真实照片需要更少的颜色才能产生合理的结果。
IM 目前只实现了一种颜色量化算法,“自适应空间细分”,并且由于它效果很好,因此很少需要添加其他算法。 但是,随着反馈的不断增加,该算法正在稳步改进。

旁白:作为参考,“Gifsicle”程序列出了许多其他颜色量化方法(使用它的“--color-method”选项)。 我不知道这些颜色量化方法与 IM 相比如何。 如果你找到了关于不同颜色量化方法的良好参考,请给我发邮件。

颜色量化内部机制

选择图像中要使用的有限数量的颜色称为颜色量化,这是一个非常复杂的过程,涉及许多因素。 ImageMagick 网站上给出了它的完整技术描述 颜色还原算法。 但是,我将尝试在这里举例说明其中一些更重要的方面。 也许最大的因素是图像中实际使用的颜色。 如果你在图像中很少有像素 '接近' 某种特定颜色,那么选择这种特定颜色是没有意义的。 因此,颜色选择不仅取决于图像中使用的颜色,还取决于 '接近' 该颜色的像素数量。 我可以通过尝试将两幅不同的双色图像还原为一种共同的颜色来轻松地展示这一点。

  magick -size 4x1 xc:blue -draw 'fill red   point 0,0' \
                                                -scale 20 colors_rb.gif
  magick -size 4x1 xc:red   -draw 'fill blue point 3,0' \
                                                -scale 20 colors_br.gif
  magick colors_rb.gif  -colors 1  colors_rb2.gif
  magick colors_br.gif  -colors 1  colors_br2.gif
[IM Output] ==> [IM Output]
[IM Output] ==> [IM Output]
正如你所看到的,最终的单一颜色不仅取决于存在的颜色,还取决于图像中每种颜色的数量。

  magick -size 20x640  gradient: -rotate 90  gradient.png
  magick gradient.png   +dither  -colors 5   colors_gradient.gif
[IM Output]
请注意,颜色量化在当前颜色空间内是统一的。
FUTURE: Just what are the effects of the "-treedepth"  setting?
Mail me if you know

颜色量化和颜色空间

选择颜色的另一个重要影响因素是准确地定义“接近”或“附近”的颜色。这是由用于量化(颜色选择)的颜色空间定义的,并且(截至 IM v6.2.8-6)由 "-quantize" 颜色空间设置控制。当选择非常少的颜色时,"-quantize" 设置变得尤为重要。为了演示,让我们使用各种不同的颜色空间并定义不同的“颜色距离”来减少一个标准的 'colorwheel' 图像。

  for S in    RGB CMY sRGB GRAY \
              XYZ LAB LUV  \
              HSL HSB HWB  \
              YIQ YUV OHTA ; do \
     magick colorwheel.png   -quantize $S   +dither -colors 16 \
             -fill black -gravity SouthWest -annotate +2+2 $S \
             colors_space_$S.gif; \
  done
[IM Output] [IM Output] [IM Output] [IM Output]
[IM Output] [IM Output] [IM Output]
[IM Output] [IM Output] [IM Output]
[IM Output] [IM Output] [IM Output]
正如你所看到的,所选的颜色在很大程度上取决于颜色空间的组织方式。sRGB(红、绿、蓝)颜色立方体通常会导致至少选择接近主色的颜色。sRGB 颜色空间特别适合选择卡通图像和图标的颜色,但实际上对于一般图片来说是一个糟糕的颜色空间。CMY 颜色空间与 sRGB 颜色空间完全相同,因为颜色通道只是被反转以在 sRGB 和 CMY 颜色空间之间进行映射。因此,量化颜色最终会得到大致相同的解决方案。
CMYK 颜色空间(未显示)也产生相同的结果,但原因不同。由于内部“K”通道和图像“颜色映射”使用相同的数据指针(参见 调色板通道),IM 在量化之前将其转换回 CMY。

正如预期的那样,sRGB 颜色空间产生与 RGB 相似的结果,但经过扭曲以去除颜色空间中的近黑色颜色。因此,颜色轮的中心可供选择的颜色更少,产生一个更大的“不太黑的”斑点。XYZ 颜色空间也与线性 RGB 颜色空间非常相似。这里的主要区别是颜色轴已发生偏移,以便更好地包含我们可以看到的所有可能颜色(甚至通常无法看到的颜色),因此颜色轮中的颜色数据被压缩得更多,因此量化似乎变得更加分散。LAB 和 LUV 颜色空间基于彼此不同的但相似的颜色轴。这导致了不同的颜色量化排列。涉及“色调”通道的特殊颜色空间,例如 HSL(色调饱和度亮度)、HSL(色调饱和度亮度)和 HWB(色调白色黑色),在颜色空间中都具有颜色的循环色轮表示。实际上,它是使用 HSL 颜色空间来生成这个色轮的。参见 生成一个色轮
在撰写本文时,IM 使用的颜色距离算法没有考虑颜色空间“色调”的循环性质。这个算法非常不同。因此,“红色”路径上会出现一个强烈的间断,色调在那里环绕,导致颜色量化过程中选择的红色很少。
YIQ、YUV 旨在产生更自然的“柔和”和“中等色调”的颜色,这些颜色更适合于照片和现实世界图像,这些图像涉及微妙的颜色阴影,尤其是肤色。Helmut Dersch 在 他的网站 上指出,你应该考虑使用 LAB 颜色空间进行失真。
在较旧版本的 IM(特别是 IM 版本 5)中,用于量化的颜色空间是使用 "-colorspace" 选项设置的。但是,在 IM 版本 6 中,此运算符用于修改图像在内存中的存储方式,因此它不是颜色量化的设置。

因此,在 IM v6.2.8-6 中,"-quantize" 设置被提供来执行这项工作。但是,它仅仅是 "-colors",颜色量化过程的设置。它不会对使用 "-remap" 和 "-posterize" 等运算符进行的颜色替换和抖动,或者各种抖动技术有任何影响。
有关可用颜色空间的完整列表,请参见 "-colorspace" 运算符。你可以通过查看 随机的纯色斑点 上的示例,看到颜色空间对颜色选择的影响。颜色量化用于使用各种颜色空间减少随机图像中的颜色数量。

量化不会保留颜色

请注意,在以上所有图像中,纯黑色从未被颜色量化实际选中。请记住,图像中只有一个纯黑色像素,而且几乎没有近黑色像素。因此,最终图像中出现的唯一黑色是在图像标记过程中稍后添加的。即使是“GRAY”颜色空间图像也没有产生纯黑色。实际上,所有图像都不包含任何主色或次色,例如:红、蓝、绿、青、洋红!唯一的例外是白色,因为图像包含相当多的纯白色,使其成为“首选颜色”(见下文)。但是,这种情况不是错误!首先,“black”颜色通常不会在上面的示例中被选中,通常是因为原始图像中只有很少的黑色,因此颜色量化通常不关心深色。实际上,它生成了更多更浅的颜色,因为这些颜色在图像中更常见。请参见上一节以了解具体示例。其次,由于量化正在尝试选择与图像中最大数量的现有颜色像素接近的颜色,因此最好的方法是不要匹配“纯”主色或次色,因为这些颜色始终位于所用颜色空间的极端位置。“偏色”往往比“主色”更匹配更多颜色,因此这些颜色更容易被选中。所以让我明确一点…
颜色量化(“
-colors
") 通常会避免选择主色!
从 IM 版本 6.3 开始,颜色量化功能进行了修改,以尝试包含在原始图像中非常常见的颜色。因此,如果图像包含一个单色区域(例如上面的“white”),该颜色通常将包含在最终颜色映射中。这在一定程度上改善了情况,特别是对于“卡通”图像或具有纯色背景的图像。通常会选择“纯色”,以帮助避免 抖动斑点,我们将在下面介绍。颜色映射中的特定颜色解决方案目前,只有一些方法可以确保“特定颜色”被包含在为后续抖动选择的颜色中。一种方法是按正常方式对图像进行量化,但随后输出生成的色图(使用 "-unique-colors")。现在你可以调整色图,使你的特定颜色真正变成那个颜色。最后,你可以使用 重新映射颜色 运算符使用提供的色图对图像进行抖动。色图可能不再是图像的最佳颜色,并且某些其他颜色可能也应该调整,但它将接近你想要的色图。或者,在使用 "-colors" 之前,将想要保留在图像中的特定颜色的大块色块附加(放大图像)。添加特定颜色的大“色块”将使该颜色更有可能在最终色图中被选中。此外,所有其他颜色将自动调整以更好地适应该色图)。如果这有效,则你添加的色块应该保持不变(不进行抖动)。之后,你可以 裁剪 图像以删除添加的色块。如果它不起作用,则 IM 至少应该添加一个接近所需“特定颜色”的颜色,因此在将 重新映射颜色 应用于原始图像之前,只需要稍微调整一下生成的色图即可。如果你尝试这样做,无论成功或失败,请告诉我你的结果。理想情况下,我希望看到一种方法来指定少量特定颜色,这些颜色必须是最终色图的一部分,然后以某种方式要求 IM 为色图中剩余的颜色选择最佳颜色,针对特定图像。

颜色量化和透明度

ImageMagick 默认情况下不仅会生成完全不透明的颜色,还会尝试生成半透明的颜色。通过这种方式,包含透明阴影或其他叠加效果的图像不会丢失这些效果。然而,截至 IM v6.2.6,涉及透明度的颜色量化进行了修改,以便将所有完全透明的颜色视为相同颜色。这是一个线性修改,因此只有半透明的颜色被认为彼此更接近,而不是完全不透明的颜色。由于这种修改,IM 颜色量化仍然会生成半透明颜色,但会更多地关注图像中的不透明颜色,而更少地关注完全透明的颜色。例如,这里我生成一个 彩虹渐变,图像顶部完全不透明,底部完全透明。我在背景图案上显示了图像,这样你就可以看到图像的透明度。

  magick xc:red xc:yellow xc:green1 xc:cyan xc:blue \
          +append -filter Cubic -resize 100x100\!  -size 100x100 \
          gradient: -alpha off -compose CopyOpacity -composite alpha_gradient.png
  magick alpha_gradient.png  +dither  -colors 256  alpha_colors_256.png
  magick alpha_gradient.png  +dither  -colors 64   alpha_colors_64.png
  magick alpha_gradient.png  +dither  -colors 15   alpha_colors_15.png
[IM Output] ==> [IM Output] [IM Output] [IM Output]
正如你所看到的,当我们要求 IM 减少此图像所需的颜色的数量时,它创建了更多不透明的颜色,并为更半透明的部分使用了更少的透明颜色。结果是选择了非常好的颜色分布,尤其是在颜色数量很少的时候。然而,正如我在上面指出的那样,不仅 主色不会被选中,而且完全透明的颜色也不会被选中,原因完全相同。实际上,即使是完全不透明的颜色也不会被选中!换句话说,前面示例中所有颜色量化图像中的颜色都是半透明的。让我明确一点。
当涉及透明度时,IM 颜色量化
可能不会选择任何完全不透明的颜色,甚至不会选择完全透明的颜色!
当然,从 IM v6.3 开始,以及“常见颜色”错误修复(见上面的 量化不会保留颜色),如果图像包含大量不透明和完全透明的颜色,这种情况发生的可能性较小,这很常见。由于某些图像可能包含大量半透明颜色,例如包含烟雾或阴影效果的图像,你可能希望进行试运行,以确保为最终图像中包含的图像选择完全透明的颜色。然后,你可以将最透明的颜色映射到完全透明,并自己进行 重新映射颜色。如果你确实想确保最终图像中包含完全不透明和完全透明的颜色,你可以 规范化或对比度拉伸 Alpha 通道。例如,这里我确保使用 "-contrast-stretch" 使主色选择变得不透明。尽管这对于更正常的情况来说可能有点过于粗暴。

  magick alpha_gradient.png  +dither  -colors 15 \
          -channel A -contrast-stretch 10%  alpha_colors_15n.png
[IM Output] ==> [IM Output] ==> [IM Output]
对于不允许半透明颜色的 GIF 图像、不允许透明度的 JPG 图像,甚至不需要量化即可正常保存的 PNG 图像来说,这 **不是** 问题。只有在特殊情况下,当涉及大量半透明颜色时,您可能需要对图像进行强制颜色缩减,才会出现问题。请记住,对于 GIF 格式,保存半透明颜色是徒劳的。因此,如果您打算自己为这种图像格式进行颜色量化,您需要告诉 IM 在生成缩减后的颜色集时忽略图像透明度。您可以通过使用特殊的 "-quantize" 颜色空间设置 'transparent' 来实现这一点。

  magick alpha_gradient.png -quantize transparent \
                            +dither  -colors 15   alpha_colors_15qt.png
[IM Output] ==> [IM Output]
请注意,颜色量化完全忽略了颜色的透明度,并且完全没有触及图像的 Alpha 通道。这意味着您可以以更适合您的图像的方式处理 Alpha 通道,完全独立于其他颜色。实际上,您可以在使用 "-colors" 之前或之后进行处理,都不会有任何问题。结果不会有任何差异。因此,当您要将图像缩减为带有布尔值或没有透明度的格式(例如 GIF 或 XPM 图像格式)时,建议使用这种量化颜色空间。如果您计算生成的颜色的数量,您还会发现它恰好生成了您请求的颜色的数量。因此,如果您还需要一个完全透明的颜色(可能需要),则需要将 "-colors" 的参数至少减少一个,以便在图像的最终颜色表中为它留出空间。因此,要处理 GIF 文件格式 256 色颜色表限制,您需要将颜色缩减到 255,而不是 256,为完全透明的颜色索引留出额外的空间,如 "-transparent-color" 设置定义。对于更小的颜色表大小,请进行相应的调整。当 IM 保存到 GIF 文件格式时,这种量化行为是自动的,但在您需要在生成全局或共享颜色表时自己进行量化时非常重要。当然,您仍然需要处理半透明像素,这样它们才能符合您希望图像的外观。
FUTURE: This last part will probably move to a new section on 'Dithering
Alpha Channel' to be created in the near future. And a reference to this
section added here. 
以下是一些仅对 Alpha 通道进行抖动以将其转换为布尔值或开/关设置的示例,而不会影响图像中其他颜色通道。

  magick alpha_gradient.png \
          -channel A   -threshold 50%       alpha_dither_threshold.gif
  magick alpha_gradient.png \
          -channel A -ordered-dither checks alpha_dither_checks.gif
  magick alpha_gradient.png \
          -channel A -ordered-dither o8x8   alpha_dither_ordered.gif
  magick alpha_gradient.png \
          -channel A -ordered-dither h8x8a  alpha_dither_halftone.gif

  magick alpha_gradient.png -channel RGBA -separate \
          \( +clone -monochrome \) \
          +swap +delete -combine alpha_dither_monochrome.gif
  magick alpha_gradient.png -channel RGBA -separate \
          \( +clone -dither FloydSteinberg -monochrome \) \
          +swap +delete -combine alpha_dither_monochrome_fs.gif
  magick alpha_gradient.png -channel RGBA -separate \
          \( +clone -remap pattern:gray50 \) \
          +swap +delete -combine  alpha_dither_map.gif
  magick alpha_gradient.png -channel RGBA -separate \
          \( +clone -dither FloydSteinberg -remap pattern:gray50 \) \
          +swap +delete -combine  alpha_dither_map_fs.gif
[IM Output] [IM Output] [IM Output] [IM Output]
[IM Output] [IM Output] [IM Output] [IM Output]
当对 Alpha 通道的副本进行抖动时,您可以使用 "-monochrome" 或 "-remap" 进行抖动,请确保图像是一个纯灰度图像,而不是包含透明度的形状蒙版。如果您没有这样做,您可能会遇到 Alpha 通道仍然存在导致的非线性效果。

有多种方法可以从图像中提取和恢复 Alpha 通道,作为灰度蒙版,以便您可以对其进行抖动。上述方法使用 通道分离合并 来实现。其他方法使用 Alpha 提取 结合 CopyOpacity 合成

误差校正抖动

正如引言中所述,误差校正抖动通常被认为是生成具有缩减颜色集的原始图像最真实表示的最佳选择。它还将自身限制在任何预定义的颜色调色板中,无论它是用户提供的,还是由 IM 颜色量化例程确定的。因此,它是在 IM 运算符 "-colors"、"-remap"、"-posterize" 和 "-monochrome" 提供的一般颜色缩减中合乎逻辑的默认选择。

E-抖动方法

从 6.4.2-9 版本开始,IM 现在提供了多种抖动样式或方法,可以使用 "-dither" 设置进行选择。在此之前,IM 仅限于 Riemersma 抖动希尔伯特曲线抖动 的变体,您可以使用 "-dither Riemersma" 进行设置。现在,您还可以使用 "-dither FloydSteinberg" 选择 Floyd-Steiberg 抖动。您可以使用以下方法查看您的 IM 版本中已实现的抖动方法类型...

  magick -list dither
[IM Text]
例如,以下是使用不同抖动方法进行抖动的色轮。

  magick colorwheel.png -dither Riemersma      -colors 16 dither_riemersma.gif
  magick colorwheel.png -dither FloydSteinberg -colors 16 dither_floyd.gif
[IM Output] [IM Output]
如您所见,Floyd-Steinberg 抖动比默认的 Riemersma 抖动产生了更均匀的抖动模式。它们之间最大的区别在于它们如何将 '颜色误差' 分布到相邻像素之间。因此,让我们看一下 E-Dither 究竟是如何工作的。

E-抖动的工作原理

正在重新编写
IM 用于一般抖动的具体方法是 "希尔伯特曲线误差校正抖动" 的变体。这实际上是一种非常好的抖动技术,定义明确且速度合理。有关完整描述(以及非常相似的变体),请参阅... Riemersma 抖动。基本上,图像中的每个像素都以一种称为 '希尔伯特曲线' 的非常复杂的路径进行查看。像素被分配给最接近该像素值的颜色,并且像素原始颜色与所选颜色之间存在的任何差异都会被保存并添加到下一个像素的颜色值(始终是相邻像素)中,然后再次选择新的颜色。通过这种方式,任何选定颜色与图像原始颜色之间的颜色变化都会分布到同一区域中的其他像素。结果是,虽然最终图像中只会分配特定颜色,但该区域的基本整体颜色将与原始图像非常匹配。例如,以下是一个小灰度图像,我要求 IM 使用一组不包含原始颜色的颜色对其进行抖动。结果图像被放大,以便您可以看到分配的单个彩色像素。

  magick -size 10x10 xc:'#999999' -scale 80x80  dither_not.gif
  magick -size 10x10 xc:'#999999' \
          -remap colortable.gif   -scale 80x80  dither.gif
[IM Output] ==> [IM Output]
如您所见,由于原始图像的颜色不在指定的颜色映射中,因此原始颜色使用指定颜色表中三个最接近的颜色模式进行近似。 如果我们对上述抖动模式生成的颜色的平均值进行计算,我们将得到颜色 [IM Text],它非常接近图像的原始平均颜色 [IM Text],这就是所产生抖动模式的全部意义。但是,由于用于分配颜色的 '路径' 很复杂(尽管通常保留在本地区域),因此颜色分配会产生基本随机的模式。从技术上讲,它并非真正随机,因为相同的图像会产生相同的模式,但结果可能与随机结果相同,或者至少是伪随机结果。"F-S" 抖动实际上只是自 1970 年代初问世以来开发的几种 '光栅化 E-Dither' 中的一种(第一个)。它也可能是最广泛实现的一种,尽管它不被认为是最好的。有关此类算法的更完整总结,请参阅论文 抖动算法。从 IM v6.4.3 开始,它也直接在 IM 中可用,并且已实现为遵循从图像顶部到底部逐行 '蛇形' 路径。

  magick -size 10x10 xc:'#999999'  -dither FloydSteinberg \
          -remap colortable.gif   -scale 80x80  dither_fs.gif
[IM Output] ==> [IM Output]
我发现 "Floyd-Stienberg 抖动" 特别是会产生比 "希尔伯特曲线抖动" 更像 '散列' 的像素模式,事实上它的设计就是如此。这种规则模式可以使小型彩色图标图像的低级手动清理更加容易。这是我在过去为 Anthony 的图标库 做了很多事情,但现在这种事情不再经常需要,除非可能是一些小型单色图像。

E-Dither 问题 - 变化敏感

使用误差校正抖动时遇到的最大问题之一是,您将获得一个基本上随机的像素模式,并且对变化非常敏感。例如,以下是用原始灰度图像替换一个像素,使其成为不同的颜色,然后对其再次进行抖动。结果是希尔伯特曲线抖动所遵循的路径中每个更远像素的抖动模式都会发生完全转变。

  magick -size 10x10 xc:'#999999' -draw 'fill #C28 point 2,2' \
          -remap colortable.gif   -scale 80x80   dither_modified.gif
  magick compare dither.gif dither_modified.gif dither_difference.gif
[IM Output]
原始抖动
[IM Output]
一个像素变化
==>
 
[IM Output]
变化对比
如您所见,只需在图像中添加一个像素就会导致抖动模式发生巨大变化!它只需要一个位的更改,结果图像就会变得不同,即使图像的整体外观(在未放大时)仍然基本相同(这毕竟是良好抖动算法的目的)。"magick compare" 图像还显示了抖动模式变化的程度。在本例中,大约 80% 的像素被分配了完全不同的颜色。在希尔伯特曲线抖动中,单个像素变化实际上会导致每个后续像素可能不同,这意味着抖动模式可能会发生 0% 到 100% 的变化。这取决于变化发生在复杂的希尔伯特曲线的哪个位置。但是,Floyd-Steinberg 抖动只以一个方向遍历图像,因此单个像素变化只会修改该变化一侧的模式。

  magick -size 10x10 xc:'#999999' -draw 'fill #C28 point 2,2' \
          -dither FloydSteinberg -remap colortable.gif \
          -scale 80x80   dither_fs_modified.gif
  magick compare dither_fs.gif dither_fs_modified.gif dither_fs_difference.gif
[IM Output]
FS 抖动
[IM Output]
一个像素变化
==>
 
[IM Output]
变化对比
如您所见,它也存在完全相同的问题。单个像素变化会导致该像素之后处理的图像区域中的抖动模式发生几乎完全变化。也就是说,从该行向下。对于单个图像,抖动颜色的结果模式并不重要。该模式的平均颜色应该使图像具有该区域的适当颜色。但是,当您有一个动画,其中一个图像后面跟随其他非常相似的图像时,并且具有大面积的恒定颜色,那么不断变化的抖动模式就会变得非常明显,并作为低级背景 '噪声' 让人感到厌烦。例如,以下是我生成一个包含 3 个图像的动画,它们使用相同的抖动颜色,但在每一帧中有一个像素变化。我还放大了中心区域,以便您可以更清楚地看到这种变化的模式。

  magick -size 80x80 xc:'#999999' \
          \( +clone -draw 'fill #C28 point 2,2' \) \
          \( +clone -draw 'fill #28C point 2,2' \) \
          -remap colortable.gif  -set delay 50 -loop 0   dither_anim.gif
  magick dither_anim.gif -crop 10x10+40+40 +repage \
                              -scale 80x80     dither_anim_magnify.gif
[IM Output] ==> [IM Output]
如您所见,图像中出现了一种类似于翻滚的背景,这是由 E-Dither 生成的伪随机性引起的。在大多数情况下,使用的颜色足够接近,因此不会使这种 '抖动噪声' 可见。但是,当抖动颜色明显不同(在本例中,由于使用了颜色映射而强制使用)时,它肯定会成为一个问题。请参阅 视频颜色优化,以获取更实用的动画示例,其中显示了这种 '抖动噪声'。模式的变化还会导致动画优化出现问题。也就是说,不同的模式意味着简单的 帧优化 无法减少帧叠加的大小。有关解决方案,请参阅 模糊颜色优化,但该方法仅在翻滚使用非常相似的颜色时才有效。
与其他抖动方法(如 阈值有序抖动)不同,"-channel" 设置不会影响颜色量化或错误校正抖动。基本上,它在这些图像操作的运作方式中没有作用。
有序抖动没有这些问题,包含对更改的直接局部区域的更改。不幸的是,它们通常也仅限于使用数学推导的颜色集。(参见 使用统一色图的有序抖动)。

E-抖动像素斑点

E-抖动另一个问题是,它们可能会在原本颜色相当均匀的区域产生偶尔的奇异颜色像素。例如,在灰度图像中偶尔会出现绿色像素。或者,如以下示例所示,在一个原本平坦的蓝色区域中会出现白色像素。这在包含大量颜色对象的图像以及其他平坦的、不变的纯色区域的图像中尤其常见。在将彩色物体叠加到平坦的彩色背景上的图像中,这尤其常见,因为你经常会在图表和图纸中看到这样的情况。你可以在上面测试示例的放大图中看到这样的奇异颜色像素,其中一个额外的浅紫色像素被添加到距离小单像素更改相当远的距离。然而,添加到上面的奇异颜色像素并不容易看到,并且色图相当好地覆盖了图像,因此奇异像素相当接近用于抖动图像的正常三种颜色。为了更极端的例子,这里我有一个模糊的渐变背景,我将其大幅降色到 64 种颜色,以真正地强调错误校正抖动。

  magick -size 100x60 xc:SkyBlue \
          -fill DodgerBlue -draw 'circle 50,70 15,35' \
          -fill RoyalBlue  -draw 'circle 50,70 30,45' \
          -blur 0x5  -colors 64     speckle_gradient.gif
[IM Output]
如你所见,在这个高度降色的色图中,错误校正抖动在表示原始渐变方面做得相当不错。但如果我们在上面添加一块纯白色……

  magick -size 100x60 xc:SkyBlue \
          -fill DodgerBlue -draw 'circle 50,70 15,35' \
          -fill RoyalBlue  -draw 'circle 50,70 30,45' -blur 0x5 \
          -fill white -draw 'rectangle 40,40 60,55' \
          -colors 64   speckle_problem.gif
[IM Output]
你可以看到,E-抖动突然开始在图像的上部区域产生白色像素的散射,而我们之前那里没有。
这是一个小区域的放大图,这样你就可以更清楚地看到这些像素……

  magick speckle_problem.gif -crop 15x15+75+0 +repage \
          -scale 90x90    speckle_prob_mag.gif
[IM Output]
奇异颜色像素是由两个因素造成的。首先,颜色量化被迫将单一的纯白色(但没有其他白蓝色抗锯齿颜色)包含到图像的最终色图中,从而允许抖动过程使用这种额外的颜色。但随着 E-抖动缓慢累积误差,尤其是在颜色极端的区域,例如上面图像的顶部区域。最终,误差会累积到足够大的值,使一种额外颜色成为最接近的匹配。因此,每隔一段时间就会输出一个高度对比的白色像素来“校正误差”,并且是在一个伪随机位置。结果是白色像素的非常轻微的点状分布。误差积累越慢,这些白色像素就越分散,它们看起来就越不正常。最佳解决方案是切换到一些其他图像格式,这些格式没有有限的颜色表。例如,将你的 GIF 格式图像转换为 PNG。这将避免需要颜色量化(减少),从而避免需要对减少的颜色进行抖动。下一个解决方案是用其他抖动方法替换 E-抖动,这种方法可以“局部化”任何误差,例如 有序抖动。但是,目前在 IM 中应用起来并不容易。参见 更好的有序抖动结果,了解一种这样的方法,直到找到更通用的方法。如果切换到另一种图像格式或使用不同的抖动方法不切实际(通常确实不切实际),那么你就只剩下尝试修复特定图像的状况。最佳修复是确保你有其他颜色,这些颜色正好位于导致 E-抖动误差累积的大组颜色的外部。但是,正常的 颜色量化 并不会这样做。它倾向于选择一组表示颜色组的平均颜色。我们需要的是额外的颜色来“栅栏式”地设置大型颜色组的边缘,而不是简单的平均颜色。例如,这里我使用了一个圆形而不是方形,这样不仅添加了纯白色,还添加了许多白蓝色颜色。这些颜色是由于圆形边缘的抗锯齿而自动添加的,以平滑其外观。

  magick -size 100x60 xc:SkyBlue \
          -fill DodgerBlue -draw 'circle 50,70 15,35' \
          -fill RoyalBlue  -draw 'circle 50,70 30,45' -blur 0x5 \
          -fill white -draw 'circle 50,45 40,40' \
          -colors 64  speckle_fixed.gif
[IM Output]
以及与之前相同的区域的放大图。

  magick speckle_fixed.gif -crop 15x15+85+0 +repage \
          -scale 90x90    speckle_fix_mag.gif
[IM Output]
如你所见,额外的颜色提供了额外的颜色,正好位于蓝青色渐变的外部。现在,虽然这些额外的颜色意味着实际渐变可用的颜色更少,但它们确实提供了其他蓝白色颜色,这将使 E-抖动能够更早、更频繁地自我校正,在累积误差能够变得太大之前。也就是说,我们并不是说我们已经阻止了 E-抖动点状分布,而只是为抖动算法提供了更好的颜色。如果你仔细研究图像的放大部分,你仍然会看到点状分布模式,但颜色更接近背景色,而且有更多这样的颜色,产生了更均匀、更分散的点状分布。另一种方法是生成我们自己的色表,也许是基于 IM 生成的色表,然后添加适当的颜色来防止误差累积。但是,这并不是一件容易的事,尤其是在三维色彩空间中。对于这个特定的图像示例,防止“点状分布”的一种方法是分别生成和抖动背景,使用比需要的颜色略少的颜色,然后叠加白色框及其附加颜色。

  magick -size 100x60 xc:SkyBlue \
          -fill DodgerBlue -draw 'circle 50,70 15,35' \
          -fill RoyalBlue  -draw 'circle 50,70 30,45' -blur 0x5 \
          -colors 63 \
          -fill white -draw 'rectangle 40,40 60,55'  speckle_perfect.gif
[IM Output]
这将为图像添加“白色”颜色,但背景不会有任何点状分布效果,因为在使用错误校正抖动时,白色是不可用的。结果是具有 64 种颜色的图像,而且没有点状分布。但是,这高度依赖于图像和你想达成的目标,因此这不是点状分布问题的一般解决方案。
除了添加额外的颜色之外,更通用的方法是尝试从最终的抖动图像中去除点状分布。也就是说,以某种方式清理图像。但是,这本身就是一个棘手的问题,因为你不知道要删除哪些像素是正常抖动模式的一部分。我们需要的是找到颜色像素,这些像素在某种程度上与周围的所有颜色都非常不同,但同时也与所有其他类似颜色保持一定距离。 你有没有更好的图像滤波器解决方案?总结对我来说,点状分布是一个非常令人讨厌的问题,尤其是对于使用非常有限的颜色表的桌面图标图像。我经常编辑较小的“图标”图像以去除点状分布或修复一些其他抖动效果,例如垂直条带。如果你知道其他更好的解决方案,请告诉我。

单色抖动位图图像

"-monochrome" 运算符是 "-colors" 运算符的专用形式,用于生成位图图像。因此,它是展示“希尔伯特曲线抖动”以及更仔细地观察颜色选择的理想运算符。这是一个典型的示例。

  magick logo.png  -monochrome     monochrome.gif
[IM Output]
运算符仅根据它们的灰度亮度“强度”或“级别”对图像进行抖动,但它不会直接对整个灰度范围进行抖动,而是将最极端的值阈值化为最大值。我们可以通过要求 IM 对渐变图像进行抖动来观察这一点。

  magick -size 15x640 gradient: -rotate 90 \
                   -monochrome     monochrome_gradient.gif
[IM Output]
如你所见,渐变只有大约中间 50% 的颜色被 "-monochrome" 运算符抖动。感谢伊万诺娃 指出 IM 工作方式的这一有趣的事实。如果你想使用整个灰度范围进行抖动,可以使用 "-remap" 运算符,使用纯黑白色图(由内置模式图像提供)。

  magick logo.png  -remap pattern:gray50  mono_remap.gif
  magick -size 15x640 gradient: -rotate 90 \
                   -remap pattern:gray50     mono_remap_gradient.gif
[IM Output]
[IM Output]
通过更仔细地使用 "-remap" 选择颜色,你可以有效地产生与 "-monochrome" 运算符使用的相同“阈值”范围,或你喜欢的任何其他阈值范围。

  magick xc:gray20  xc:white  +append   ctrl_colors.gif
  magick logo.png -colorspace Gray \
          -remap ctrl_colors.gif  -normalize  mono_remap_ctrl.gif
  magick -size 15x640 gradient: -rotate 90 \
          -remap ctrl_colors.gif  -normalize  mono_remap_grad_ctrl.gif
[IM Output]
[IM Output]
"-monochrome" 实际上是先将给定图像转换为灰度图像,然后执行双色 '颜色量化',以确定用于抖动图像的阈值颜色。这是下一部分示例将探讨的内容。
"+dither" 设置目前对 "-monochrome" 的结果没有影响。然而,这在未来可能会改变,因此请确保在使用此运算符时,你的脚本中没有将其关闭。

双色量化

与其自己选择两个控制颜色,不如使用颜色量化,通过使用 "-colors" 运算符来选择图像中最好的两种颜色。

  magick logo.png   -colors 2 -colorspace gray  -normalize \
                                                 colors_monochrome.gif
[IM Output]
然而,结果将与使用 "-monochrome" 不一样,因为我们没有先将图像转换为灰度图像。相反,图像直接在选定的两个非灰色颜色值之间进行抖动。也就是说,选择最好的两种颜色来抖动图像,而不是两种灰度亮度级别。因此,它将为只有大约相同灰度“级别”的颜色图像产生更好的结果。例如,这里我们使用 "-colors" 以及 "-monochrome" 位图抖动运算符,作用于红蓝色渐变。如你所见,结果并不相同。

  magick -size 20x640 gradient:red-blue -rotate 90    gradient_rb.png
  magick gradient_rb.png   -colors 2 -colorspace gray \
                                -normalize        colors_threshold.gif
  magick gradient_rb.png       -monochrome       mono_threshold.gif
[IM Output] [IM Output] [IM Output]
上面的 "-monochrome" 运算符无法找到任何与位图抖动不同的差异,因为蓝色和红色几乎具有相同的强度。然而,使用 "-colors" 量化方法却可以轻松找到可接受的颜色来进行抖动。你还可以看到,只有中间部分的颜色被抖动了。这是因为颜色量化在它选择的两个颜色“簇”的中间选择颜色。因此,在选定颜色“外部”的颜色实际上被直接阈值化为该颜色,而没有进行抖动。这表明颜色量化颜色空间外部的颜色不会被抖动,虽然这个事实很难在实践中使用。
通过在量化之前将 "-colorspace" 设置为灰度,你将复制 "-monochrome" 运算符的内部操作。

  magick logo.png -colorspace gray   -colors 2  -normalize \
                                                 monochrome_equivelent.gif
[IM Output]
最后,通过关闭抖动,你可以产生比使用固定 "-threshold" 设置更自动的颜色分离。

  magick logo.png  -colorspace gray  +dither  -colors 2  -normalize \
           threshold_two_grays.gif
[IM Output]
请记住 "-monochrome" 目前忽略 "+dither" 设置,因此你不能只使用该运算符来进行“智能阈值”。
如果你删除图像处理颜色量化阶段的 "-colorspace",你可以根据图像可能实现的最佳颜色分离(而不是灰度颜色分离)对图像进行阈值处理。

  magick logo.png  +dither  -colors 2  -colorspace gray -normalize \
           threshold_two_color.gif
[IM Output]

使用预定义颜色映射抖动

如上所示,"-colors" 尝试选择最优的有限颜色集来表示图像。使用 "-remap",您可以为 IM 提供要用于图像的最终颜色集,无论您计划抖动这些颜色,还是只是用其最近的邻居替换它们。参数以包含要使用的所有颜色的图像形式给出。如果您想将大量颜色图像简化为其颜色列表,您可以使用 "-unique-colors",在保存之前,用于 "-remap" 的后续使用。
请注意,虽然 "-remap" 运算符将接受任何图像以供使用,但不要为此图像使用 JPEG 图像,否则由于其“有损压缩”会生成额外的颜色,您将获得大量额外颜色。

另一方面,使用 JPEG 生成额外颜色可能有助于解决之前看到的“斑点”问题!
例如,这里我将 IM 徽标中使用的颜色限制为预定义的命名 X 窗口颜色映射。默认值为 "Riemersma" 抖动,但从 IM v6.4.4 开始,"-dither" 已扩展为允许选择其他抖动方法,例如 "FloydSteinberg"。您当然仍然可以使用 "+dither" 选项关闭抖动。

  magick logo.png  -dither None       -remap colortable.gif  remap_logo_no.gif
  magick logo.png  -dither Riemersma  -remap colortable.gif  remap_logo_rm.gif
  magick logo.png  -dither FloydSteinberg \
                                       -remap colortable.gif  remap_logo_fs.gif
[IM Output]  + [IM Output] [IM Output] [IM Output] [IM Output]
如您所见,IM 尝试使用给定的颜色合理地表示图像,尽管结果远不如允许 IM 选择要使用的颜色集时获得的图像。请注意,此 "colortable.gif" 图像从未设计用于抖动图像,而是一个用于为更原始的 X 窗口颜色显示器设计卡通风格的彩色图标的颜色集(有关详细信息,请参阅 Anthony 的 X 窗口图标库AIcons 颜色选择)。还要注意,最终图像没有使用此映射提供的全部 32 种颜色,尽管在启用某种抖动时,映射中的更多颜色将被使用( 分别),而不是在禁用它时 ()。最后一个示例表明选择一个好的颜色映射是多么重要。因此,我建议您让 IM 使用 "-colors" 运算符优化图像中使用的颜色选择,并根据您的需要进行修改,除非您有更迫切的理由不这样做。最后一点,虽然您可以在 "-colors" 将找到最佳颜色集的颜色空间中指定一个颜色空间,但您目前无法为颜色映射或抖动阶段定义一个颜色空间。我的所有实验似乎都表明,颜色集是基于 RGB 空间应用的(包括误差校正抖动和最近颜色替换)。"-quantize" 颜色空间设置仅用于选择颜色,而不是映射。那么,如果使用颜色映射是一个糟糕的主意,为什么您会想要使用它呢?通常有几个常见原因,通常是因为您需要更多地控制图像中使用的特定颜色调色板。另一位用户还将颜色映射分离出来,以便他可以在 Risograph(一种数字印刷系统)上使用它。如果您知道其他原因要使用 "-remap" 运算符,而我尚未在下面介绍 - 请给我发邮件。

常用或“最佳”颜色映射

在处理多个图像时,另一种技术是为所有涉及的图像生成一个公共颜色表。基本上,您将所有图像附加在一起形成一个大型图像,然后使用 "-colors" 运算符找出适合所有图像的良好颜色映射。获得该颜色映射图像后,您可以使用它重新对每个原始图像进行着色,使用此刚刚生成的 预定义颜色映射。或者,您可以使用特殊的 "+remap" 运算符,它对 255 色颜色映射执行相同操作。它计算颜色,执行颜色量化以形成良好的公共颜色映射,然后在需要时抖动图像以使用该映射。然而,"-remap" 和 "+remap" 两种形式都具有 GIF 动画的一个非常重要的功能。它将所有图像转换为 "-type" 为 "Palette" 的图像,所有图像都使用相同的颜色调色板。原因是,当写入 GIF 图像时,第一个图像的调色板将用于文件格式的“全局调色板”。然后,在写入每个图像时,它会注意到这些图像使用相同的颜色集,因此它不会创建“局部调色板”。这可以为最终 GIF 文件中的每个图像节省高达 256 × 3 或 768 字节的调色板空间。只有 "-remap" 运算符可以做到这一点。因此,在处理 GIF 时,特别是 GIF 动画时,这将是一个重要的要点。有关更多详细信息和示例,请参阅 Gif 动画,全局颜色表

网络安全着色

当 WWW 首次创建时,计算机显示器提供的颜色范围有限,而网络浏览器通常使用更简单的颜色集来显示图像。因此,通常会将图像重新着色为此颜色集,以使图像更小,并确保它们在用户的浏览器上看起来不错。有关更多详细信息,请参阅 网络样式指南,抖动。为了帮助解决此问题,IM 提供了一个内置颜色映射图像,它包含此特殊的 216 色表,称为 "netscape:"。因此,让我们看看我们的测试图像在使用这些颜色的旧 Web 浏览器显示器上的显示效果。

  magick logo.png         -remap netscape:  remap_netscape.gif
  magick logo.png +dither -remap netscape:  remap_netscape_nd.gif
[IM Output]  + [IM Output] [IM Output] [IM Output]
此颜色集是由显示器和计算机工程师而不是图形艺术家设计的数学确定的调色板,虽然它足够通用,可以合理地适用于真实图像(如照片),但它对于包含大面积平坦颜色区域的图像(如徽标、背景、计算机生成的图像(如图形)和卡通风格的图像)非常糟糕。基本上,它在颜色变化很大的区域工作,但在颜色恒定的较大平坦区域中,会应用三种颜色的抖动(通常),例如 IM 徽标测试图像(以上)的淡蓝色衬衫。换句话说,如果您要为 Web 设计图像或徽标,通常会尝试使用此调色板中的颜色用于大面积平坦区域,并且仅在颜色阴影变化的区域中使用抖动颜色。以上命令使您可以测试图像以查看它们在更原始的计算机显示器上的外观,以及编辑图像以使用这些颜色,以使它们能正常工作。这对于符号和导航图像尤其重要。当然,如今,由于游戏和 Web 用户的需求,您可以相当确定大多数用户拥有现代计算机显示器,这些显示器不再存在这些旧的颜色限制,但是仍然可以使用此“网络安全调色板”,因为它确实具有其他优势,例如图像压缩。有关在现代世界中使用网络安全颜色的讨论,请参阅 网络安全调色板的死亡?,可能从最初识别此颜色映射的图形设计师那里获得更重要的观点,Lynda Weinman

生成颜色映射

确定任何图像或特定图像集的良好颜色映射非常重要。当您处理将用于 GIF 动画的图像序列时,这一点尤其重要。基本上,您希望使其能够只使用一个颜色表,用于动画的所有帧,而不是为每个帧使用一个单独的颜色表。换句话说,您希望所有图像都使用一个单一颜色映射。在这种情况下,您实际上只有两种选择。您可以尝试创建一个适合任何图像的颜色映射,或者尝试针对您应用它的特定图像集优化颜色映射。

网络安全颜色映射

[IM 输出] 第一种方法通常是数学生成的色图,例如 IM 内置的 "netscape:" 色图。这提供了 216 种颜色,这些颜色可以很好地适应 GIF 格式的 256 种颜色限制,并且仍然有空间用于处理图像透明度,甚至为特殊目的添加一些额外颜色,如阴影或文本叠加。此颜色映射是通过为三个颜色通道中的每一个创建 6 个颜色级别生成的,产生 6×6×6 个颜色或 216 个颜色。野兽的数量。由于仅使用 219 种颜色,因此它仍然有空间(用于 GIF 图像)为特定目的向颜色映射中添加更多颜色。例如,透明颜色以及更多灰度色调。Macintosh 的旧版网络安全地图实际上就是这样做的,以试图改善其整体结果,但它仅在 Macintosh 网络客户端上使用。这可能是最常见的“统一” (或数学推导)颜色映射,得益于其简单性和在万维网上的一般使用。

统一 332 色图

另一个常用的统一颜色映射是“332 RGB 颜色映射”。该数字指的是在 8 位颜色索引中表示每种颜色所使用的位数。也就是说,3 位(或 8 个级别)红色,3 位绿色,以及 2 位(或 4 个颜色级别)蓝色,因为我们的眼睛对蓝色没有很好的反应。这提供了 3+3+2 位或 8 位颜色索引,或 256 种颜色。非常适合有限的 GIF 颜色表。但是,它不会为 GIF 透明颜色或其他特殊用途颜色留出任何空间。以下是一种让 IM 生成此颜色映射的方法…

  magick -size 16x16 xc: -channel R -fx '(i%8)/7' \
                          -channel G -fx '(j%8)/7' \
                          -channel B -fx '((i>>3&1)|(j>>2&2))/3' \
          -scale 600% colormap_332.png
[IM Output]
在 IM 6.2.9-2 版本之前,"-fx" 运算符中缺少位移运算符 ">>" 和 "<<"。
完成相同操作的更简单方法是使用 使用统一颜色级别的有序抖动,使用操作 "-ordered-dither threshold,8,8,4" (请参阅该示例区域)。与以上 DIY FX 方法 相比,这是一种更轻松、更快的技术,甚至允许您使用其他内置抖动映射来更好地处理渐变。此映射的唯一缺点是它实际上根本没有提供任何“灰色”颜色。但是,此缺点在使用抖动时可能成为优势,因为轻微的颜色差异会减少灰度渐变中颜色边界变化的影响,使其看起来更加平滑。

真彩色 16 位颜色映射

X 窗口在很少使用的 16 位视觉类别中使用的“332 色图”类似的统一色图。在这种情况下,16 位用于颜色索引,该颜色索引被划分为 5 位红色,5 位绿色,以及 6 位蓝色。换句话说,此颜色映射更像是“556 颜色映射”,最好使用 使用统一颜色级别的有序抖动 使用“阈值”抖动映射来实现。具体来说是操作 "-ordered-dither threshold,32,32,64"。但是,16 位颜色映射很少见,因为使用颜色映射的图像通常需要 8 位颜色表。因此,我不会进一步提及它。

伽马校正统一颜色映射

目前 IM 无法直接处理伽马校正颜色映射。相反,您应该使用 magick 对您的图像进行处理(假设您拥有 Q16 或更高版本的 IM 编译时 质量 版本),从 sRGB 或图像具有的任何伽马级别转换为线性 RGB 模型,然后再进行抖动。 这也适用于许多其他图像处理操作,例如调整大小、模糊等。有关示例,请参见 使用伽马校正调整大小

海报化,使用统一颜色映射重新着色

该操作符的最初用途(使用 '2' 的参数)是使用仅 8 种基本颜色重新着色图像,就好像该图像使用简单的廉价海报打印方法使用仅基本颜色生成一样。 因此该操作符得名。 实际上,“-posterize” 操作符是一种特殊的颜色缩减操作符,它根据给定的每个颜色通道的“级别”数量生成颜色映射,并使用误差校正抖动对图像进行抖动。

  magick netscape: -scale 50%  +dither  -posterize 2   posterize_2_ns.gif
  magick netscape: -scale 50%  +dither  -posterize 3   posterize_3_ns.gif
  magick netscape: -scale 50%  +dither  -posterize 6   posterize_6_ns.gif
[IM Output] [IM Output] [IM Output] [IM Output]
正如您所见,“-posterize” 参数 '2' 意味着每个颜色通道仅提供 2 种颜色,为 3 通道 RGB 图像生成仅 8 种颜色的映射,例如上面所示。 它基本上会使用 8 种颜色的阈值集对图像进行重新着色。 参数 '3' 将根据 27 种颜色的颜色映射映射图像颜色,包括中间色调颜色。 而参数 '4' 将生成 64 色颜色表,而 '5' 将生成 125 色颜色映射。 当然,如上所述,参数 '6' 将复制与内置“netscape:” 图像中提供的相同 216 色集。 请注意,“-posterize” 参数 '0' 或 '1' 没有意义,并且在最新的 IM 版本中只是将图像转换为纯黑色(虽然合乎逻辑,但非常无用)。 结果是图像已使用数学推导的或“统一”的颜色映射重新着色。 您可以在渐变图像上更清楚地看到这一点,它会生成均匀分布的海报化灰度级。

  #magick -size 20x640  gradient: -rotate 90  gradient.png
  magick gradient.png  +dither  -posterize 5   posterize_gradient.gif
[IM Output]
例如,让我们以不同的级别海报化 IM 徽标图像…

  magick logo.png  +dither -posterize 2  posterize_logo.gif
  magick logo.png          -posterize 2  posterize_logo_dither.gif
  magick logo.png          -posterize 6  posterize_6_logo.gif
[IM Output] ==> [IM Output] [IM Output] [IM Output]
作为更好的测试,让我们对阴影的“色轮”图像进行海报化。

  magick colorwheel.png  +dither  -posterize 2   posterize_2_cw.gif
  magick colorwheel.png  +dither  -posterize 3   posterize_3_cw.gif
  magick colorwheel.png  +dither  -posterize 6   posterize_6_cw.gif
[IM Output] [IM Output] [IM Output] [IM Output]
这里是在启用抖动的情况下做同样的事情…
[IM Output] [IM Output] [IM Output] [IM Output]
当然,我们在下一节中查看的许多位图抖动也可以使用各种抖动样式生成 2 级有序抖动。 但是很少有可以使用更多灰度级。 自 IM v6.2.9 以来,有序抖动 也是一种海报化方法,因为它目前仅限于使用统一颜色映射进行抖动。 但是抖动模式更加均匀,并且具有比“-posterize” 生成的伪随机抖动更多的样式选择。 将这些与上面显示的抖动“-posterize” 版本进行比较。

  magick colorwheel.png  -ordered-dither o8x8,2   posterize_2_od.gif
  magick colorwheel.png  -ordered-dither o8x8,3   posterize_3_od.gif
  magick colorwheel.png  -ordered-dither o8x8,6   posterize_6_od.gif
[IM Output] [IM Output] [IM Output] [IM Output]
threshold” 抖动映射(而不是上面使用的 'o8x8')有效地将“-ordered-dither” 转换为未抖动的海报化方法。 最后,有序抖动 允许您为每个单独的颜色通道指定不同的颜色级别数量。 “-posterize” 操作符目前尚不支持此功能。

阈值抖动方法

阈值图像

将图像转换为黑白位图(彩色)图像的最简单方法是使用“-threshold”。 这实际上是一个简单的数学运算符,它只提供一个截止值。 等于或低于该值的任何值将变为黑色,而大于该值的任何值将变为白色。

  magick logo.png     -threshold   -1   threshold_0.gif
  magick logo.png     -threshold  25%   threshold_25.gif
  magick logo.png     -threshold  50%   threshold_50.gif
  magick logo.png     -threshold  75%   threshold_75.gif
  magick logo.png     -threshold 100%   threshold_100.gif
[IM Output] [IM Output] [IM Output] [IM Output] [IM Output]
正如您所见,'-1' 的值会将所有颜色 magick 为白色,而 '100%' 会将所有颜色转换为黑色。 '50%' 当然是使用最常见的 value。
'0' 的值是一种特殊情况,它会将所有非纯黑色变为白色。 当然,如果图像中没有纯黑色,那么您只会得到一个纯白色图像!

  magick logo.png  -threshold   0    threshold_black.gif
[IM Output]
如果您实际上要将所有非纯白色 magick 为黑色,那么我建议您对 反转 的图像进行阈值处理,而不是尝试计算出要使用的正确阈值(比 IM 当前的“MaxRGB”小一),该值取决于您特定 IM 的 质量或“Q” 设置的编译时。

  magick logo.png  -negate -threshold 0 -negate threshold_white.gif
[IM Output]
-threshold” 操作符可以归类为终极“对比度”操作符,通过阈值级别最大限度地提高颜色差异。 但是它是一个灰度操作符,这意味着“-channel” 设置可以用于调整将应用操作符的颜色通道。 例如,您可以对图像的每个单独通道进行阈值处理,以产生与未抖动的 2 级“-posterize” 操作相同的效果。

  magick logo.png  -channel R -threshold 50% \
                     -channel G -threshold 50% \
                     -channel B -threshold 50%   threshold_posterize.gif
[IM Output]
请注意,“-threshold” 将图像中的任何透明度视为蒙版通道,而不是 alpha 通道(就像它在 IM 中的内部存储一样)。 因此,如果您计划将此操作符应用于 alpha 通道,则需要谨慎。 有关更多详细信息,请参见 蒙版通道
对于更自动的阈值处理技术,您可以使用之前展示过的 双色量化 技术。
例如,这将根据图像中找到的最佳两种颜色对图像进行阈值处理。 这些颜色不一定必须是灰度色或甚至是相反色,只是最能代表整个图像的两种颜色。 然后使用“-normalize” 将这两种颜色映射到纯黑色和白色。

  magick logo.png  +dither  -colors 2  -colorspace gray -normalize \
             threshold_two_color.gif
[IM Output]

随机抖动和阈值

-random-threshold” 操作符是一种特殊的位图图像转换形式。 在这种情况下,它使用非常简单的“随机抖动”来确定特定像素是否变为白色像素或黑色像素。 与“-threshold” 或“-monochrome” 操作符,甚至上一节中的变体不同,“-random-threshold” 在每个选定通道上完全独立地工作,彼此之间没有任何关系。
当然,直接使用该操作符将导致使用随机抖动对图像进行 2 级海报化。

  magick logo.png  -random-threshold  0x100%  random_posterize.gif
[IM Output]
转换为灰度级将使图像中的所有通道均衡化,然后进行抖动。 但是由于每个通道都是独立地且随机地进行抖动的,因此结果不是预期的位图图像。 相反,您将获得彩色像素的飞溅,特别是对于中间色调颜色。

  magick logo.png  -colorspace Gray -random-threshold  0x100% \
                                                   random_greyscale.gif
[IM Output]
以下是生成正确的随机抖动位图图像的正确方法。

  magick logo.png  -colorspace Gray -channel B \
          -random-threshold 0x100%    -separate   random_monochome.gif
[IM Output]
它基本上做的是只对灰度图像的一个通道进行抖动,然后使用“-separate” 通道操作符将该通道提取为最终的位图图像。 技巧但有效。
作为该操作符的特殊功能,IM 将确保在“-channels” 选项为 'All' 时生成位图图像。

  magick logo.png  -channel All -random-threshold 0x100% random_all.gif
[IM Output]
但是请注意,任何 alpha 通道都将被忽略并使用此方法丢失,因此通常不建议这样做。 我自己只是从源代码中意外发现了这个古老的功能。 现在您已经知道如何使用该操作符从彩色图像正确生成位图,让我们看看该参数如何影响抖动的范围。 这也清楚地显示了这种抖动产生的像素“聚集成块”。

  #magick -size 20x640  gradient: -rotate 90  gradient.png
  magick gradient.png  -channel All \
                        -random-threshold 0x100%  random_grad_0x100.gif
  magick gradient.png  -channel All \
                        -random-threshold 10x90%  random_grad_10x90.gif
  magick gradient.png  -channel All \
                        -random-threshold 25x75%  random_grad_25x75.gif
  magick gradient.png  -channel All \
                        -random-threshold 50x50%  random_grad_50x50.gif
[IM Output] [IM Output] [IM Output] [IM Output]
-random-threshold” 设置为 '0x100%' 将产生图像的纯粹“随机抖动”。 如果两个边界设置为相同的值(甚至超过彼此),它只会产生纯“-threshold” 图像。 使用任何其他边界集(通常使用百分比指定)将在给定范围内对位图进行阈值处理,同时为给定范围内的值生成随机抖动模式。
最佳结果可以通过使用略小的范围来获得,就像使用“-monochrome” 操作符一样。 大约 '30x80%' 的值可能是大多数情况下的最佳结果。

  magick logo.png  -channel All -random-threshold 30x80%  random_30x80.gif
[IM Output]
当然,结果仍然不是很好。 但这确实是您可以获得的最简单、最差的抖动形式。 实际发生的情况是,随机抖动模式往往会产生像素的“聚集成块”,而不是平滑的抖动模式。 这是由于随机数生成器中的高频“噪声”。 但是,在非常高的分辨率下,随机抖动已被证明可以产生非常好的结果,如果足够随机的话。 IM 使用加密级的随机性,因此它很可能非常随机,尽管图像很少以足够高的分辨率使用,以至于无法以这种方式发挥作用。 针对这种抖动的一种“修复”方法,有人建议使用随机“蓝色噪声”生成器(高频滤波器,与声音制作中使用的低频“粉红噪声”滤波器相反)。 这应该消除像素的聚集成块,但数字实现起来非常困难。 尚未发现已知的“蓝色噪声随机抖动”实现,并且不太可能创建。

有序抖动

虽然随机抖动会产生随机的像素块,而各种错误修正抖动会产生基本上随机的点状图案,但有序抖动基本上是相反的。它被设计成尽可能地数学确定。确定性如此之高,以至于您实际上需要指定要在抖动图像时使用的图案。“-ordered-dither” 操作符将对图像中每个选定的“-channels” 进行给定预定义图案的抖动。参数定义要使用的图案(称为阈值图)。这些阈值图分为三种基本样式。分散像素抖动,其中像素尽可能地彼此远离放置,以避免“聚集”和平铺伪像。或者将它们聚集在一起形成紧密的点,这使得它们更容易在称为数字半色调的技术中进行机械打印。还有一些我们将要介绍的专门的艺术阈值图,甚至可以设计我们自己的抖动图案或阈值图。在每种情况下,阈值图中开启或关闭的像素数量取决于被抖动到位图中的图像(或单个颜色通道)的灰度强度。该图以一致的方式添加像素阈值级别,因此一旦像素在特定“阈值”处打开,它将保持打开状态以供任何更浅的灰色。这种一致性非常重要,否则会在抖动图案变化边界处产生伪像。关于这一点的重要一点是,图像中每个像素的结果纯粹由数学确定,独立于图像中任何其他像素。因此,对原始图像的任何微小更改都不会对图像的任何其他区域产生影响,这是错误校正抖动所面临的问题,如上所述。这一点对于视频图像的一致抖动和优化动画至关重要。

扩散像素抖动

有序抖动的最初目的,以及大多数图形程序员在使用有序抖动时所期望获得的结果,有时更准确地称为“分散像素有序抖动”。这意味着当阈值强度增加时,像素被添加到平铺图中,因此它们尽可能地彼此远离并均匀分布。这会产生一个高度一致的图案,在大多数现代显示器上看起来非常平滑且几乎不可见。此类图案已针对 2 的幂的平铺大小而计算得出,即 2、4 和 8 的平铺大小。虽然 IM 也为 3x3 阈值图平铺提供了一个合理的阈值图案。以下是 IM 目前提供的内置有序抖动的当前集合。请记住,参数反映有序抖动的平铺大小。

  magick logo.png    -ordered-dither o2x2    logo_o2x2.gif
  magick logo.png    -ordered-dither o3x3    logo_o3x3.gif
  magick logo.png    -ordered-dither o4x4    logo_o4x4.gif
  magick logo.png    -ordered-dither o8x8    logo_o8x8.gif
[IM Output] [IM Output] [IM Output] [IM Output]
注意,较大的平铺大小允许您模拟更多的“颜色级别”,但也会在某些级别生成更明显的缺陷或矩形像素阵列。
o8x8” 有序抖动是 IM 核心代码的一部分,但一直没有使用。它只是作为 IM v6.2.9 中“-ordered-dither” 操作符的选项添加的,当时 IM 示例开始详细介绍此操作符的使用。

此时,这些地图被赋予了更明确的名称,以便进一步扩展“-ordered-dither” 操作符,尽管保留了较旧的向后兼容的“平铺大小”名称作为新名称的别名。

此外,生成“o3x3” 和“o4x4” 的“地图”已被彻底修改,以产生更好的“分散像素”抖动图案。在此之前,地图会生成不同的像素“块”。

请参阅有序抖动升级说明页面,了解旧图案在修复之前的示例,以及在 IM v6.3.0 中升级的官方发布开发过程中进行的其他更改。
当然,您需要首先将图像变为灰度,以生成图像中所有通道的正确位图,但是由于该过程不是随机的,因此您不需要像对-random-threshold” 操作符那样对图像进行后处理,从而极大地简化了操作。

  magick logo.png -colorspace Gray  -ordered-dither o2x2  logo_bw_o2x2.gif
  magick logo.png -colorspace Gray  -ordered-dither o3x3  logo_bw_o3x3.gif
  magick logo.png -colorspace Gray  -ordered-dither o4x4  logo_bw_o4x4.gif
  magick logo.png -colorspace Gray  -ordered-dither o8x8  logo_bw_o8x8.gif
[IM Output] [IM Output] [IM Output] [IM Output]
作为参考,以下是每个“-ordered-dither” “分散像素”图案应用于灰度渐变的效果,因此您可以清楚地看到它们的样子。

  # Threshold Non-Dither / Minimal Checkerboard Dither
  magick gradient.png   -ordered-dither threshold  od_threshold.gif
  magick gradient.png   -ordered-dither checks     od_checks.gif
  # Diffused Pixel Dither
  magick gradient.png   -ordered-dither o2x2       od_o2x2.gif
  magick gradient.png   -ordered-dither o3x3       od_o3x3.gif
  magick gradient.png   -ordered-dither o4x4       od_o4x4.gif
  magick gradient.png   -ordered-dither o8x8       od_o8x8.gif
[IM Output] [IM Output]
[IM Output] [IM Output] [IM Output] [IM Output]
特定有序抖动产生的有效或伪级别图案的数量通常(但不总是)等于图案中的像素数量加 1。因此,“o3x3” 有序抖动将在结果图像中产生 3x3+1 或 10 个有效灰度级别(黑色、白色和 8 种人造灰色图案)。上面还显示了两个特殊的最小抖动阈值图
  1. 一个直的“50% 阈值”非抖动,它不会产生任何额外的灰度级别,以及
  2. 一个“棋盘格”或棋盘格抖动图案,它只插入一个图案以在结果渐变中添加一个额外的“伪级别”。

数字半色调抖动

-ordered-dither” 在 IM v6.2.8-6 中进行了扩展,包含一组数字半色调抖动图案(感谢 Glenn Randers-Pehrson)。所有这些都被设置为产生一个简单的 45 度点图案。在 IM v6.3.0 中,它被进一步扩展了,包含一组类似的较大无角度半色调。
在 IM v6.3.0 发布之前,半色调屏幕是通过使用形式为“{number}x1” 的参数来选择的。通过有序抖动的重新开发,此限制被解除,选择了更好的命名,并添加了额外的半色调屏幕(正交形式)(请参阅下面的示例参数)。
请注意,数字半色调严格来说不是真正的半色调屏幕,半色调屏幕旨在处理圆形墨点,这些墨点被机械地沉积到纸张、纸板甚至金属等介质上。这些墨点可能会重叠并在印刷过程中涂抹,因此需要进行一些非线性级别调整。这对于产生纯粹的数字半色调效果来说是不必要的。有关此过程的更多详细信息,请参阅文档抖动和半色调(PDF)。也就是说,有序抖动 数字半色调图案确实提供了与报纸和廉价印刷杂志中看到的效果相同的基本效果。

  # Halftone Screen (45 degree angle)
  magick logo.png   -ordered-dither h4x4a    logo_h4x4a.gif
  magick logo.png   -ordered-dither h6x6a    logo_h6x6a.gif
  magick logo.png   -ordered-dither h8x8a    logo_h8x8a.gif
  # Halftone Screen (orthogonal)
  magick logo.png   -ordered-dither h4x4o    logo_h4x4o.gif
  magick logo.png   -ordered-dither h6x6o    logo_h6x6o.gif
  magick logo.png   -ordered-dither h8x8o    logo_h8x8o.gif
[IM 输出] [IM 输出] [IM 输出]       [IM 输出] [IM 输出] [IM 输出]
再次使用“-colorspace” 操作符来生成图像的真实位图抖动。

  # Halftone Screen (45 degree angle)
  magick logo.png -colorspace Gray  -ordered-dither h4x4a logo_bw_h4x4a.gif
  magick logo.png -colorspace Gray  -ordered-dither h6x6a logo_bw_h6x6a.gif
  magick logo.png -colorspace Gray  -ordered-dither h8x8a logo_bw_h8x8a.gif
  # Halftone Screen (orthogonal)
  magick logo.png -colorspace Gray  -ordered-dither h4x4o logo_bw_h4x4o.gif
  magick logo.png -colorspace Gray  -ordered-dither h6x6o logo_bw_h6x6o.gif
  magick logo.png -colorspace Gray  -ordered-dither h8x8o logo_bw_h8x8o.gif
[IM 输出] [IM 输出] [IM 输出]       [IM 输出] [IM 输出] [IM 输出]
最后,另一个渐变参考图像,清楚地显示半色调抖动图案,以及随着灰度级别的变化,抖动图案内的像素块如何相互增长。

  # Halftone Screen (45 degree angle)
  magick gradient.png   -ordered-dither h4x4a      od_h4x4a.gif
  magick gradient.png   -ordered-dither h6x6a      od_h6x6a.gif
  magick gradient.png   -ordered-dither h8x8a      od_h8x8a.gif
  # Halftone Screen (orthogonal)
  magick gradient.png   -ordered-dither h4x4o      od_h4x4o.gif
  magick gradient.png   -ordered-dither h6x6o      od_h6x6o.gif
  magick gradient.png   -ordered-dither h8x8o      od_h8x8o.gif
  magick gradient.png   -ordered-dither h16x16o    od_h16x16o.gif
  # Circle Halftones (black and white)
  magick gradient.png   -ordered-dither c7x7b      od_c7x7b.gif
  magick gradient.png   -ordered-dither c7x7w      od_c7x7w.gif
[IM Output] [IM Output] [IM Output]
[IM Output] [IM Output] [IM Output] [IM Output]
[IM Output] [IM Output]
直到 ImageMagick 版本 6.2.9,以上所有阈值有序抖动地图都是 IM 可能实现的。现在这种情况发生了变化,允许用户添加自己的图案,甚至可以将它们贡献给 IM 社区。IM v6.6.5-6 由 Glenn Randers-Pehrson 添加了“圆形”半色调阈值。

偏移半色调抖动

上述半色调抖动的唯一问题是,完全相同的阈值图(平铺)以相同的方式应用于所有颜色通道。这意味着相同的颜色集以具有相同“中心”的点排列。为了获得所谓的“偏移印刷”,阈值图案以特定模式旋转,使得颜色形成小型的“玫瑰花图案”,从而破坏了您可能出现的更可怕的干扰(摩尔)图案。该图基本上解释了该过程,并在维基百科页面半色调上进行了详细解释。
[IM Output]
但是请注意,旋转的屏幕无法很好地平铺,因此最好的方法是直接生成旋转的图案,而不是使用平铺的阈值图案。以下是一种使用小的旋转 2x2 像素棋盘格图案,让图像呈现偏移半色调印刷效果的方法,这大约是最小的可使用的“屏幕”。

  magick colorwheel.png  -set option:distort:viewport '%wx%h+0+0' \
          -colorspace CMYK -separate null: \
          \( -size 2x2 xc: \( +clone -negate \) \
                +append \( +clone -negate \) -append \) \
          -virtual-pixel tile -filter gaussian \
          \( +clone -distort SRT 60 \) +swap \
          \( +clone -distort SRT 30 \) +swap \
          \( +clone -distort SRT 45 \) +swap \
          \( +clone -distort SRT 0 \)  +swap +delete \
          -compose Overlay -layers composite \
          -set colorspace CMYK -combine -colorspace RGB \
          offset_colorwheel.png
[IM Output]
请注意,四个旋转的“屏幕”被应用于整个图像,只有 CMYK 色彩空间中的“-combine”步骤实际上从屏幕图像中提取了 4 种不同的颜色通道。此外,最后一个“黑色”通道的“无操作”扭曲很重要,因为它会根据在其他通道旋转期间使用的高斯滤波器对输入棋盘格图案进行模糊,即使该屏幕本身没有被旋转。这里我使用SRT 扭曲 的缩放功能来生成旋转的平铺,以创建稍微更大且更模糊的“屏幕图案”。

  magick parrots_med.png  -set option:distort:viewport '%wx%h+0+0' \
          -colorspace CMYK -separate null: \
          \( -size 2x2 xc: \( +clone -negate \) \
                +append \( +clone -negate \) -append \) \
          -virtual-pixel tile -filter gaussian \
          \( +clone -distort SRT 2,60 \) +swap \
          \( +clone -distort SRT 2,30 \) +swap \
          \( +clone -distort SRT 2,45 \) +swap \
          \( +clone -distort SRT 2,0  -blur 0x0.7 \) +swap +delete \
          -compose Overlay -layers composite \
          -set colorspace CMYK -combine -colorspace RGB \
          offset_parrots.png
[IM Output]
请注意,该图案仍然非常“方形”,特别是从所有其他图案派生的黑色图案。未来可能性:将上面使用的 2 像素棋盘格替换为“pattern:gray50” 像素级棋盘格图案。高斯滤波器选项可用于调整缩放图案的模糊程度。或者,您可以模糊缩放图案并对其进行阈值处理,以使点更圆。然后可以像以前一样对其进行旋转,以创建 4 色屏幕。如果使用更大的屏幕,使用六边形点的图案,而不是我上面使用的棋盘格图案,那就更好了。
需要注意的是,这实际上并不是像真正的胶印那样生成彩色点,而是通过简单地将彩色网点与原始图像相乘来模拟。你可以看到,红色鹦鹉与绿色背景交界处的颜色急剧变化,就是这种情况。使用纯色点进行的真正胶印不会出现点中间的颜色变化。只有纯色点的尺寸会根据源图像中该区域的平均颜色而改变。要真正生成一个只包含每个颜色通道中适当大小的圆点的胶印图像,还需要做更多工作。需要确定每个颜色通道中每个点的平均颜色,并由此生成适当大小的彩色点(抗锯齿圆圈)。有人愿意尝试一下吗? 以上内容来自 IM 论坛讨论 CMYK 半色调效果 中的讨论,该讨论着眼于 Photoshop 如何“模拟”以及 ImageMagick 如何实现相同的效果。该讨论也与 黑白半色调抖动 相关,后者更详细地介绍了如何使用适当大小的实际点生成真正的半色调网点。然而,该讨论并没有将它扩展到使用偏移(旋转)网点的下一步。此类网点可能需要旋转图像以生成点,然后为该特定颜色通道再次旋转点模式。

XML 阈值映射

从 IM 6.3.0 版本开始,不再使用内置于 IM 源代码中的固定映射集(如前所示),而是从程序本身外部的一组 XML 数据文件中读取映射。作为此更改的一部分,你现在可以列出 "-ordered-dither" 运算符可使用的可用“阈值映射”。

  magick identify -list threshold
[IM Output]
上面的列表不仅显示了可用的阈值映射,还显示了为向后兼容性或备用命名提供的别名,以及在我自己的个人 "thresholds.xml" XML 数据文件(保存到我的主目录的 ".magick" 子目录中)中定义的别名。当 "-ordered-dither" 查找映射时,将使用在上面列表中找到的第一个映射。因此,你不能覆盖系统定义的阈值模式。系统文件 "thresholds.xml"(其路径由上面的 "-list" 选项给出)包含 XML 文件格式的完整摘要。该格式足够简单(IM 进行错误检查),允许用户定义和创建自己的有序抖动阈值映射。例如,这是我在个人 "threshold.xml" 文件中定义的“diag5x5”阈值映射的副本。
[IM Output]
如果你仔细观察,它会创建一个简单的 5x5 映射,其中包含一条单一的对角线,随着阈值水平的升高,对角线会变粗。映射中的级别编号从 0 到 5,比除数少 1,除数声明它需要将颜色渐变划分为多少个“灰色”。以下是使用此个人阈值映射抖动的渐变。

  magick gradient.png   -ordered-dither diag      od_diag.gif
[IM Output]
以下是一个使用该阈值对简单阴影图像的 alpha 通道进行抖动的示例,这是我设计它的目的。

  magick -size 70x60 xc:none -font Candice -pointsize 50 \
          -fill black -annotate +10+45 'A' -channel RGBA  -blur 0x5 \
          -fill white -stroke black -draw "text 5,40 'A'"   shadow.png

  magick shadow.png  -channel A  -ordered-dither diag   shadow_diag.gif
[IM Output] ==> [IM Output]
很酷吧!稍后会详细介绍抖动 alpha 通道。首先,我需要展示如何使用扩展的 "-ordered-dither" 运算符的颜色功能。

使用均匀颜色级别的有序抖动

随着 IM v6.3.0 的发布,不仅 "-ordered-dither" 使用的阈值映射已更改为从外部文件读取,而且内部操作也得到了增强,使其能够使用数学定义的“分层”颜色映射。这意味着你可以生成比使用“误差校正抖动”所能实现的更确定的图像抖动。这对于涉及动画的颜色减少尤其重要,因为你不会遇到帧之间颜色差异的问题。分层级别通过使用附加到要使用的阈值映射名称的额外逗号分隔的数字列表传递给 "-ordered-dither" 参数。如果未提供数字,则运算符将回退到正常的 2 色(或分层级别 1)颜色映射。例如,参数“checks,6”将使用经典的 网络安全颜色映射(分层级别 6)颜色映射(也由 "netscape:" 内置颜色映射图像定义)。但是,由于使用了“checks”的最小抖动映射,因此在 6 个颜色级别之间添加了一个额外的抖动级别,在图像的每个通道中创建了 11 个伪级别颜色。换句话说,即使每个通道只使用了 6 个颜色级别(生成 6^3 或 216 种颜色),但级别之间的单个抖动模式也会将抖动增加到有效的 11 个级别(生成有效的 11^3 或 1331 种颜色)。例如,以下是使用 6 个灰色级别和各种阈值映射抖动的灰度渐变。第一个映射“threshold”是一个特殊的非抖动有序抖动阈值映射,只显示了所使用的颜色。

  magick gradient.png   -ordered-dither threshold,6  od_threshold_6.gif
  magick gradient.png   -ordered-dither checks,6     od_checks_6.gif
  magick gradient.png   -ordered-dither o2x2,6       od_o2x2_6.gif
  magick gradient.png   -ordered-dither o4x4,6       od_o4x4_6.gif
  magick gradient.png   -ordered-dither o8x8,6       od_o8x8_6.gif
[IM Output] [IM Output] [IM Output] [IM Output] [IM Output]
如你所见,即使只使用了 6 种颜色,通过有序抖动,你也会增加用于定义渐变的有效颜色数量,以至于你很难注意到实际上只使用了多少种颜色!你不仅可以定义所有通道的分层级别数,而且与 "-posterize" 误差校正抖动选项不同,你可以为每个通道指定级别。根据 "-channels" 设置,将数字分配给通道。例如,这里我们使用一个特殊的 332 色映射(8 个红色和绿色级别,4 个蓝色级别)对渐变进行抖动,该映射定义了总共 256 种颜色。

  magick gradient.png   -ordered-dither o8x8,8,8,4   od_o8x8_884.gif
[IM Output]
由于每个通道的颜色级别数不同,因此上面的图像不只包含纯灰色,还包含一些蓝色和黄色的像素,它们相互抵消,从而产生额外的灰色级别。现在将 O 抖动 版本与使用 2 和 6 的分层级别以及“332 色映射”(8 个红色和绿色级别,4 个蓝色级别)的误差校正抖动版本进行比较。

  magick logo.png  -ordered-dither o8x8        logo_o8x8_2.gif
  magick logo.png  -posterize 2                logo_posterize_2.gif
  magick logo.png  -ordered-dither o8x8,6      logo_o8x8_6.gif
  magick logo.png  -posterize 6                logo_posterize_6.gif
  magick logo.png  -ordered-dither o8x8,8,8,4  logo_o8x8_332.gif
  magick logo.png  -remap colormap_332.png     logo_remap_332.gif
[IM 输出] [IM 输出]     [IM 输出] [IM 输出]     [IM 输出] [IM 输出]
上面每一对中的第一张图像都是数学有序抖动,而第二张图像则是伪随机“误差校正”抖动。最后一对使用了特殊的“332 色映射”(参见 生成颜色映射),这被认为是具有 256 色限制的通用图像的最佳分层颜色映射。通道级别的奇特差异为这种类似卡通的图像产生了略微更好的颜色阴影。正是为了能够生成“332 色映射”,"-ordered-dither" 运算符才包含了为每个颜色通道指定单独级别的能力。

更好的有序抖动结果

让我们仔细看看我们刚刚生成的级别 6 O 抖动

  magick logo.png -ordered-dither o8x8,6 -format %k info:
[IM Text]
如你所见,对于此图像,我们甚至没有接近填充 GIF 颜色表(256 个限制)。基本上,由于图像通常主要由蓝色组成,因此甚至没有使用来自级别 6 均匀颜色映射的红色或绿色的阴影。但是,通过增加分层级别数,我们可以更好地填充 GIF 颜色表,从而生成更好的 O 抖动 图像。

  magick logo.png -ordered-dither o8x8,13 -format %k info:
[IM Text]
这会生成足够多的颜色,使其略小于 GIF 颜色表限制。随着颜色数量的增加,结果看起来比简单标准均匀颜色映射的结果好得多。

  magick logo.png -ordered-dither o8x8,13    logo_o8x8_13.gif
[IM Output]
如你所见,使用较高的“级别”值,"-ordered-dither" 可以生成与颜色量化的图像相当的图像,与颜色量化和误差校正抖动生成的特定颜色选择相当。这些图像的主要要点不在于它们的质量很高。毕竟,完整的 颜色量化 可以更轻松地为图像生成更好的颜色映射。但是,图像中的低级别抖动模式是固定的,无论发生任何细微变化都无关紧要。只有区域变化会在有序抖动图像中发生改变。也就是说,它们不会对 帧优化 中的 GIF 动画造成问题的 E 抖动敏感性。(参见 优化问题)当然,对于动画,你需要使用 "-append" 将所有图像连接在一起,然后再检查实际上使用了多少种颜色。你需要使用特殊的 "+remap" 选项,在使用 "-ordered-dither" 后,强制 IM 为所有图像生成“通用全局颜色映射”,即使你已经执行了颜色减少和抖动。这种确定颜色级别数的方法并不简单,但它确实有效。我希望找到一种方法让 IM 自动确定最佳级别,尤其是对于 GIF 动画。

DIY 抖动模式和阈值映射

之前,我向你展示了新的 "-ordered-dither" 运算符可以接受用户定义的抖动模式。这里,我将向你展示如何创建自己的抖动模式。具体来说,我发现的一种对于生成由水平线组成的阴影非常有用的特殊模式。

多图像抖动模式

首先,你需要创建一组图像来定义你想要创建的模式。模式应该以一个大小正确的纯黑色图像(所有像素都关闭)作为第一张图像,并在另一端以一个纯白色图像(所有像素都打开)作为结尾。下一张图像应该是中间 50% 灰色模式,定义了你试图实现的抖动基本样式。例如,这是我最初的 DIY 抖动模式。我将其保存到一个多图像 GIF 文件(不是 GIF 动画)中……

  magick -size 2x2 xc:black \
          \( +clone -draw 'fill white line 0,0 1,0' \) \
          xc:white     dpat_hlines2x2.gif
  montage dpat_hlines2x2.gif    -tile x1 -background none -frame 2 \
          -filter box  -geometry 32x32+5+0    dpat_hlines2x2_imgs.gif
[IM Output]
这是你能得到的关于抖动模式图像的最简单的一组,它与“checks”或“棋盘抖动”非常相似,但使用的是水平线,而不是棋盘图案。因此,你可以看到这种抖动模式到底是什么样子,以下是一个相当简单的 DIY 有序抖动,它直接使用阈值抖动图像集。

  magick gradient.png   dpat_hlines2x2.gif \
          -virtual-pixel tile  -fx 'u[(floor((n-1)*u)+1) % n]' dgrad_hlines2x2.gif
[IM Output]
如您所见,抖动模式没什么特别的。 “-fx” 函数是 颜色查找表 函数的变体,即 IM Dither Lookup Patterns 类型的函数。 并使用 "-virtual-pixel" 设置为 'tile',该函数甚至不需要知道您使用的抖动模式图像的大小。
在 IM 版本 6.2.9-2 之前,"-fx" 操作符使用像这样计算的索引使用 "-virtual-pixel" 是有问题的。
让我们再次尝试这个抖动模式集,但使用简单的阴影图像...

  magick shadow.png dpat_hlines2x2.gif  -channel A \
          -virtual-pixel tile  -fx 'u[floor((n-1)*u)+1].g' \
          shadow_dpat_hlines2x2.gif
[IM Output] ==> [IM Output]

DIY 有序抖动阈值映射

上面的 DIY 抖动模式尽可能简单,因此我们可以直接将其转换为 XML 阈值映射,以便快速内置的 "-ordered-dither" 操作符可以使用它。 这是最终的 XML 定义,我将其保存在我个人阈值映射文件 "~/.magick/thresholds.xml" 中,该文件位于我的 "$HOME" 目录中。
[IM Output]
XML 格式非常简单,定义了一个 2x2 像素映射。 第一张黑色图像的值为零,没有像素,因此没有零值存在。 中间图像中开启的像素(变为白色)设置为 '1',其余或第二张图像像素的值为 '2'。 'divisor=' 定义了图像的数量或伪彩色级别(假彩色级别),该抖动模式表示,因此它的值为 '3'。 它将像素值除以定义像素开启的颜色级别。 因此,对于大于 1/3 的颜色,最上面的两个像素被开启,而最下面的两个像素被开启,用于大于 2/3 的颜色值。 也就是说,每个像素值代表一个“阈值”级别,这就是为什么抖动模式也被称为阈值映射。 定义的其余部分定义了您可以用于有序抖动操作符的阈值映射的名称(和可选别名)。 所以让我们试试...

  magick gradient.png  -ordered-dither hlines2x2  od_hlines2x2.gif
  magick shadow.png  -channel A \
          -ordered-dither hlines2x2   shadow_hlines2x2.gif
[IM Output]
[IM Output] ==> [IM Output]
如您所见,结果相当不错,但我们可以做其他事情来改善结果。 通过调整映射中的阈值,我们可以更改边界,因此它不会将颜色空间划分为 3 个相等区域...
[IM Output]
请注意,我将除数增加到 '10',以便将颜色级别划分为十个相等的部分。 然后我更改了阈值设置,使模式从透明端(黑色)的 30% 阈值开始,到完全不透明(白色)的 90% 结束。 这是更改阈值映射的结果。

  magick gradient.png  -ordered-dither hlines2x2a  od_hlines2x2a.gif
  magick shadow.png -channel A \
          -ordered-dither hlines2x2a  shadow_hlines2x2a.gif
如您所见,这拓宽了使用纯水平线作为抖动模式的半透明像素的范围。 这提供了更好的阴影效果,尽管它可能只应与示例中使用的模糊程度较低的阴影一起使用。 但是请注意,这种类型的阈值更改非常少见。 虽然在这种情况下为预期用途辩护是合理的。 基本上它没有正确定义渐变,或者允许更亮和更暗的阴影图案。 为此,我们需要制作一个更复杂的阈值映射,具有更多像素和更多图案。

DIY 水平线抖动

在这里,我将上面创建的简单水平线抖动模式扩展成一组模式,以产生从“关闭”到“开启”的更平滑的渐变。 这是结果。

  montage dpat_hlines.gif   -filter box   -geometry 60x20+2+0 \
          -tile x1 -background none  -frame 2   dpat_hlines_images.gif
  magick gradient.png  dpat_hlines.gif  \
          -virtual-pixel tile  -fx 'u[(floor((n-1)*u)+1) % n]' \
          dgrad_dpat_hlines.gif
  magick shadow.png dpat_hlines.gif  -channel A \
          -virtual-pixel tile  -fx 'u[floor((n-1)*u)+1].g' \
          shadow_dpat_hlines.gif
[IM Output]
[IM Output]
[IM Output] ==> [IM Output]
如您所见,它现在包含 9 个 12x4 像素图像。 它不代表您可能具有的所有像素模式,但这增强了线条的效果。 此外,我将其高度加倍,以便适当地抖动线条中的间隙。 这是使用这种抖动模式的另一个示例...

  magick -size 120x55 xc:white  -draw 'fill #777 ellipse 50,43 30,5 0,360' \
          -motion-blur 0x15+180   -blur 0x2      sphere_shadow.png
  magick sphere_shadow.png dpat_hlines.gif \
          -virtual-pixel tile  -fx 'u[(floor((n-1)*u)+1) % n]' \
          sphere_shadow_dither.gif
  magick sphere_shadow_dither.gif   -fill red  -stroke firebrick \
          -draw 'circle 35,25 35,5'     sphere_shadow_hlines.gif
[IM Output] ==> [IM Output] ==> [IM Output]
下一步是将这组抖动模式转换为单个阈值映射图像,而不是一组多个图像。 这是通过使用一些花哨的图像操作将所有图像合并在一起来实现的。
[IM Output]
[IM Output]

  magick -size 1x10 gradient: -flip -crop 1x1 +repage -delete 0,-1 \
          -scale 12x4\! null: \( dpat_hlines.gif -delete 0 \) \
          -alpha off -compose CopyOpacity -layers Composite \
          -reverse -compose Over -flatten -alpha off dmap_hlines.png
[IM Output]
值 '10' 比抖动模式中图像的数量多 1,而 "-scale 12x4\!" 是要转换为阈值映射的抖动模式的大小。 结果是灰度映射,没有纯黑色或白色颜色。 用于像素的灰度级别意味着如果颜色级别等于或高于该灰度值,则应开启该像素。 也就是说,每个灰度级别是颜色值从黑色变为白色的“阈值”级别。 如果你喜欢以另一种方式看待图像,那么暗像素通常会导致这些像素在更多颜色级别时被开启。 而亮像素只有在图像颜色变得非常亮时才会被开启。 这几乎是对图像实际外观的否定,但如果你仔细想想,这是有道理的。 我还使用 PNG 图像而不是 GIF 图像作为映射,因为只需要保存一张图像,更重要的是,尝试为阈值保留 16 位质量级别。 GIF 只能处理 8 位颜色级别。 现在我们可以使用单个图像和对每个像素直接与抖动阈值图像(或映射)进行简单的阈值比较来抖动我们的图像。

  magick gradient.png dmap_hlines.png \
          -virtual-pixel tile  -fx 'u>=v'   dgrad_dmap_hlines.gif
[IM Output] ==> [IM Output]
看看阈值映射是多么简单。 您只有一张图像,并且每个通道都对每个像素进行一次直接比较。 这使得使用阈值映射进行抖动非常快。 比全彩色量化快得多。 这种简单性是 ImageMagick 和大多数图形软件使用阈值映射来保存各种抖动模式的原因。
直到 IM 版本 6.2.9-2,大于或等于('>=')测试才添加到 "-fx" 操作符中。 如果这是一个问题,请在上面使用反向测试 'v<u'。
但是,如果用户希望使用多个颜色级别进行抖动,这种简单性就会变得复杂得多。 这种概念的证明首先是在 Posterized Ordered Dither 页面上的示例中阐述的,然后再被合并到 IM 核心函数中。 现在我们有了合并的阈值图像,接下来我们需要将上面的图像转换为 IM 可以直接读取的 XML 阈值映射,并且 "-ordered-dither" 操作符可以使用它。 为此,我们需要将我们的图像输出为表示其代表的 9 个灰度级别的数字。 最好使用 NetPBM 或 PBMplus 图像格式,并使用 "NetPbm" 图像处理软件进行深度调整。 这个软件包通常是标准的 Linux 安装,所以大多数人已经拥有它,或者可以从他们通常的软件发行版中安装它。 "pnmdepth" 数字再次是阈值图像包含的灰度级别数量。

  magick dmap_hlines.png pgm:- | pnmdepth 9 | pnmnoraw > dmap_hlines.pgm
[IM Text]
上面所有数字(除了 'P2' 图像魔法标识符)都是生成适当“阈值映射”所需的数字,您可以将其添加到您的个人 "thresholds.xml" 文件中。 例如,以下是根据上面创建的结果阈值映射条目。
[IM Output]
以下是如何使用此阈值映射的示例。

  magick shadow.png  -channel A  -ordered-dither hlines   shadow_hlines.gif
[IM Output] ==> [IM Output]
这就是如何从一系列图像生成复杂的阈值映射。

使用符号模式抖动

现在,虽然您可以使用单个阈值映射或阈值图像,而不是用于大多数抖动操作的多图像模式集,但这并不意味着多图像映射没有其自身的用途。 您可以使用一组查找图像来一次性平铺多个区域,而不是一次一个。 例如,通过缩放一个简单的图像,然后用特定符号替换图像中的每个像素。 例如,在这里我使用非常小的“眼睛”图像 [IM Output] 并用各种符号替换单个像素,为原始图像中的每个像素生成这样的图案。

  montage dpat_symbols.gif   -geometry +5+0 \
          -tile x1 -background none -mattecolor blue  -frame 3 \
          dpat_syms_images.gif
  magick eyes.gif -alpha off -colorspace sRGB -grayscale Average \
          -alpha off -scale 1600% -negate  \
          dpat_symbols.gif -virtual-pixel tile -fx 'u[floor(15.9999*u)+1]' \
          eyes_syms.gif
[IM Output]
[IM Output]
montage 用于扩展多图像 GIF 图像,以便您可以看到它的内容,而不会“动画化”。 您可以从正常的 'Rec709Luminance' 到更暗的 'Rec709Luma' 来调整要使用的“-grayscale” 强度方法,或者使用非线性 'sRGB' 颜色空间或线性 'RGB' 颜色空间的“average”。 您甚至可以调整值的“-gamma” 缩放比例,以获得最佳的颜色分布。 可能性很多,什么好取决于您的符号排列而不是实际选择的方法。 上述的关键是确保输入图像中的每种颜色都产生一个独特的符号,而这可能很难实现。 此示例可用于创建爱好者可以遵循的十字绣或编织指南,从较小的计算机图像生成更大规模的艺术作品。 您可以使用此技术用一组平铺颜色图像来平铺灰度图像。 结果有点像在许多旧的电脑战争游戏中看到的景观地图。

  montage dpat_map.gif   -geometry +5+0 -tile x1  -background none  \
          dpat_map_images.gif
  magick -seed 100 \
          -size 200x200 plasma:'gray(50%)-gray(50%)' -blur 0x15 \
          -channel G -auto-level +channel -set colorspace sRGB \
          dpat_map.gif -virtual-pixel tile  -fx 'u[floor(5.999*u.g)+1]' \
          map.gif
[IM Output]
[IM Output]
请注意,我需要确保 IM 认为灰度图像已经处于最终的 sRGB 颜色空间中(因为平铺图像也是如此),即使它实际上是用于 FX 索引查找的线性 RGB 数据。 如果没有这个,生成的“地图”会偏向于树木繁茂的景观,而水域的机会很少。 如您所见,任何图像集都可以用作平铺,这些图像甚至不需要彼此对齐,或者甚至具有相同的平铺大小。 当然,如果平铺大小相同并且彼此密切相关,就像 3 个蓝色“海洋”平铺一样,平铺图案可以从一个平铺区域“流动”到另一个平铺区域。 通过用数字图像替换平铺,您还可以生成一种“按数字绘画”指南。 但是,可能需要进行一些额外的处理来对不同的区域进行边框处理。 这留作练习,将您的解决方案发给我,您可以在 IM 示例中以该技术作者的身份留下您的姓名。

有序抖动随机笔记和未来可能性

正在建设中

少量的颜色有序抖动。

当对小型图像使用少量颜色时,类似 IM 的希尔伯特曲线误差校正抖动,甚至更简单的 Floyd-Steinberg 误差校正抖动(参见上面的 误差校正抖动)会产生非常糟糕的结果。 理想情况下,应该对低颜色和小图标图像使用 有序抖动,以产生更好的外观。 但是目前 IM 中的有序抖动只能使用“固定”的数学生成的色表,而不仅仅是一组“最佳”颜色。

具有任何颜色映射的有序抖动

有一些算法可以让你对特定颜色集进行有序抖动。 基本上,通过将有序抖动算法可以生成的“伪颜色”(可能涉及三种颜色抖动)添加到给定的颜色映射中,以“填充它”。 然后,您可以将图像的单个像素“映射”到此“扩展颜色映射”。 从该初始映射到特定的伪颜色(实际上,可以从生成该伪颜色的阈值映射中选择实际颜色,从而对该颜色的有序抖动区域,以及整个图像到给定的颜色集进行有序抖动。 由于我在图标和 GIF 动画方面的背景,我当然希望看到任何颜色有序抖动实现,但我还没有找到关于如何使用固定颜色集进行有序抖动的实用参考。