ImageMagick 示例 -
颜色修改

索引
ImageMagick 示例前言和索引
将颜色转换为灰度
图像级别调整
使用直方图修改进行调整 (更改图像的直方图)
DIY 级别调整 (通用色调运算符)
对图像的中间色调进行色调处理 (通用色调运算符)
全局颜色修改器
使用查找表重新着色图像
这里,我们将介绍修改整个图像中所有颜色的技术。无论是使图像变亮或变暗,还是更大幅度的颜色修改。
为了探索这些技术,我们需要一个测试图像...
不用担心我是如何生成这个图像的,这对于练习并不重要。我确实设计了它,使其包含一系列颜色、透明度和其他功能,特别是为了让 IM 在使用时得到充分锻炼。
[IM Output]
如果您真的对用于生成此图像的命令感兴趣,您可以查看我用来创建它的特殊脚本“generate_test”。
警告:以下颜色处理过程通常假设图像使用线性颜色空间。然而,大多数图像都是使用 sRGB 或伽马校正颜色空间保存的,因此为了使结果正确,应该首先应用颜色空间校正。

将颜色转换为灰度

灰度图像在许多用途下都非常有用,例如,进一步处理原始图像或用于背景合成。将图像转换为灰度的最佳方法是直接让 IM 将图像转换为灰度 颜色空间 表示。

  magick test.png  -colorspace Gray   gray_colorspace.png
[IM Output] ==> [IM Output]
请注意,蓝色比红色暗得多,这是因为权重匹配了它们的强度,因为它们看起来像是人眼看到的。也就是说,“红色”的颜色比看起来更暗的“蓝色”要亮得多。
这等效于使用 'rec709luma' 转换公式,使用专用 “-grayscale” 运算符(在 IM v6.8.3-10 中添加)。

  magick test.png  -grayscale rec709luma  gray_grayscale.png
[IM Output]
'rec709luma' 值只是许多灰度化公式中的一种,这些公式被定义为 “-intensity” 设置(见下文)使用。
例如,以下是另一个常见的灰度化公式 'rec601luma'

  magick test.png  -grayscale rec601luma  gray_grayscale_601.png
[IM Output]
您可以看到,不同红色、绿色和蓝色颜色通道的强度级别略有不同。
然而,还有许多其他方法和'灰度'的含义...
例如,您可以使用 调节运算符 将图像中的所有颜色都滤掉,将所有颜色饱和度级别设置为零。

  magick test.png  -modulate 100,0  gray_modulate.png
[IM Output]
这实质上将图像转换为 HSL 颜色空间,并从该颜色空间中提取灰度 '亮度' 值。但是,使用“-define modulate:colorspace”,您可以指定其他要使用的颜色空间模型。请参阅以下 在其他颜色空间中调节。请注意,我在测试图像中使用的中心彩色圆盘的 IM '绿色' 颜色实际上不是纯绿色,例如彩虹中使用的绿色,而是由新的 SVG - 可缩放矢量图形 标准定义的半亮绿色。如果您需要纯 RGB 绿色,可以使用颜色 'lime'。有关更多详细信息,请参阅 颜色名称冲突
另一种方法是使用 FX DIY 运算符 对三个通道进行平均,以获得灰度的纯数学意义。

  magick test.png -fx '(r+g+b)/3' gray_fx_average.png
[IM Output]
sRGB 通道值的平均值也等效于 'OHTA' 颜色空间(红色通道)的强度通道。或 HSI 的 'I' 通道。颜色空间。
另一种技术是简单地将三个通道加在一起(一种称为曼哈顿距离的颜色度量),虽然生成的图像不会由于“量子舍入”效应而丢失信息,但您可能会丢失有关最亮颜色的信息。不幸的是,您也会丢失透明度通道。

  magick test.png -separate \
          -background black -compose plus -flatten   gray_added.png
[IM Output]
您可以使用相同的添加通道技术来控制各个颜色通道的权重。例如,以下是一个您可以使用的 DIY 公式...

  magick test.png -fx '0.3*r+0.6*g+0.1*b' gray_diy.png
[IM Output]
如果您想要在 “-fx” 运算符中获得相同的含义,也可以使用 'intensity'。

  magick test.png  -fx intensity  gray_intensity.png
[IM Output]

然而,由于解释了 FX DIY 运算符,它可能会运行得非常慢。对于更复杂的运算,您可以使用更简单的 评估运算符,“-evaluate”。例如,以下是一个 2/5/3 比例的灰度图像,但同样,我没有尝试保留原始图像的透明度通道。

  magick test.png -channel R -evaluate multiply .2 \
                   -channel G -evaluate multiply .5 \
                   -channel B -evaluate multiply .3 \
                   -channel RGB -separate -compose add -flatten gray_253.png
[IM Output]
对于以 'Q8' 质量级别 编译的 ImageMagick,以上将遭受“量化”效应。这是因为 “-evaluate” 的结果将保存到一个用于图像值的 8 位小整数中。只有在之后,这些值才会被加在一起,并导致精度损失。

使用 'Q16' 编译的 ImageMagick,或者更好的是使用 HDRI 质量编译选项将产生更精确的结果。另一个新的替代方案是 Poly - 加权图像合并运算符,它将在一个操作中完成分离通道图像的加权和加法,从而避免“量子舍入”效应。

可以使用类似的技术通过直接平均三个 RGB 通道来生成纯数学灰度。

  magick test.png -separate -evaluate-sequence mean  gray_average.png
[IM Output]
但是,您可以看到,我没有尝试保留结果图像的 alpha 通道。另一个快速的替代方案是使用 “-recolor” 颜色矩阵运算符,它允许您指定三个颜色通道的权重。

  magick test.png -recolor '.2 .5 .3
                             .2 .5 .3
                             .2 .5 .3'   gray_recolor.png
[IM Output]
这不会影响透明度,但使其成为使用特定权重转换颜色的更好方法。基本上,第一行数字是结果图像红色通道的通道权重,接下来的三个数字是绿色,最后三个数字是蓝色。
您也可以使用 “-type” 告诉 IM 在读取或写入图像时将图像视为灰度。

  magick test.png  -type GrayScaleAlpha  gray_type.png
[IM Output]
-type” 设置通常仅用作图像被读入或写入文件时的指导。因此,它的操作被延迟到图像的最终写入。它的效果还高度依赖于所涉及的图像文件格式的功能,并用于覆盖 ImageMagick 在该过程中的正常确定。有关更多信息,请参阅 类型 示例。
在 IM v6.3.5-9 之前,由于错误,以上将删除写入图像中的任何透明度(等效于“-type Grayscale”)。在我注意到问题并上报后,这个问题就很快得到了修复。(这里有一课 :-))

一种更有趣的方法是从各种 颜色空间 表示的图像中提取不同的亮度含义,以提取相应的 颜色通道。有关示例,请参阅 来自颜色空间表示的灰度通道

图像级别调整

您可以对图像进行的最基本形式的调整称为“级别”调整。这基本上意味着获取各个 RGB 颜色值(甚至 alpha 通道值)并调整它们,以便拉伸或压缩这些值。由于仅调整通道值,因此最好在灰度图像上演示它们,而不是彩色图像。但是,如果您以相同的量调整图像的所有颜色通道,则可以使用它们处理彩色图像,以增强或调整图像。不要将此与我们将在下面下一节示例中讨论的更自动的级别调整形式混淆,即 归一化调整。此函数将执行完全相同的操作,而不管图像的实际内容是什么。图像的亮度或暗度无关紧要,也不管其是蓝色还是黄色色调。操作对实际图像内容视而不见。 [IM 图表] 在演示这些操作时,我将使用修改后的 “gnuplot” 图表(如右图所示),我使用一个特殊的脚本 “im_graph” 来生成它。该图表有一条红线,它将给定的原始 'x' 值(代表最上面的渐变的灰度值)映射到显示的 'y' 值。生成的彩色渐变也显示在输入线性渐变下方。右侧显示的图表是 IM “-noop” 运算符的图表,该运算符实际上不对图像执行任何操作。因此,每个图像的颜色值都直接映射到完全相同的值,没有改变。因此,较低的渐变与较高的渐变相同。

图像反转

你可以进行的最简单、最基本的全局级别调整是反转图像,使用“-negate”图像操作符。本质上,这会将白色变为黑色,黑色变为白色,并调整所有颜色以匹配。也就是说,它会将红色变为其互补色青色,蓝色变为黄色,等等。你可以通过下面显示的映射图看到这一点,因为我在“测试”图像和标准 IM “玫瑰”内置图像上使用了“-negate”操作符。注意映射图图像中的较低梯度现在被反转了,因此黑色和白色被交换了,并且在反转的“测试”图像中出现了相同的反转。

  magick test.png  -negate  test_negate.png
  magick rose:    -negate  rose_negate.gif
[IM Output]
[IM Output]
==> [IM Graph] ==> [IM Output]
[IM Output]
在内部,反转实际上相当愚蠢。它独立地处理三个颜色通道,默认情况下忽略 alpha 通道。如果不是这样,你将得到一个非常愚蠢的结果,像这样...

  magick test.png -channel RGBA  -negate  negate_rgba.png
[IM Output] ==> [IM Output]
图像被反转了,你可以通过半透明的颜色梯度看到这一点。但由于透明度通道也被反转了,你丢失了图像中的所有不透明颜色。这就是“-channel”的默认设置是“RGB”的原因。有关更多信息,请参见 颜色通道。你可以将反转限制在一个通道上,例如绿色通道。这可能看起来不太有用,但在某些情况下它至关重要。

  magick test.png -channel green  -negate  negate_green.png
[IM Output] ==> [IM Output]
-negate”操作符实际上是它自己的逆运算。对具有相同“-channel”设置的图像进行两次反转会相互抵消。

  magick negate_green.png  -channel green  -negate  negate_restore.png
[IM Output] ==> [IM Output]
反转在图像处理中非常常见,尤其是在处理灰度图像时,作为其他处理选项之前或之后的步骤。因此,我建议你尝试一下,并在进行任何操作时牢记它,因为使用反转图像可以解决一些原本难以解决的问题。

直接级别调整

-level”操作符是更通用的级别调整操作符。你基本上需要提供两个值,一个“黑点”和一个“白点”,以及一个可选的第三个值(伽马调整),我将在 稍后讨论。它的作用是将图像中等于或小于“黑点”的任何颜色值映射为黑色(或 0 值)。类似地,任何等于或比“白点”更亮的色值将被映射为白色(或最大值)。这两个点之间的颜色然后被线性“拉伸”以填充完整的色值范围。这样做的效果是提高对比度,增强图像中的颜色。例如,这里是我们测试图像的 25% 对比度增强,使用与图表显示的相同值。由于你通常会从 0%100% 调整黑色和白色点相同的值,因此你只需指定“黑点”即可。白点将被调整相同的值向内移动。

  magick test.png  -level 25%,75%  test_level.png
  magick rose:    -level 25%      rose_level.gif
[IM Output]
[IM Output]
==> [IM Graph] ==> [IM Output]
[IM Output]
请注意,25% 对任何图像来说都是一个巨大的对比度增强,但它清楚地显示了它的作用。你不必同时改变“黑色”和“白色”点。相反,只调整颜色范围的一端是完全允许的。例如,我们可以制作一个非常亮或非常暗的玫瑰图像。

  magick rose: -level 0,75%     rose_level_light.gif
  magick rose: -level 25%,100%  rose_level_dark.gif
[IM Graph] ==> [IM Output]  [IM Graph] ==> [IM Output]
但是,我再次警告你,给定范围之外的颜色会被“裁剪”或“燃烧”,因此将不再可用于以后的图像处理。这是使用“-level”操作符的最大问题。 [IM 图表] 通过使用负值,你可以对图像进行一些粗略的降对比度操作。这意味着,你实际上不是提供一个颜色值来将值映射到“黑色”和“白色”,从而拉伸两者之间的颜色,而是压缩颜色值,以便将假想的负色映射到黑色或白色。结果是图像普遍变灰。

  magick rose: -level -25%  rose_decontrast.gif
[IM Output]

但是,这种降对比度图像的方法非常不准确,不建议使用,除非你的 IM 版本低于 6.4.2,在这种情况下你无法使用新的 反转级别操作符. [IM 图表] 你可以使用“-level”操作符来反转图像(如上面所示,只需交换给定的“黑色”和“白色”点值,使用“-level 100%,0”。

  magick rose: -level 100%,0  rose_level_neg.gif
[IM Output]

[IM 图表] 或者,通过将它们设置为相同的值,你可以有效地将图像中的所有颜色值阈值化。使用“-level”对图像进行阈值化与使用具有该值的 阈值操作符 是一样的。右侧显示的映射图显示了“-level 50%,50%”操作的结果及其对灰度梯度的影响。

  magick rose: -level 50%,50%  rose_level_thres.gif
[IM Output]

请注意,与“-threshold”不同,当使用默认的“-channel”设置时,图像不会自动转换为灰度图像。使用级别对图像进行线性修改的通用性质使得“-level”操作符非常适合一般的灰度图像修改和蒙版调整。再加上你可以修改单个通道(使用“-channel”设置)而不是整个图像,使其成为 IM 用户可用的最佳颜色修改操作符之一。
请注意,你也可以使用 评估和函数操作符 对颜色值进行更直接的数学修改,以实现与 -level 两种形式(+ 和 -)相同的结果)。
请注意,“-level”操作符将透明度通道视为“遮罩”值。因此,100% 表示完全透明,而 0% 表示不透明。在将“-level”与模糊形状图像一起使用时,请牢记这一点。这通常是在模糊“形状”图像后进行的,以扩展和拉伸结果。有关这方面的示例,请参见 柔和边缘阴影轮廓

反向级别调整-- 降对比度图像

从 IM 版本 6.4.2 开始,级别操作符 已扩展为提供“反转”形式“+level”(注意“加号”)。或者,你可以使用操作符的原始“-level”形式,但在给定的级别参数中添加“!”(对于旧的 API 接口)。该变体的参数完全相同,但它不是拉伸值以将“黑点”和“白点”映射到“黑色”和“白色”,而是将“黑色”和“白色”映射到给定的点。换句话说,“+level”是“-level”的精确反转。例如,这里我们将“黑色”映射到 25% 灰色,将白色映射到 75% 灰色,使用两种指定“反转”形式的方法,以非常精确的方式降对比度图像。

  magick test.png   +level 25%    test_level_plus.png
  magick rose:     -level 25%\!  rose_level_plus.gif
[IM Output]
[IM Output]
==> [IM Graph] ==> [IM Output]
[IM Output]
如果你将上面的“+level 25%”操作与我们之前显示的负降对比度“-level -25%”操作符进行比较,你会发现它们并不相同。“加号”版本产生了一个更强的降对比度图像(它更灰),但它通过将值映射到你给操作符的精确值来实现这一点,而不是“减号”形式使用的“假想”值。这种精确值使用非常重要,也是添加操作符“加号”形式的原因之一。当然,25% 再次是一个非常大的值,不建议在典型的图像处理工作中使用。请注意,“-level”和“+level”实际上在给定相同参数时是完全相反的。也就是说,一个将值映射到范围极端,而另一个从范围极端映射。例如,这里我们使用“+level”压缩图像的颜色,然后使用“-level”再次解压缩它们,以恢复接近原始外观的图像。

  magick test.png  +level 20%  -level 20%  test_level_undo.png
[IM Output] ==> [IM Graph] ==> [IM Output]
这两张图像看起来非常相似,由于我使用的是高 质量 的“Q16”版本 IM,你很难注意到任何差异。但是,值可能并不完全相同,因为你实际上已经将图像的颜色值压缩到更小的整数范围内,然后又恢复了它们。在极端情况下,这会导致 量子舍入效应。按照相反的顺序进行这两个操作(拉伸,然后压缩颜色值)会导致 量子裁剪效应。“+level”操作符的另一个有用方面是,你可以将图像中的所有颜色值完全压缩到相同的灰度级别。

  magick test.png  +level 30%,30%  test_level_const.png
[IM Output] ==> [IM Graph] ==> [IM Output]
通过根据每个通道的特定颜色的值指定级别,你可以有效地将灰度梯度转换为特定的颜色梯度。但是,这很难计算和执行。因此,还提供了一个“-level-colors”操作符,它允许你使用特定颜色而不是“级别”值来指定黑点和白点。请参见下面的 按颜色划分级别

级别伽马调整

以上两个“-level”变体也允许你使用第三个设置。“伽马”调整值。默认情况下,它设置为 1.0' 的值,它不会对生成的图像进行任何中间色调调整,而是生成从旧图像到新图像的值的纯线性映射。但是,通过将此值增大,你会使生成的线弯曲,从而使图像变亮,而缩小该值会使图像变暗。例如,这里我只使用“伽马”设置来调整图像的中间色调的亮度和暗度。

  magick rose: -level 0%,100%,2.0   rose_level_gamma_light.gif
  magick rose: -level 0%,100%,0.5   rose_level_gamma_dark.gif
[IM Graph] ==> [IM Output]  [IM Graph] ==> [IM Output]
值通常介于 10(耀眼的亮图像)到 0.2(非常暗的图像)之间。如前所述,值 1.0 不会对图像进行任何“伽马”更改。但是,特殊的“2.0”值(见上文)可用于获取图像归一化颜色的平方根。“-level”的两个版本都以相同的方式处理“伽马”。这意味着你可以将“黑色”和“白色”端点的级别调整与非线性“伽马”调整结合起来。你也可以只调整图像的单个通道。例如,这里我们在蓝色通道的黑色端给图像添加了微妙的色调,同时使用伽马来保留图像的中间色调色级。

  magick test.png  -channel B +level 25%,100%,.6 test_blue_tint.png
[IM Output] ==> [IM Graph] ==> [IM Output]
此特定示例可用于对气象卫星照片进行着色,其中只有海洋是纯黑色,而陆地则更灰。此蓝色通道调整的其他替代方案在下面的 DIY 数学非线性调整 中给出。

伽马操作调整

也提供了“-gamma”运算符,它与“-level”运算符中的“gamma”设置具有完全相同的效果。但是它允许您为每个单独的通道调整“gamma”调整级别。它真正的用途是在对图像执行线性运算之前调整图像的“gamma”函数。有关更多详细信息,请参见人类颜色感知和伽马校正。我们还可以使用此函数以不同的方式为每个单独的 RGB 通道增亮图像。

  magick rose: -gamma 0.8,1.3,1.0  gamma_channel.gif
[IM Output]
如您所见,这可用于对图像进行一些细微的着色和颜色调整,或校正包含过多特定颜色的图像。
有关为什么要使用此函数的原因,请参见伽马校正

此函数实际上等效于Evaluate POW 函数,但参数反转。因此,“-evaluate POW 2.2”实际上会执行“-gamma 0.45455”(0.45455 等于 1/2.2) 操作,这与“-gamma 2.2”相反。
-gamma”的一个不太明显的用途是将特定图像通道归零(参见归零颜色通道)。或者将图像完全涂成“黑色”、“白色”或其他主要颜色(参见主要彩色画布)。

按颜色级别调整

-level-colors”运算符已添加到 IM v6.2.4-1 中。本质上,它与我们上面讨论的级别运算符完全相同,但每个通道的值指定为颜色值。也就是说,“-level-colors”选项将给定颜色映射到“黑色”和“白色”,并将所有其他颜色线性拉伸到它们之间。这有效地从图像中去除了给定的颜色范围。虽然这有效,但它并不特别有用,因为它容易在某些通道具有相同值的颜色中失败。例如,“DodgerBlue”和“White”颜色在蓝色通道中具有相同的颜色值。因此,“-level-colors DodgerBlue,White”可能并不总是将这些颜色映射到黑色和白色。在这种情况下,更好的技术是提取具有最大差异的通道(如红色)的灰度图像,并对该通道进行级别调整或归一化。警告:注意“透明”颜色。
另一方面,运算符的加号形式“+level-colors”非常有用,因为它会将“黑色”和“白色”颜色映射到给定的值,将所有其他颜色线性压缩以适合您提供的两种颜色。例如,让我们将“black”和“white”映射到“green”和“gold”...

  magick test.png  +level-colors green,gold   levelc_grn-gold.png
[IM Output] ==> [IM Output]
如您所见,灰度梯度被重新映射到以给定颜色为边界的梯度,虽然灰度范围之外的颜色也会被修改,但它们也会遵循指定的颜色范围的基本风格。这使得“+level-colors”运算符非常有用,尤其是在映射灰度图像时。如果您只提供一个颜色名称,但包含逗号,则缺失的颜色将根据需要默认为“black”或“white”。

  magick test.png  +level-colors ,DodgerBlue   levelc_dodger.png
  magick test.png  +level-colors ,Gold         levelc_gold.png
  magick test.png  +level-colors ,Lime         levelc_lime.png
  magick test.png  +level-colors ,Red          levelc_red.png

  magick test.png  +level-colors Navy,         levelc_navy.png
  magick test.png  +level-colors DarkGreen,    levelc_darkgreen.png
  magick test.png  +level-colors Firebrick,    levelc_firebrick.png
[IM Output] [IM Output] [IM Output] [IM Output]
[IM Output] [IM Output] [IM Output]
这使得将灰度图像映射到您喜欢的任何颜色的梯度变得容易。例如,这里我将黑白色梯度重新映射到红白色梯度(注意参数中的“,”)...

  magick cow.gif   +level-colors red,   cow_red.gif
[IM Output] ==> [IM Output]
这不仅用“红色”替换了“黑色”,而且还将所有抗锯齿灰色重新映射到“红色”和“白色”的适当混合,从而产生非常平滑的结果。[IM Output] 如果我只是执行简单的直接颜色替换,将纯黑色转换为红色,我会得到可怕的图像(右边所示)。有关用于生成该图像的示例,请参见模糊因子。当然,如果您希望将其中一种颜色设置为透明,那么最好使用-alpha Shape运算符,因为这需要您将梯度转移到 alpha 通道。
如果您只指定一种颜色,没有任何“逗号”分隔符,则该颜色将用于黑色和白色点。这意味着图像中的所有颜色将被重置为该颜色。(根据当前“-channel”设置限制)。

  magick test.png  +level-colors dodgerblue  levelc_blue.png
[IM Output]
这与使用“-fill DodgerBlue -colorize 100%”对图像着色的结果相同(见下文)。如果您还希望设置图像的透明度设置,则需要将“-channel”设置为包含透明度通道,或者使用“-alpha opaque”或“-alpha off”将Alpha 通道设置为完全不透明。

  magick test.png -channel ALL +level-colors dodgerblue levelc_blue2.png
[IM Output]
另请参见空白现有图像。以下是一些使用此方法调整或“着色”彩色图像而不是灰度图像的示例。

  magick rose: +level-colors             navy,lemonchiffon  levelc_faded.gif
  magick rose: +level-colors        firebrick,yellow        levelc_fire.gif
  magick rose: +level-colors 'rgb(102,75,25)',lemonchiffon  levelc_tan.gif
[IM Output] ==> [IM Output] [IM Output] [IM Output]
总之,“+level-colors”是一种梯度颜色替换,一种线性着色运算符,也可以完全重置颜色。

S 型非线性对比度

在关于“图像处理基础”(第 44 页)的 PDF 文档中,他们提出了一种使用线性对比度控制(级别)的替代方法,其中一种使用伽马校正被称为“S 型非线性对比度控制”。结果是在整个颜色范围内产生非线性的平滑对比度变化(数学上的“S 型函数”),保留白色和黑色,这对于照片颜色调整来说要好得多。论文中的确切公式非常复杂,甚至有错误,但本质上需要两个调整值。对比度函数的中心阈值(通常位于“50%”),以及对比度因子(“10”非常高,“0.5”非常低)。
对于那些感兴趣的人,以下是“S 型非线性对比度控制”的校正公式...
( 1/(1+exp(β*(α-u))) - 1/(1+exp(β*(α)) ) / ( 1/(1+exp(β*(α-1))) - 1/(1+exp(β*α)) )
其中 α 是阈值,β 是要应用的对比度因子。

这是一个使用中间变量的公式的替代版本。
x = exp(β * (α - u)) y = exp(β + 1 结果 (x / y + 1) * (1 / (x + 1) - 1 / y)
该公式实际上是一个非常简单的指数曲线,上面公式的大部分旨在确保 0 保持为零,1 保持为一。也就是说,图表始终经过点 0,0 和 1,1。并且最大的变化梯度位于给定的阈值处。
例如,以下是在“-fx”中实现上述公式,结果是“10”的非常高的对比度值和“50%”的阈值。这些值已被滚动到浮点常量中,以加快函数速度。

  magick test.png  -fx '(1/(1+exp(10*(.5-u)))-0.006693)*1.013567' \
              sigmoidal.png
[IM Output] ==> [IM Graph] ==> [IM Output]
幸运的是,IM v6.2.1 将此复杂函数内置为一个新的运算符“-sigmoidal-contrast”,允许更简单的应用。

    magick test.png  -sigmoidal-contrast 10,50% test_sigmoidal.png
[IM Output]
作为奖励,IM 还提供了反函数,即“S 型对比度降低”函数(作为运算符的加号“+”形式),如果使用相同的参数应用,它会恢复我们的原始图像(几乎完全相同)。

    magick test_sigmoidal.png +sigmoidal-contrast 10,50% \
                                             test_sigmoidal_inv.png
[IM Output]
这里我们将它应用于玫瑰图像...

    magick rose: -sigmoidal-contrast 10,50%  rose_sigmoidal.gif
[IM Output]
我确实说过“10”是一个非常重的对比度因子。事实上,任何高于此值的都可被认为更像是模糊阈值操作,而不是对比度增强。有关使用此运算符的实际示例,请参见高级“凝胶”效果示例,其中它用于锐化添加到形状区域颜色的亮区。

其他对比度运算符

正在建设中
   -contrast  and   +contrast
         Rather useless minor contrast adjustment operator

-threshold
   Threshold the image, any value less than or equal to the given value is
   set to 0 and anything greater is set to the maximum value.

   Note that like level, this is a channel operator, but if the default
   'channel setting' is used only the gray-scale intensity of the image is
   thresholded producing a black and white image.

   magick rose: -threshold 45%  x:

   You can force normal channel behaviour, where each channel is thresholded
   individually buy using "-channel All"

   magick rose: -channel All -threshold 45%  x:

-black-threshold
-white-threshold
   This is like -threshold except that only one side of the threshold value is
   actually modified.

   For example, here anything that is darker than 30% is set to black.

   magick rose: -black-threshold 30%  x:
   magick rose: -white-threshold 50%  x:

   These operators however do not seem to be channel effected, so may only be
   suitable for gray-scale images!


使用直方图修改进行调整

本节由Fred Weinhaus和 Anthony Thyssen 共同完成。什么是直方图?直方图是一种特殊的图形。它只是将图像中像素的颜色级别按固定数量的“箱”进行排序,每个“箱”跨越一定范围的值。因此,每个“箱”都包含一个计数,表示图像中落在该范围内的颜色级别(像素值)的数量。结果是对构成图像的颜色值的分布的表示,从左侧的黑色到右侧的白色。
可以分别为每个通道生成直方图,也可以作为全局直方图,它查看来自所有通道组合的值。结果通常显示为条形图的图像。在 IM 中,这是使用特殊的直方图:输出格式完成的。例如...

  magick rose: histogram:histogram.gif
[IM Output]
但它也可以显示为折线图,其中折线连接条形的顶部。这将在下面的讨论中进行演示。有关此特殊输出格式的更多详细信息,请参见直方图:。建议您此时阅读,因为这是使用 IM 提取图像的直方图信息的最有效方法。直方图的实际高度几乎没有实际意义,因为它通常按比例缩放,以便最高峰触及图像的顶部。因此,每个“条”的高度并不相关。更重要的是整个范围内的直方图分布,以及整个图表中相对高度之间的关系。查看直方图时,您会考虑以下因素。
  • 直方图是否形成一个宽阔的值带?这意味着图像广泛利用了颜色空间,因此具有良好的对比度。
  • 或者它是否全部集中在中间或范围的一端?这意味着图像的对比度很低,使其看起来“模糊”或“灰暗”,或者可能过亮或过暗。
  • 它是否形成两个或多个峰值?由于图像中存在高度不同的区域或区域。
  • 大多数像素在哪里?在左侧,意味着图像非常暗。或者在右侧,意味着它非常亮。或者分布在中间周围?
  • 各个条之间是否有规律的间隙或空白?这通常意味着图像的像素很少,因此它无法完全填充整个直方图,或者图像的颜色被降低了,或者以某种方式进行了修改,从而产生了这些间隙。
本质上,直方图是图像的简化表示,因此它更容易改变或调整图像的直方图。几乎任何对图像进行的数学颜色变换通常都会导致图像不仅被修改,而且其直方图也会被修改。这些包括线性运算,如水平算子,或非线性运算,如伽马算子(见上文)。我们上面看到的映射图表示图像中的灰度级以及图像直方图将如何变换。例如,让我们创建一个低对比度的图像来演示。但是,最终结果是它不仅修改了图像,而且通过修改图像的直方图(通过压缩它)来实现。

  magick chinese_chess.jpg -contrast -contrast -contrast -contrast \
          chinese_contrast.png

  magick chinese_chess.jpg     histogram:chinese_chess_hist.gif
  magick chinese_contrast.png  histogram:chinese_contrast_hist.gif
[IM Output] ==> [IM Output]
[IM Output] ==> [IM Output]
在上述情况下,“-contrast”是一个简单的水平型算子,它只为图像添加了一点点对比度。结果是直方图本身被更多地扩展,使其更好地覆盖了所有可能的颜色范围。您还可以从前后直方图中看到,由于拉伸的方式,颜色最终也会在“箱”之间出现间隙和孔洞。具体来说,它会创建一个“直方图”,其中所有颜色都被放置到“箱”中。这些“箱装”的颜色随后作为一个整体被修改,导致图像颜色被分组在一起。这不是处理图像颜色的一种特别好的方法。然而,此算子是盲目工作的,它不知道图像内容或颜色分布。因此,在没有一些用户控制的情况下无法进行,因为算子很容易让它应用于任何图像,使其变得更差,而不是更好。在本节中,我们将研究图像处理算子,它们将图像的直方图作为其决策过程的一部分进行检查。然后,它使用此研究的结果来修改图像,以增强图像颜色分布的某些质量。由于这些算子利用了来自正在处理的图像的实际信息,因此它们通常可以更全局地应用于许多图像,而用户只需进行很少的检查。此类算子包括自动线性“水平”型算子,如“-normalize”、“-contrast-stretch”和“-linear-stretch”,但也包括非线性算子,如“-equalize”,以及其他可能最终被包含在 ImageMagick 中的算子,例如Fred Weinhaus 的脚本,“redist”。

直方图拉伸

最简单的技术,就像前面的例子一样,只是将图像的直方图向外拉伸以改善颜色范围。但是,他们不是盲目地为水平操作选择黑点白点,而是根据图像直方图选择点。基本上,他们从每个端点向内计数每个直方图箱中的颜色值数量,直到达到某个阈值。然后,这些点将用作直方图(水平)拉伸的黑点白点需要图表基本上,直方图计数提供了拉伸将强制转换为黑和白的灰度级值。这意味着图像中所有落在从纯黑到选定黑点箱对应的灰度级的箱子范围内的像素最终都会变成纯黑。同样地,图像中所有落在从纯白到白点箱对应的灰度级的箱子范围内的像素最终都会变成纯白。然而,落在这些点之外的像素将被拉伸到可能的颜色值范围之外,因此它们将被简单地设置为范围限制。也就是说,这些像素被“裁剪”,“刻入”,因为它们被转换为纯黑或纯白的极端颜色值。因此,如果选择黑点白点的“阈值”限制设置得太高,您将在图像中获得大量的黑白色区域,结果直方图在极端箱中具有大量的计数(高条)。严重刻入的例子 - 中国象棋图像?“拉伸”算子总结... -contrast-stretch 和 -linear-stretch 都生成一个直方图(使用 1024 个箱子)来确定要拉伸的颜色位置。因此它不是“精确的”。另一个区别是“零”的处理方式,以及 -linear-stretch 实际上执行 -level 操作来进行拉伸,而 -contrast-stretch 使用直方图箱值进行颜色替换拉伸(这会引入 1024 量化舍入效应。-normalize 在内部使用 -contrast-stretch。一个数学上完美的归一化拉伸算子是 -auto-level。虽然完美的“仅白点”或“仅黑点”版本是可能的,但目前还没有实现。

自动水平 - 完美的数学归一化

-auto-level”查找图像中最大和最小值,用于将图像拉伸到完整的量子范围。直方图拉伸到值范围之外,不会导致任何值被“裁剪”或“刻入”。“-channel”设置将确定所有通道是否以相同的方式“同步”拉伸(使用所有通道的最大值和最小值),还是分别拉伸(每个通道作为一个独立的实体)。目前,完全透明像素的隐藏颜色也用于确定水平,这会导致透明度出现问题。这被认为是一个错误。
FUTURE: We actually need three modes of operation...
  synced color channels with 'alpha' (and 'read') masking.
  synced channels (as defined by channel)       (current default)
  individual separate channels   (currently if -channel is set by user)
它是一个纯粹的数学直方图拉伸,就像手动水平算子一样。也就是说,最小值将被调整为零,最大值将被调整为量子范围,并且使用线性方程来调整图像中的所有其他值。它不使用“直方图箱”或其他方法可能用于确定要使用的水平或其他直方图调整的“值分组”。

归一化

-normalize”算子是这三个算子中最简单的。它只是扩展灰度直方图,使其占据灰度值的完整动态范围,同时在直方图的低端(黑色)裁剪或刻入 2%,在高端(白色)裁剪或刻入 1%。也就是说,图像中最暗的灰色的 2% 将变为黑色,最亮的灰色的 1% 将变为白色。这在大多数图像中不是很大的损失,总的结果是图像的对比度(强度范围)将被自动最大化。这里需要一个理想化的图表!使用中国象棋的例子?这里,我们创建一个灰度渐变,并将其扩展到完整的黑白色范围。

  magick -size 150x100 gradient:gray70-gray30 gray_range.jpg
  magick gray_range.jpg  -normalize  normalize_gray.jpg
[IM Output] ==> [IM Output]
出于与 JPEG 颜色不准确性(有关更多详细信息,请参阅JPEG 颜色失真)和扫描图像噪声有关的实际原因,“-normalize”不会扩展最亮和最暗的颜色,而是会扩展到这些值之外一点。也就是说,它等效于“-contrast-stretch”,其值为“2%,99%”(见下文)。

这意味着如果最高和最低颜色值非常接近,“-normalize”将失败,并且不会采取任何措施。

如果您确实想将最亮和最暗的颜色值精确地扩展到其极端值,请改用“-auto-level”。
在 IM 版本 6.2.5-5 之前,“-normalize”纯粹作为灰度算子工作。也就是说,每个红色、绿色、蓝色和 alpha 通道都会根据“-channel”设置独立地扩展。从 IM 版本 6.2.5-5 开始,如果只给出默认的“+channel”设置,那么“-normalize”将把所有颜色通道捆绑在一起,并以相同的量归一化它们。这确保了图像中的像素颜色不会发生偏移。但是,这也意味着您可能不会获得纯白色或黑色像素。例如,这里我们在我们的归一化测试图像中添加了一些额外的颜色(从蓝色到深蓝的渐变)。

  magick -size 100x100 gradient:gray70-gray30 \
          -size  50x100 gradient:blue-navy  +append  color_range.jpg
  magick color_range.jpg -normalize  normalize.jpg
[IM Output] ==> [IM Output]
从最后一个例子中可以看到,对于彩色图像,“-normalize”将所有通道一起最大化,因此一个通道具有零值,另一个通道具有最大值。也就是说,没有生成黑色像素,因为所有添加的蓝色颜色已经在“红色”和“绿色”通道中包含“零”值。因此,图像的下界没有扩展。
如果您想要旧的“-normalize”行为(在 IM v6.2.5-5 之前),您需要指定任何非默认的“-channel”设置。对于不包含 alpha(或遮罩)通道的图像,您只需使用“all”通道设置。

  magick color_range.jpg -channel all  -normalize   normalize_all.jpg
[IM Output]
或者,您可以使用“-separate”算子(从 IM v6.2.9-2 开始)将每个通道作为单独的图像进行归一化,然后将它们“-combine”到一个图像中。

  magick color_range.jpg -separate -normalize -combine normalize_sep.jpg
[IM Output]
在这最后两个例子中,我们看到图像的灰度区域变成了黄色,因为“red”和“green”通道被增亮了,而“blue”通道只被稍微暗淡了。这让我们想到一个重要的问题。
归一化和其他直方图算子实际上是灰度算子,
在使用彩色图像时需要谨慎。
实际上,“-normalize”只是更通用的“-contrast-stretch”的一个子集,它具有黑点 2% 和白点 = 1% 的默认值。那么“-contrast-stretch”是什么?

对比度拉伸

-contrast-stretch”算子(在 IM v6.2.6 中添加)类似于“-normalize”,不同之处在于它允许用户指定将被裁剪或刻入的像素数量。也就是说,它为您提供了一些控制,用于选择用于直方图拉伸的“黑点”和“白点”。因此,用户指定图像中最暗灰色的计数(或百分比计数)变为黑色,以及最亮灰色的计数变为白色。例如,这将用它们的极端值(白色和黑色)替换最上面的和最下面的 15% 的颜色,并相应地拉伸剩余的 70% 的颜色。最终结果是尝试改善图像的整体对比度。

  magick gray_range.jpg  -contrast-stretch 15%  stretch_gray.jpg
[IM Output] ==> [IM Output]
您还可以轻松地看到上面渐变顶部的“刻入”和“裁剪”效果,因为这些灰色被拉伸到颜色范围的限制之外。
在这里,我故意“刻入”了 90% 的较暗灰色,只留下 10% 的最亮像素被拉伸到图像顶部的紧密线性渐变中。

  magick gray_range.jpg  -contrast-stretch 90%x0%  stretch_black.jpg
[IM Output]
这在查找图像中最亮的“N”个像素时非常有用,因为它们将是唯一不会被“烧毁”为零值的像素。(更好的方法是使用“-threshold-black”)。“-contrast-stretch”的一个重要方面是使用零作为黑点白点阈值计数。在这种情况下,“-contast-stretch 0”将定位图像直方图中的最小和最大 bin。由于计数实际上从这些 bin 开始,因此结果只是将最小和最大 bin 拉伸到全黑和全白。这将导致对比度拉伸,最小或可能为零的裁剪量,所有这些“bin”中的值都将变为 0 和最大值。
 
正在建设中

线性拉伸

在许多方面,“-linear-stretch”与之前的“-contrast-stretch”运算符非常相似。这两个函数都可以接受黑点和白点参数,这些参数可以是原始计数或参与的总像素数的百分比。但是,有一些重要的区别。一个区别与如何计算默认的黑点和白点有关。对于“-contrast-stretch”,如果只提供一个值(黑点),则白点将是相同的值。因此,“-contrast-stretch 1”等效于“-contrast-stretch 1x1”,而“-contrast-stretch 1%”等效于“-contrast-stretch 1x1%"”。但是,对于“-linear-stretch”,如果只提供一个值(黑点),则白点将是补值。也就是说,如果黑点被指定为原始计数,则白点将是图像中的总像素数减去黑点计数。同样,如果黑点被指定为百分比计数,则白点将是 100% 减去黑点百分比计数。因此,“-linear-stretch 1%”将等效于“-linear-stretch 1x99%”。第二个区别与计数从何处开始有关。考虑一个具有 256 个 bin 的直方图(一些“bin”可能具有零计数),从灰度级 0 到灰度级 255。在“-contrast-stretch”中,计数从图像中填充率最低(最小)和最高(最大)的 bin 开始(这些 bin 可能在直方图中的 bin 0 或 bin 255 处,也可能不在)。因此,黑点为 10% 将从最小 bin 后的所有 bin 开始累积计数,直到它达到 10%,并将黑侧从该灰度级拉伸。因此,直方图黑侧的烧入量将最终是 10% 加上之前在较暗的“bin”中已经找到的量。从直方图亮侧计数也是如此。对于“-linear-stretch”,计数从直方图的末端开始,即 bin 0 和 bin 255。因此,暗侧的烧入量将始终是黑点值,亮侧的烧入量将始终是白点值。例如,让我们取一个 100 像素的渐变并查看它的直方图。

  magick -size 1x100 gradient: \
          -depth 8 -format "%c" histogram:info:
[IM Text]
正如预期的那样,每个 bin 都以单个像素填充,产生 1 的计数。(要查看完整列表,请点击上面的输出文本图像)。现在让我们在使用“-contrast-stretch 10x10%”后做同样的事情。

  magick -size 1x100 gradient:   -contrast-stretch 10x10%  \
          -depth 8 -format "%c" histogram:info:
[IM Text]
现在是“-linear-stretch 10x10%”。

  magick -size 1x100 gradient:   -linear-stretch 10x10%  \
          -depth 8 -format "%c" histogram:info:
[IM Text]
因此我们确认,对于“-contrast-stretch 10x10%”,我们在每端获得 11 个像素。也就是说,等效于端 bin 中的计数加上图像像素的 10%,即等于 10 个像素。所以 10+1=11 个像素被烧入。另一方面,在“-linear-stretch”中,端 bin 最终只包含 10 个像素或图像的 10%。上述区别的一个结果是,如果填充率最低和/或最高的 bin 不是 0 和 255 处的端 bin,则“-contrast-stretch 0x0”可能会更改图像。在这种情况下,图像将在对应于这些 bin 的灰度级之间拉伸。另一方面,“-linear-stretch 0x0”永远不会更改图像。例如,让我们取一个渐变,将其灰度级压缩每端 10%。也就是说,我们将黑点向上移动 10% 到灰度级 26,并将白点向下移动 10% 到灰度级 230。

  magick -size 1x100 gradient:   +level 10x90%  \
          -depth 8 -format "%c" histogram:info:
[IM Text]
现在,让我们将“-contrast-stretch 0x0”应用于上述反对比度渐变

  magick -size 1x100 gradient: -level 10x90%  -contrast-stretch 0x0  \
          -depth 8 -format "%c" histogram:info:
[IM Text]
现在是“-linear-stretch 0x0”。

  magick -size 1x100 gradient: -level 10x90%  -linear-stretch 10x10% \
          -depth 8 -format "%c" histogram:info:
[IM Text]
因此我们看到原始图像的直方图没有跨越 0 到 255 的完整动态范围。它只在灰度级 26 到 230 之间。但在应用“-contrast-stretch 0x0”之后,它被拉伸到了完整的动态范围。另一方面,“-linear-stretch 0x0”没有对生成的直方图进行任何更改。第三个区别是“-contrast-stretch”对通道敏感,而“-linear-stretch”则不敏感。这意味着,使用“-contrast-stretch”,任何一个或多个通道都可以更改而不会影响其他通道。因此,如果未指定通道,则将使用来自所有通道的总体直方图以相同的方式修改所有通道,从而不会产生颜色偏移。但是,如果指定了“-channel RGB”,则每个通道将被单独拉伸,结果将取决于每个通道中的端 bin。如果它们不同,则在生成的图像中的各个通道之间将产生颜色偏移。对于“-linear-stretch”,所有通道将以共同的方式进行处理,从而确保不会产生通道之间相对于彼此的颜色偏移。因此,让我们获取一个详细的识别和真实图像的直方图。

  magick port.png  -verbose -identify +verbose  histogram:port_hist.gif
[IM Text] [IM Output]
[IM Output]
我们看到上述图像的通道都没有跨越完整的动态范围。还要注意,每个通道都跨越了独特的不同值范围。现在让我们在没有“-channel”设置的情况下应用“-contrast-stretch 1x1%”。

  magick port.png -contrast-stretch 1x1% \
          -write histogram:port_cs1_hist.gif   port_cs1.png
[IM Output] [IM Output]
在上述结果中,图像在所有通道中一致拉伸。因此,通道之间没有颜色偏移。现在让我们做同样的事情,但使用“-channel RGB”。

  magick port.png  -channel RGB  -contrast-stretch 1x1% \
          -write histogram:port_cs1rgb_hist.gif    port_cs1rgb.png
[IM Output] [IM Output]
在上述结果中,因为我们设置了“-channel RGB”,而不是使用默认的通道设置,所以图像在每个通道中都被不同地拉伸。这会导致通道之间的颜色偏移。现在让我们在没有“-channel”设置的情况下应用“-linear-stretch”。

  magick port.png   -linear-stretch 1x1% \
          -write histogram:port_ls1_hist.gif \
          port_ls1.png
[IM Output] [IM Output]
在上述结果中,图像在所有通道中一致拉伸。因此,通道之间没有颜色偏移。现在让我们做同样的事情,但使用“-channel RGB”。

  magick port.png  -channel RGB  -linear-stretch 1x1% \
          -write histogram:port_ls1rgb_hist.gif    port_ls1rgb.png
[IM Output] [IM Output]
在上述结果中,使用“-linear-stretch”,图像在所有通道中一致拉伸,而“-channel RGB”被忽略。因此,通道之间没有颜色偏移,结果与上面没有“-channel RGB”的结果相同。 

直方图重新分配

直方图重新分布是一种非线性技术,它重新分布直方图中的 bin 以实现某种特定形状。两种最常见的形状是均匀(平坦)和高斯(钟形),尽管双曲线和瑞利也是其他类型的分布,也已被使用。

均衡 - 均匀直方图重新分布

对于均匀分布的情况,直方图 bin 被移位、间隔和组合,以便平均而言,直方图在整个范围内具有平坦或恒定的高度。这称为直方图均衡。IM 函数“-equalize”就是这么做的。不幸的是,它分别对每个通道进行操作,而不是对所有通道应用相同的操作。因此,当它应用于 RGB 色彩空间时,可能会发生颜色偏移。以下是在使用 IM 函数 -equalize 进行直方图均衡的示例。请注意每个通道独立均衡带来的颜色平衡偏移。

  magick zelda.png  -write histogram:zelda_hist.gif \
          -equalize  -write histogram:zelda_equal_hist.gif \
          zelda_equal.png
[IM Output] ==> [IM Output]
[IM Output] ==> [IM Output]
您可能会注意到直方图看起来不太均匀。但是,如果我们将生成的图像转换为灰度并显示其直方图,与原始图像的灰度直方图相比,它的直方图看起来更加均匀。

  magick zelda.png  -colorspace gray   histogram:zelda_ghist.gif

  magick zelda_equal.png  -colorspace gray \
          histogram:zelda_equal_ghist.gif
[IM Output] ==> [IM Output]
另一种方法是使用从每个通道的单独累积直方图和所需的集成分布曲线生成的变换查找表来重新分布 bin。如果不想在通道之间产生任何颜色偏移,则使用来自图像所有通道的组合直方图。近似方法只是使用将图像转换为灰度后的直方图。 Fred Weinhaus 开发了一个名为“redist”的脚本,它正是这样做的。它将图像的直方图重新分布到均匀分布中,同时对所有颜色通道进行相同更改。

  redist -s uniform zelda.png  zelda_uniform.png

  magick zelda_uniform.png   histogram:zelda_uniform_hist.gif
[IM Output] ==> [IM Output]
[IM Output] ==> [IM Output]
请注意结果与 IM 内置的“-equalize”运算符有何不同。具体来说,所有颜色都保留,没有您之前看到的颜色偏移。该脚本的作用是对灰度直方图进行操作,然后将其应用于所有颜色通道,以便所有颜色保持在一起。为了与 IM“-equalize”直方图进行比较,让我们也在这里显示灰度直方图结果。请注意,重新分布的直方图似乎比 IM 均衡的直方图更平坦(平坦或均匀)。

  magick zelda.png  -colorspace gray   histogram:zelda_ghist.gif

  magick zelda_uniform.png  -colorspace gray \
          histogram:zelda_uniform_ghist.gif
[IM Output] ==> [IM Output]
将来:添加在其他色彩空间中均衡的示例!也就是说,HSL、HSB 和 CMYK 色彩空间中的灰度通道。

高斯重新分配

均衡直方图并不是更改图像直方图分布的唯一方法。实际上,它通常不太有用,除非在计算机视觉应用中。以下是一张相同的图像,但经过变换,使其直方图具有高斯(钟形)分布。这里使用的值是 60% 灰色平均值,在该平均值的任一侧都有 60 个 sigma 滚降。

  redist -s gaussian 60,60,60  zelda.png \
         zelda_gaussian.png

  magick zelda_gaussian.png -colorspace gray \
          histogram:zelda_gaussian_ghist.gif
[IM Output] ==> [IM Output]
[IM Output] ==> [IM Output]
从生成的灰度直方图中,您可以看到图像被修改,因此其颜色遵循高斯钟形曲线类型的分布。对于照片,这会产生更“自然”的外观。图像不仅会被优化对比度,还会调整亮度,以便图像中的大多数像素都具有大约 60% 的灰度亮度。

直方图重新分配方法

那么这种类型的直接直方图调整是如何工作的呢?基本上,它计算当前图像和所需分布的直方图。然后,它计算出每个“bin”的灰度级值需要如何更改,以便 bin 中的计数最符合所需的分布。某些 bin 可能被移到更暗的地方,而另一些 bin 可能被移到更亮的地方。这实际上是一个相当复杂的过程,所以让我们逐步进行。
首先,我们需要从 ImageMagick 获取实际的直方图数据,而不是直方图的图形图像。请注意,数据来自所有颜色值,并组合成灰度。这样做是为了将所有通道一起分布,并将图像的整体亮度调整为遵循所需的曲线。

  magick zelda.png -colorspace gray \
         -depth 8 -format "%c" histogram:info:- |\
    tr -cs '0-9\012' ' ' |\
      awk '# collect the histogram data.
           { bin[$2] += $1; }
           END { for ( i=0; i<256; i++ ) {
                   print bin[i]+0;
                 }
               } ' > zelda_hist_data.txt

  # get the maximum count for any one histogram 'bin'
  max_count=`sort -n zelda_hist_data.txt | tail -n 1`

  # magick histogram into a profile graph of the data
  echo "P2 256 1 $max_count" | cat - zelda_hist_data.txt |\
    im_profile -s - zelda_hist_graph.gif
[IM Output] ==> [IM Output]
为了收集数据,我从直方图图像中提取“comment”元数据,该元数据专门由IM包含用于此目的。然后清理数据,只保留原始数字(使用名为“tr”的程序,简称为“translate”)。然后将这些原始数据提供给另一个名为“awk”的实用程序,用于收集每个bin的实际直方图计数。为了便于查看结果,我还将直方图计数处理成梯度图像(通过NetPBM,PGM文本灰度图像文件格式),并使用“im_profile”脚本将其显示为折线图。本质上,这只是生成直方图图像的不同方法,不过这次是直接从数字数据文件生成。现在我们已将直方图数据存储在文本文件中,我们还需要将我们希望重新分布数据匹配的函数的直方图。在这种情况下,它是一个均值为153(60% 灰色)和标准差为60的正态分布。这两个值均以直方图“bins”的256个范围表示。

  awk '# AWK to generate gaussian distribution graph
        BEGIN { mean = 153;   sigma = 60;
                fact = 1/(2*(sigma/256)^2);
                expo = exp(1);
                for ( i=0; i<256; i++ ) {
                  print int(65535*expo^(-(((i-mean)/256)^2)*fact));
                }
              }' /dev/null  > gaussian_hist_data.txt

   # magick gaussian data into a profile graph
   echo "P2 256 1 65535" | cat - gaussian_hist_data.txt |\
     im_profile -s -b - gaussian_hist_graph.gif
[IM Output]
上面的直方图很有趣,它们反映了图像的原始直方图分布和直方图的期望状态。但出于转换目的,这种形式的直方图虽然便于我们理解,但对于我们的目的而言并不是非常有用。实际上,我们真正需要的是累积直方图。这些直方图与普通直方图非常相似,不同之处在于直方图中的每个“bin”都是其“bin”加上之前所有“bin”的计数,从0开始。也就是说,每个“bin”都是所有较暗“bin”的“累积”或计数。这些实际上更容易直接从原始图像生成。因此,让我们重复这个过程,但计算并保存“累积”计数。

  magick zelda.png -colorspace gray \
         -depth 8 -format "%c" histogram:info:- |\
    tr -cs '0-9\012' ' ' |\
      awk '# Collect the cumulative histogram for an image
               { bin[$2] += $1; }
           END { for ( i=0; i<256; i++ ) {
                   cum += bin[i];
                   print cum;
                 }
               } ' > zelda_cumhist_data.txt

  total_count=`tail -n 1 zelda_cumhist_data.txt`
  echo "P2 256 1 $total_count" | cat - zelda_cumhist_data.txt |\
    im_profile -s - zelda_cumhist_graph.gif

  awk '# AWK to generate gaussian distribution cumulative graph
        BEGIN { mean = 153;   sigma = 60;
                fact = 1/(2*(sigma/256)^2);
                expo = exp(1);
                for ( i=0; i<256; i++ ) {
                  gas[i] = expo^(-(((i-mean)/256)^2)*fact);
                  total += gas[i]
                }
                for ( i=0; i<256; i++ ) {
                  cum += gas[i];
                  print int(65535*cum/total);
                }
              }' /dev/null  > gaussian_cumhist_data.txt

  total_count=`tail -n 1 gaussian_cumhist_data.txt`
  echo "P2 256 1 $total_count" | cat - gaussian_cumhist_data.txt |\
    im_profile -s -b - gaussian_cumhist_graph.gif
[IM Output]
图像累积
直方图
[IM Output]
高斯累积
直方图
现在我们需要做的是将图像的累积直方图转换成高斯累积直方图。为此,将输入图像中的每个灰度值用于查找其“归一化”累积值。然后将其映射到高斯分布中的相同累积值,然后找到其对应的灰度值。此图应该使映射过程更清晰...
[diagram]
以下命令对所有可能的8位颜色值执行查找,以生成颜色查找表(CLUT)。然后可以使用此特殊图像将原始图像中的颜色值映射到重新分布图像直方图所需的新的颜色值。

  # Generate a CLUT to Redistribute the Histogram
  paste  zelda_cumhist_data.txt   gaussian_cumhist_data.txt |\
    awk '# AWK to generate gaussian distribution graph
              { bin[NR] = $1;   gas[NR] = $2;  }
          END { k=0;  # number of pixels less than this value
                print "P2 256 1 65535";
                for ( j=0; j<256; j++ ) {
                  while ( k<255 &&
                            gas[k]/gas[255] <= bin[j]/bin[255] ) {
                    k++;
                  }
                  print 65535*k/255;
                }
              }' |\
      magick pgm:- gaussian_clut.png

  magick zelda.png   gaussian_clut.png -clut   zelda_redist.png
[IM Output] ==> [IM Output]
[IM Output] ==> [IM Output]
如您所见,将图像的直方图转换为尝试遵循特定分布函数(例如高斯钟形曲线)的过程非常复杂,并且高度依赖于数值计算。以下是所有这些步骤在一个相当长且复杂的命令中...

  magick zelda.png -colorspace gray \
         -depth 8 -format "%c" histogram:info:- |\
    tr -cs '0-9\012' ' ' |\
      awk '# AWK to generate gaussian distribution graph
            { # just read in image histogram into a 'bin' table
                  bin[$2] += $1;
                }
            END { # Generate Gaussian Histogram
                  mean = 153;   sigma = 60;
                  fact = 1/(2*(sigma/256)^2);
                  expo = exp(1);
                  for ( i=0; i<256; i++ ) {
                    gas[i] = expo^(-(((i-mean)/256)^2)*fact);
                  }
                  # Convert normal histograms to cumulative histograms
                  for ( i=0; i<256; i++ ) {
                    gas[i] += gas[i-1];
                    bin[i] += bin[i-1];
                  }
                 # Generate Redistributed Histogram
                 k=0;  # number of pixels less than this value
                 print "P2 256 1 65535";
                 for ( j=0; j<256; j++ ) {
                   while ( k<255 &&
                            gas[k]/gas[255] <= bin[j]/bin[255] ) {
                     k++;
                   }
                   print 65535*k/255;
                 }
                }' |\
        magick zelda.png   pgm:-  -clut   zelda_gaussian_redist.png
[IM Output] ==> [IM Output]
关于上述技术的最后几句话。
  • Anthony Thyssen建议并贡献了使用“awk”进行计算以加快Fred Weinhaus的“redist”脚本的速度。
  • 为了将上述重新分布技术应用于生成“均匀”或“均衡”分布,函数直方图只是一个常数。这反过来会导致一个集成分布,该分布仅仅是公式y = x,或者只是一个对角线。应用相同的转换技术会导致一个CLUT图像,该图像与输入图像的累积直方图相同。换句话说,为了均衡直方图,您可以简单地将图像的累积直方图转换成CLUT并将其直接应用于图像。
  • 大多数图像处理软件包,包括目前的ImageMagick,将转换公式直接应用于图像本身的值,而不是生成中间CLUT。但是,由于直方图以及累积直方图的大小有限(通常为256个“bins”),这会导致严重的错误,因为图像颜色值可能会在处理过程中被舍入。然而,对于ImageMagick,我们生成了一个中间CLUT(包含相同的舍入错误),然后使用值的线性插值将原始未舍入图像值通过准备好的CLUT进行转换。由于这种插值,新图像的颜色值更加准确,因为它们在处理过程中没有被舍入或“bin化”。
以上内容有望最终集成到ImageMagick中。在此之前,Fred Weinhaus的“redist”脚本可用于执行此任务。您可能还对Fred的“retinex”脚本感兴趣,该脚本尝试对图像进行类似的自动增强,但在图像的局部区域进行,而不是像本技术那样在全局范围内进行。

DIY 色阶调整

数学线性直方图调整

上面显示的各种基本形式的色阶调整线性调整图像的颜色。这些更改也可以用数学方法应用。例如,通过将图像乘以特定颜色,我们将所有纯白色区域设置为该颜色。因此,让我们读取我们的图像,创建一个包含我们想要颜色的图像,然后使用IM自由格式的“-fx”或DIY 操作符将原始图像乘以此颜色。

  magick test.png  -size 1x1 xc:Yellow \
          -fx 'u*v.p{0,0}'    fx_linear_white.png
[IM Output] ==> [IM Output]
让“-fx”从第二个“v”图像中读取颜色,这使得更改颜色变得容易,而无需将颜色转换成RGB值以用于数学计算。如果您使用的是像“Gimp”和“Photoshop”这样花哨的图形图像处理软件包,上面的操作将通过调整图像的直方图曲线来应用于图像。 [IM 输出] 例如,在右侧是“gnuplot”生成的图形(请参阅脚本“im_histogram”),该图形显示了对三个RGB通道中的一个通道进行的操作的数学公式。原始颜色(绿线)线性重新映射到较暗的颜色(红线)。线性着色黑色也很简单。例如,要将“黑色”线性映射到类似金色的颜色“rgb(204,153,51)”(同时将“白色”保留为“白色”),将需要以下数学公式...
          result = 1-(1-color)*(1-intensity)
此公式否定颜色,将图像乘以否定后的所需颜色,然后再次否定图像。结果是给灰度级的黑色端着色,保持白色不变。

  magick test.png  -size 1x1 xc:'rgb(204,153,51)'  \
          -fx '1-(1-v.p{0,0})*(1-u)'   fx_linear_black.png
[IM Output] ==> [IM Output] ==> [IM Output]
为了参考,上面也显示了重新映射公式的“gnuplot”直方图图形。使用稍微复杂的公式,您可以线性替换灰度级的“黑色”和“白色”两端特定的颜色。

  magick test.png  -size 1x2  gradient:gold-firebrick \
          -fx 'v.p{0,0}*u+v.p{0,1}*(1-u)'   fx_linear_color.png
[IM Output] ==> [IM Output] ==> [IM Output]
上面的“-size 1x2 gradient:color1-color2”仅用于为“-fx”公式生成一个双色像素图像以供参考。第一个颜色替换白色,第二个颜色替换黑色,而所有其他颜色都在白色和黑色之间进行插值。与灰度级运算符一样,每个RGB通道都被视为独立的灰度级通道,尽管每个通道的线性插值不同。顺便说一下,这完全等同于按颜色进行色阶调整运算符“+level-colors
然而,与“+level-colors”不同,要使用的颜色当然可以来自任何图像源,而不仅仅是作为参数提供的颜色名称。但是,即使直接使用颜色名称也是可能的。

  magick test.png   -fx "yellow*u+green*(1-u)"  fx_linear.png
[IM Output]

数学非线性直方图调整

虽然线性颜色调整很重要,并且有更快的可行方法,但许多情况下线性“色阶”调整并不符合要求,而这正是“-fxDIY 操作符变得更有用的地方。嗯,线性调整的另一种公式是“-fx 'v.p{0,1}+(v.p{0,0}-v.p{0,1})*u'”,它具有将“u”替换为单个随机函数“f(u)”以产生非线性颜色变化的优点。这允许您做更多有趣的事情。例如,在最后一个示例中,如果要将所有颜色推向“黑色”侧,从而使图像成为更“砖红”的颜色,会怎么样。

  magick test.png -size 1x2  gradient:gold-firebrick \
          -fx 'v.p{0,1}+(v.p{0,0}-v.p{0,1})*u^4'  fx_non-linear.png
[IM Output] ==> [IM Output] ==> [IM Output]
在更实际的例子中,Adelmo Gomes需要为一个自动化的天气地图重新着色脚本进行颜色调整,该脚本正在开发中。在这种情况下,他希望将图像的纯黑色部分着色为.25蓝色,但保持灰度级的其他部分不变,尤其是图像的白色和中间色调灰色。只有蓝色需要这种调整,他目前正在图像编辑器中手动进行调整。例如,您可以使用像“u^2”这样的二次公式将直方图的黑色端着色为“.25”蓝色。只有蓝色通道需要修改,因此该值直接插入公式中。

  magick test.png  -channel B  -fx '.25+(1-.25)*u^2'  fx_quadratic.png
[IM Output] ==> [IM Output] ==> [IM Output]
但是,虽然这产生了合理的结果,但它确实使中间色调灰色略微变暗,产生了病态的淡黄色。为了避免这种情况,可以使用“指数”函数,以更好地控制着色过程。

  magick test.png  -channel B  -fx '.3*exp(-u*4.9)+u'  fx_expotential.png
[IM Output] ==> [IM Output] ==> [IM Output]
同样,该图显示了如何修改蓝色通道以使黑色具有独特的深蓝色色调。第二个值(“4.9”)是衰减回线性“+u”图的值。该值越小,衰减越慢,调整越接近线性。该值越大,衰减越剧烈。可能需要根据不同的颜色值调整该值,因此这不是一个适合一般黑色着色的通用公式,但非常适合为天气地图着色。总的来说,如果您能够用数学公式表达您想要的颜色调整,那么您可以使用“-fx”操作符来实现您想要的结果。

'曲线' 调整

[图] 通常,在图形照片编辑器中,您会看到一个像我左侧显示的直方图“曲线”图表。然后,用户可以通过移动四个(或更多)控制点来编辑“曲线”,直方图调整函数将遵循这些点。控制点通常指定第一个灰度级在调整后应变为第二个灰度级。因此,像0.0,0.2这样的点基本上意味着0% 灰色(黑色)在调整后应为20% 灰色级。现在IM不允许您直接指定“控制点”来生成“曲线”调整,它需要的是该“曲线”生成的数学公式。幸运的是,有程序可以从控制点生成该曲线公式,包括“gnuplot”,“fudgit”,“mathematica”和“matlab”,以及更多数学软件包。以下是使用“gnuplot”从四个控制点生成公式的一种方法,该方法是大多数Linux发行版以及Windows上可以安装的标准附加软件包。

  ( echo "0.0 0.2";  echo "1.0 0.9"; \
    echo "0.2 0.8";  echo "0.7 0.5"; )   > fx_control.txt

  ( echo 'f(x) = a*x**3 + b*x**2 + c*x + d'; \
    echo 'fit f(x) "fx_control.txt" via a, b, c, d'; \
    echo 'print a,"*u^3 + ",b,"*u^2 + ",c,"*u + ",d'; \
  ) | gnuplot 2>&1 | tail -1             > fx_funct.txt
[Data]
控制点
==> [Gnuplot]
[Gnuplot]
Gnuplot 拟合的 FX 函数
请注意,用于曲线拟合的参数数量(上面的“a”到“d”)必须等于您提供的控制点数量。因此,如果您想要五个控制点,则需要在函数中包含另一个“e”项。

如果您的直方图曲线经过固定的控制点0,01,1,则您实际上只需要两个参数,因为“d”将等于“0”,而“c”将等于“1-a-b”。

有关上面内容的更详细的使用指南,特别是针对Windows用户的,但对Linux用户也适用,已发布在StackOverflow:使用 Gnuplot 在 Windows 上进行 IM 曲线上。如您从上面额外的“gnuplot”生成的图像中所见,生成的函数完美地拟合了控制点。此外,由于它生成了一个“-fx”样式公式,因此可以直接用作IM参数。
例如...

  magick test.png    -fx "`cat fx_funct.txt`"     fx_funct_curve.png
[IM Output]
为了方便用户将控制点应用到直方图调整功能中,我创建了一个名为“im_fx_curves”的 shell 脚本,用于调用“gnuplot”并输出给定控制点的更美观的曲线方程。Gabe Schaffer 还提供了一个 Perl 版本(使用下载的“Math::Polynomial”库模块),名为“im_fx_curves.pl”,用于执行相同的操作。任一脚本都可以使用。例如,这是一个具有 5 个控制点的不同曲线...

    im_fx_curves  0,0.2  0.3,0.7  0.6,0.5  0.8,0.8  1,0.6  > fx_curve.txt
[Gnuplot] ==>
[Gnuplot]
然而,FX 函数非常慢。但从 IM 6.4.8-9 版本开始,您现在可以直接将拟合多项式表达式的系数传递到 多项式函数方法 中。您可以使用“im_fx_curves”和特殊的“-c”选项生成以逗号分隔的系数列表...

    im_fx_curves -c  0,0.2  0.3,0.7  0.6,0.5  0.8,0.8  1,0.6  > coefficients.txt
[Gnuplot] ==>
[Gnuplot]
例如,让我们将这些曲线应用到我们的测试图像...

  magick test.png  -function Polynomial `cat coefficients.txt`  test_curves.png
[IM Output]
此方法更实际的示例在高级 “水”效果 示例中详细介绍。IM 论坛讨论中介绍了一种生成“曲线”的替代方法 任意色调再现曲线

着色图像

均匀着色图像

通常,着色图像可以通过将图像与颜色混合一定比例来实现。可以使用 评估操作符混合图像 技术来完成,但这些方法并不简单。幸运的是,我们可以使用“-colorize”图像操作符,通过更简单的方法将均匀颜色混合到图像中。此操作符将当前“-fill”颜色混合到当前图像序列中的所有图像中。原始图像的 Alpha 通道保留,仅颜色通道被修改。例如,为了使图像变亮(灰度或其他),我们使用“-colorize”将一定比例的白色混合到图像中,使其变亮,但不会完全饱和。

  magick test.png  -fill white -colorize 50%  colorize_lighten.png
[IM Output] ==> [IM Output]
类似地,我们可以使用“black”填充颜色使图像变暗。

  magick test.png  -fill black -colorize 50%  colorize_darken.png
[IM Output] ==> [IM Output]
要使图像的两端向中间色调变灰,可以使用特定的灰色填充颜色。颜色“gray50”是 RGB 颜色光谱的正中间颜色。

  magick test.png  -fill gray50 -colorize 40%  colorize_grayer.png
[IM Output] ==> [IM Output]
这也经常用作“去对比度”的方法,例如 反向色阶调整操作符 提供的功能,但控制程度较低。 “-colorize”操作符还允许您分别为三个颜色通道指定溶解百分比。这对于以特殊方式线性地使图像变暗(或变亮)非常有用。
在 IM v6.7.9 之前,“-colorize”操作符不会修改 Alpha 通道。从该版本开始,如上所述,它现在统一着色所有像素,包括完全透明的像素。
-colorize”操作符的一个常见用途是简单地替换现有图像中的所有颜色(着色“100%”),但保留图像的透明度(Alpha)形状,以便生成彩色蒙版。但是,从 IM v6.7.9 版本开始,您需要通过禁用 Alpha 通道来保护 Alpha 通道免受此操作符的影响,然后重新启用 Alpha 通道。(有关更多详细信息,请参见 Alpha 开启)。例如...

  magick test.png -alpha off \
          -fill blue -colorize 100% \
          -alpha on  colorize_shape.png
[IM Output]
如果没有这种保护,colorize 会将画布完全空白为给定颜色...

  magick test.png -fill blue -colorize 100% colorize_blank.png
[IM Output]
但是,如果有可能使用低于 IM v6.7.9 版本的 IM,建议您在上述内容中包含“-alpha opaque”或“-alpha off”操作,以确保生成的图像完全符合您预期的空白图像。请注意,您可以使用 按颜色进行色阶调整 操作符用单个颜色而不是颜色范围更快地空白画布。另请参见 空白画布

中间色调颜色色调

虽然 着色操作符 应用“-fill”颜色以线性着色图像中的所有颜色,但“-tint”操作符应用“-fill”颜色,以仅着色图像的中间色调颜色。该操作符是一个灰度操作符,颜色由给定的百分比(0 到 200)进行调节或增强。为了限制其效果,它还使用数学公式进行调整,因此它不会影响黑色和白色,但会对每个颜色通道的中间色调颜色产生最大影响。“-tint 100”基本上会使完美的灰色颜色着色,使其成为填充颜色强度的二分之一。较低的值将使它着色为更暗的颜色,而较高的值将使它着色为更接近该颜色的完美匹配。

  magick test.png  -fill red  -tint 40 tint_red.png
[IM Output] ==> [IM Output]
测试图像中的绿色不是真正的 RGB 绿色,而是缩放矢量图形“green”,其亮度仅为真正的绿色颜色的一半。因此,它也是一种中间色调颜色,因此受到“-tint”操作符的影响,变得更暗,与测试图像中的红色和蓝色色点不同。您还可以通过使用以逗号分隔的百分比列表来着色各个颜色分量。例如“-tint 30,40,20,10”。但这可能很难使用,可能需要一些试验才能正确使用。最好指定您想要的完美的 50% 灰色的颜色。
[IM 输出]-tint”操作符通过某种方式获取给定的颜色和百分比,然后根据以下公式调整图像中的各个颜色,根据“-fill”颜色的强度。(见右图)

f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
一个二次函数,其结果用作图像中现有颜色的向量。如您所见,它为纯中间色调灰色提供了完整的颜色替换,对白色或黑色没有调整。

或者,您可以使用更底层的操作符来自己动手操作此类操作,请参见 FX 操作符 以及 评估和函数操作符

着色操作符非常适合调整“-shade”输出的结果(参见 阴影叠加突出显示图像),例如 3D 子弹图像 中的示例。您还可以使用“-tint”来使图像的中间色调颜色变亮或变暗。这有点类似于图像的“伽马调整”,但并不完全相同。例如,使用大于 100 的着色值和“white”颜色会使中间色调变亮。

  magick test.png  -fill white  -tint 130 tint_lighter.png
[IM Output] ==> [IM Output]
而小于 100 的值会使颜色变暗。

  magick test.png  -fill white  -tint 70 tint_darker.png
[IM Output] ==> [IM Output]
目前,纯中间色调灰色颜色不会映射到“-fill”颜色。

百分比参数不是“混合百分比”,而更像是“亮度百分比”。例如,它对“黑色”填充颜色根本不起作用。

我不知道它为什么是这样设计的,也不知道它背后的历史。但是,它确实使着色灰度图像时的最终颜色的精确控制变得非常尴尬。

使用下面的 叠加合成着色 将提供更精确(尽管非常线性,而不是抛物线)的中间色调灰色的颜色着色。

棕褐色调色

一种特殊的摄影重新着色技术,“-sepia-tone”基本上是将图像转换为灰度,并将所有中间色调着色为特殊的棕色。

  magick rose:  -sepia-tone 65%     sepia-tone.jpg
[IM Output]
给定的参数是将成为最接近棕褐色的灰度“中间点”,这类似于颜色“Goldenrod”。最常见的用途是生成 双色调效果,以生成“旧式”照片(参见维基百科上的 棕褐色调)。例如,在这里我 着色 增强对比度的灰度玫瑰图像,使用各种颜色,以实现类似的棕褐色效果。您应该使用哪种颜色取决于您要寻找的确切效果。

  magick rose: -colorspace gray -sigmoidal-contrast 10,40%  rose_grey.jpg
  for color in      goldenrod  gold  khaki  wheat
  do
    magick rose_grey.jpg  -fill $color   -tint 100    sepia_$color.jpg
  done
[IM Output] ==> [IM Output] [IM Output] [IM Output] [IM Output]
我个人发现,将棕褐色图像与原始图像混合或混合,以降低其效果,也可以产生更好的“褪色”效果。

  magick rose: \( +clone -sepia-tone 60% \) -evaluate-sequence mean  sepia-tone_blended.jpg
[IM Output]
另请参见 Hald 颜色查找表,了解一种方法,您可以使用该方法保存更复杂的颜色更改变化,例如上面的最后一个示例。

双色调效果

“双色调”是一种印刷方法,您将图像的灰度(黑色油墨)与其他颜色混合,以在预算有限或印刷设备有限的情况下获得更好的效果。例如,今天您看到的所有旧照片都呈现出棕褐色调的原因是,棕褐色油墨保存了下来,没有随着时间的推移而退化或褪色。其他“黑白”图像格式褪色成无用。请参见上面的 棕褐色调操作符。另一种称为“蓝晒”的双色调技术(更常被称为“蓝图”)已成为制作原始黑白建筑图纸的大尺寸副本的一种广泛使用的方法。请记住,这种技术是在激光和复印(以及施乐)发明之前很久就使用了。有关更多信息,请参见维基百科条目 双色调,以及 假双色调与真双色调。上面的 着色操作符 确实生成了双色调效果的合理仿制品,就像它在上面为棕褐色效果所做的那样。

  magick rose: -colorspace gray -sigmoidal-contrast 10,40%  rose_grey.jpg
  for color in      blue  darkcyan  goldenrod  firebrick
  do
    magick rose_grey.jpg   -fill $color   -tint 100    duotone_$color.jpg
  done
[IM Output] ==> [IM Output] [IM Output] [IM Output] [IM Output]
请注意,我通常选择“双色调”颜色的更暗版本,但您也可以使用 着色操作符 的参数来调整它。亮度和对比度也可以使用 S 形对比度操作符 的参数进行调整。另一种更精确的从三种颜色(黑点、中间点和白点颜色)生成双色调的方法是使用 颜色查找表(见下文)。这是一个快速示例,我使用“Black”、“Chocolate”和“LemonChiffon”颜色来创建非常不寻常的双色调。是的,黑点颜色通常保留为黑色,这就是为什么它通常被称为色调的原因。

  magick -size 1x1 xc:Black xc:Chocolate xc:LemonChiffon \
                                   +append     duotone_clut.gif
  magick -size 20x256 gradient: -rotate 90   duotone_clut.gif \
          -interpolate Bicubic -clut       duotone_gradient.gif
  magick rose_grey.jpg   duotone_clut.gif \
          -interpolate Bicubic -clut       rose_duotone.jpg
[IM Output] ==> [IM Output]
[IM Output] ==> [IM Output] ==> [IM Output]
上述方法的优点是对中间点颜色进行精确控制(与 着色 不精确相比)。您也可以直接使用您喜欢的任何三种颜色,就像上面的示例一样,或者使用颜色的扩展渐变来更精细地控制三种(或更多)控制点之间的颜色。该技术还为您提供了一种非常紧凑的方法来存储特定的双色调效果,以便重复使用和将来使用。另请参见 Hald 颜色查找表,了解更复杂的颜色更改保存方法,这些方法超出了灰度图像着色。

颜色色调,DIY

-tint” 最大的问题之一是它是一个灰度(或矢量)运算符。也就是说,它完全独立地处理每个红色、绿色、蓝色通道。反过来,这意味着像“blue”或“yellow”这样的主色和副色不受“-tint”的影响,即使所有灰度都受到影响。但是,借助各种通道数学变换,例如 FX 运算符 和更快的 Evaluate 和 Function 运算符,您可以生成自己的颜色叠加来修改图像。也就是说,以类似于 Colorize 运算符 的方式对图像进行 色调 处理。例如,在这里我将图像的灰度亮度级别转换为所需特定颜色的半透明叠加。

  magick test.png  \( +clone -colorspace gray \
               -function polynomial -4,4,0 -background Gold -alpha shape \) \
          -composite   tint_diy_compose.png
[IM Output]
警告,这不会正确保留图像透明度,但对于完全不透明的图像效果很好。请注意,与色调不同,可以使用任何颜色,包括“black”,因为颜色不视为矢量加法,而是 Alpha 合成。结果与您对普通色调获得的结果不太一样。

颜色色调叠加

特殊的 Alpha 合成 方法“Overlay”和“Hardlight”实际上是针对颜色(和图案)色调而设计的。这些合成方法还会替换中色调的灰色,只保留图像中的黑色和白色高光。
例如,这里我快速生成一个彩色叠加图像,并将其合成到原始图像以对其进行色调处理。

  magick test.png \( +clone -alpha off -fill gold -colorize 100% \) \
          -compose overlay -composite  tint_overlay.png
[IM Output]
如您所见,Alpha 合成不会保留原始图像的任何透明度,需要使用第二个 Alpha 合成操作来解决此问题。

  magick test.png \
          \( +clone -alpha off -fill gold -colorize 100% \
             +clone +swap -compose overlay -composite \) \
          -compose SrcIn -composite  tint_overlay_fixed.png
[IM Output]
使用“Overlay”是比上面使用的二次函数更线性的色调形式,并且与“-tint”一样,它分别应用于图像的每个通道,因此主色和副色也保持不变。此外,这种 Alpha 合成方法没有提供调整控制,因此,如果您想控制色调级别,则需要在应用色调之前调整叠加图像的透明度。当然,与我迄今为止展示的其他色调方法不同,您不仅限于对简单颜色进行色调处理,还可以使用图像或平铺图案来应用色调。

  magick test.png \
          \( -size 150x100 tile:tile_disks.jpg \
             +clone +swap -compose overlay -composite \) \
          -compose SrcIn -composite  tint_overlay_pattern.png
[IM Output]
但是,这超出了基本颜色处理的范围,因此我将在此处停止图像色调处理。
Alpha 合成方法“HardLight”将产生与“Overlay”相同的结果,但源图像和目标图像已交换。

这可以用来代替最后几个示例中的“+swap”。

全局颜色修改器

调节亮度、饱和度和色调

-modulate”运算符很特殊,因为它在特殊的 HSL(色相-饱和度-亮度)颜色空间 中修改图像。它将图像中的每个颜色像素转换为该颜色空间,对其进行修改,然后将其转换回原始颜色空间。它以百分比的形式接受三个值(虽然后面的值是可选的),因此 100 不会对图像进行任何更改。例如。

  magick rose: -modulate 100,100,100  mod_noop.gif
[IM Output]
第一个值,亮度是图像整体亮度的乘数。

  magick rose:  -modulate 0     mod_bright_0.gif
  magick rose:  -modulate 50    mod_bright_50.gif
  magick rose:  -modulate 80    mod_bright_80.gif
  magick rose:  -modulate 100   mod_bright_100.gif
  magick rose:  -modulate 150   mod_bright_150.gif
  magick rose:  -modulate 200   mod_bright_200.gif
[IM Output] [IM Output] [IM Output] [IM Output] [IM Output] [IM Output]
请注意,虽然“0”的亮度参数将生成纯黑色图像,但您无法使用此运算符单独生成纯白色图像。第二个值饱和度也是一个乘数,它会调整图像中存在的颜色总量。

  magick rose:  -modulate 100,0     mod_sat_0.gif
  magick rose:  -modulate 100,20    mod_sat_20.gif
  magick rose:  -modulate 100,70    mod_sat_70.gif
  magick rose:  -modulate 100,100   mod_sat_100.gif
  magick rose:  -modulate 100,150   mod_sat_150.gif
  magick rose:  -modulate 100,200   mod_sat_200.gif
[IM Output] [IM Output] [IM Output] [IM Output] [IM Output] [IM Output]
0”的饱和度将生成灰度图像,如上面 将颜色转换为灰度 中所示。但是,灰色会像 HSL 颜色空间定义的那样,均匀地混合所有三个颜色通道,因此不会生成真实的“亮度”灰度。本质上,较小的值会产生更多“柔和”的颜色,而大于“100”的值会产生更多卡通化的彩色图像。请注意,由于亮度饱和度是百分比乘数,因此您需要乘以一个非常大的数字才能将几乎所有图像颜色值更改为接近最大值。也就是说,您需要使用接近一百万的亮度系数才能使除纯黑、白色之外的所有颜色都变成黑色、白色。

色调调制

最后一个值,色调,实际上更有用。它以循环方式旋转图像的颜色。为了实现这一点,给定的色调值会产生“模加法”,而不是乘法。但是要注意,色调是使用百分比旋转的,而不是使用角度旋转的。这可能看起来很奇怪,但“-modulate”一直都是这样。角度和调制参数之间的转换公式是。
色调角 = ( 调制参数 - 100 ) * 180/100
调制参数 = ( 色调角 * 100/180 ) + 100
这意味着“100”(对于所有三个参数)不会产生任何变化。虽然“0”或“200”的值实际上会否定图像中的颜色(但不会否定亮度)。例如。

  magick rose:  -modulate 100,100,0      mod_hue_0.gif
  magick rose:  -modulate 100,100,33.3   mod_hue_33.gif
  magick rose:  -modulate 100,100,66.6   mod_hue_66.gif
  magick rose:  -modulate 100,100,100    mod_hue_100.gif
  magick rose:  -modulate 100,100,133.3  mod_hue_133.gif
  magick rose:  -modulate 100,100,166.6  mod_hue_166.gif
  magick rose:  -modulate 100,100,200    mod_hue_200.gif
[IM Output]
0
(红色 <-> 青色)
[IM Output]
33.3
(红色 -> 蓝色)
[IM Output]
66.6
[IM Output]
100%
无操作
[IM Output]
133.3
[IM Output]
166.6
(红色 -> 绿色)
[IM Output]
200
(与 0 相同)
如您所见,“33.3”的值会使所有颜色大约 60 度的负向旋转,或逆时针旋转,有效地将红色映射到蓝色,蓝色映射到绿色,绿色映射到红色。使用“0”或“200”的值会产生 180 度的颜色完全否定,而不会否定图像的亮度。请注意,色调是循环的,因此使用“300”的值将产生 360 度的颜色旋转,并且不会对图像产生任何变化。有关使用“色调调制”来修改图像中颜色的示例,请参见 色度键蒙版地图中的图钉
也可以使用高级 颜色空间 技术来应用这些类型的操作以及更多操作,例如在 重新着色矩阵运算符(下面)中使用,但对于图像的基本“调制”,此运算符极大地简化了操作。对于主要颜色交换,无论是 重新着色矩阵运算符,还是通道交换(参见 分离/组合运算符),可能都是更准确的技术。虽然它没有那么通用。另请参见 Hald 颜色查找表,了解一种可以保存颜色变化(特别是色调变化)以供以后重复使用的方法。

调节 DIY

如果您真的想“自己动手”。您基本上将图像转换到适当的颜色空间,修改值,然后转换回来。请记住,在 HSL 颜色空间 中,绿色通道保存饱和度值,蓝色通道保存亮度值。例如,以下代码等效于“-modulate 80,120”(稍微变暗,增加颜色饱和度),使用默认的 HSL 颜色空间。

  magick rose: -colorspace HSL \
          -channel B -evaluate multiply 0.80 \
          -channel G -evaluate multiply 1.20 \
          +channel -colorspace sRGB   modulate_channel.png
[IM Output] ==> [IM Output]
当然,如果您使用此方法修改色调(红色通道),则需要确保最终值“环绕”(模运算),而不是简单地在最大值或最小值处剪切值(这两个值都是“红色”色调)。因此,对于色调修改,直接使用 Modulate 运算符 可能更容易。

在其他颜色空间中调节

-modulate”最大的问题是处理包含大量“接近白色”颜色的图像时。由于它在 HSL 颜色空间中完成工作,因此随着亮度降低,偏白色的颜色会变得更加“饱和”。您可以在上面玫瑰图像的白叶中看到这一点,它在 50% 变暗时显示出大量的颜色伪像。这在处理 JPEG 图像格式时尤其是一个问题,因为它由于其有损压缩算法,往往会生成偏白色颜色(实际上所有颜色通常都略微偏色)。例如。

  magick wedding_party_sm.jpg  -modulate 85  modulate_off-white.png
[IM Output] ==> [IM Output]
这里的问题是在 HSL 中,所有偏白色的颜色都集中在使用颜色空间的小“白点”区域(双锥体)内。当亮度降低时,偏白色的颜色会随着颜色锥体的扩展而扩展,导致偏白色颜色生成更多彩色的(饱和的)偏白色颜色集。也就是说,颜色的小变化会被夸大。解决此问题的方法是在 HSB 颜色空间而不是 HSL 颜色空间中“-modulate”。
HSB 中的“B”表示亮度,但通常也称为 HSV,其中“V”表示值。它们是同一个颜色空间,但“V”是一个令人困惑的术语,因为值通常表示“存储的数字”。

还有一个 HSI 颜色空间(使用“I”表示亮度),但它并不常见,而且由于添加了 HCL(其中“L”表示亮度)循环颜色空间(见下文),因此不需要它。

在 HSB 颜色空间中,“白色”不是一个点,而是一个大的“圆盘”,因此偏白色彼此之间并不“靠近”。因此,当您降低亮度时,偏白色会均匀收缩,减少任何细微的颜色变化,而不是扩大它们。因此,白色只是变成了灰色,而不是更鲜艳。要在 HSB 颜色空间 中调制图像,您可以使用 DIY 技术(见上文)在该颜色空间中进行操作。或者,使用 IM v6.5.3-7 及更高版本,您可以通过 定义操作控制modulate:colorspace”来使用其中一个“色调”颜色空间。

  magick wedding_party_sm.jpg \
          -define modulate:colorspace=HSB -modulate 85 \
          modulate_HSB.png
[IM Output]
其他“色调”颜色空间是 HWB 和 HCL(见下一节)。当然,如果您将图像调整到这么小的尺寸,更好的解决方案是不要将图像保存为 JPEG,因为这是导致偏白色值的原因。更好的是,在完成操作之前不要保存图像,这样可以将所有颜色值保持在内存中最佳的 质量 设置。HSB 颜色空间未作为默认值用于调制的原因是,如果您在这个颜色空间中使图像变亮,颜色会变得更加饱和,更加大胆,而不是图像变得更加明亮,更加白色。例如,这里比较了在默认 HSL 中和指定 HSB 颜色空间中对“玫瑰”图像进行 150% 亮度处理。

  magick rose:         -modulate 150        mod_bright_HSL.gif
  magick rose: -define modulate:colorspace=HSB \
                         -modulate 150        mod_bright_HSB.gif
[IM Output]
HSL
[IM Output]
HSB
在 IM v6.4.0-10 之前,“-modulate”运算符实际上使用的是 HSB 颜色空间,而不是 HSL 颜色空间。更改此设置是因为用户报告了上述情况的错误。

关键是,对于某些图像,如果您使用 HSL,您就完蛋了,而对于其他图像,如果您使用 HSB 颜色空间,您就完蛋了。这仅仅取决于您尝试执行的操作!

在 LCHab 和其他颜色空间中调制

色调调制(在 HSL 或 HSB 色彩空间中)实际上被认为相当粗略。这些色彩空间没有考虑到更真实的颜色强度。因此,在“蓝色”和“黄色”色调之间旋转也会产生非常大的亮度变化。参见 维基百科:HSL 色彩空间的缺点。一种替代方法是执行亮度保持旋转,如 Grafica Obscura 的“矩阵运算”论文中所述。这很复杂,因为颜色修改是在操作过程中完成的,作为单个计算的矩阵操作,该操作根据所需的旋转量而不同。从 IM v6.8.4-7 开始,调制运算符 也可以处理特殊的色彩空间“LCHab”和“LCHuv”,它们是各自“Luv”和“Lab”色彩空间的圆柱形(色调-彩度)形式。参见 维基百科,圆柱形 LUV 或 LCHuv 色彩空间HCL 色彩空间 以了解更多信息。
LCHab”和“LCHuv”色彩空间的等效通道与“HCL”和“HCB”色彩空间的通道相反。也就是说,“灰度”强度等效位于图像的第一个(“红色”)通道中,而色调位于图像的第三个(“蓝色”)通道中。
例如,我们对红色玫瑰进行了一些色调旋转,使用“LCHab”色彩空间。将这些与上面“HSL”色彩空间的先前集合进行比较。

  for i in   0 25 50 75 100 125 150 175;  do
    magick rose: -define modulate:colorspace=LCHab \
                            -modulate 100,100,$i     mod_lch_$i.gif
  done
[IM Output]
0% & 200%
[IM Output]
25%
(红色->蓝色)
[IM Output]
50%
[IM Output]
75%
[IM Output]
100%
无操作
[IM Output]
125%
[IM Output]
150%
[IM Output]
175%
请注意,色调分布与更传统的色调色彩空间不同。但更重要的是,原始图像的强度得以保留。因此,您永远不会从纯主色/二次色循环到另一个纯主色/二次色,因为这些颜色都没有相同的强度。然而,颜色在色调上的进展更加平滑,在主色和二次色处没有那么尖锐的“峰值”。以下是“LCHab”与普通“HSL”色彩空间(使用适当的旋转百分比)的简单色调旋转红色到蓝色的比较。
[IM Output]
原始
[IM Output]
LCHab
25%
[IM Output]
HSL/HSB
33.3%
请注意,蓝色绝不像黑色那么深,但它与原始图像的色调更匹配。有关 HCL 色彩空间色调的更多信息,请参阅 LCH 色轮 上的示例。
在 IM v6.8.4-7 之前,您将使用色彩空间“HCL”(IM v6.7.9-1 引入)。该色彩空间与“LCHuv”完全相同,但通道顺序相反(色调存储在图像的红色通道中,这只是该色彩空间的定义方式)。这意味着您还必须交换各种通道以使 调制运算符 正确工作。

LCHab”和“LCHuv”色彩空间以与“HSL”相同的方式对通道进行排序,以便允许调制正常工作,并直接对色彩空间进行操作,而无需重新排序通道。

请注意,对于非常暗的颜色,“LCHuv”可能会生成具有不连续性的颜色值。但是,对于真实图像来说,这种情况不应该发生,而只发生在直接在圆柱形空间中生成的图像中。

颜色矩阵运算符

-color-matrix”运算符将使用矩阵技术重新着色图像。也就是说,您需要提供一组值,这些值表示如何线性混合图像的各种颜色通道值以生成新的颜色值。典型的用法是向运算符提供 9 个值,这些值构成三个函数(行)或三个乘数(列)。因此,前三个数字是“红色”通道的颜色公式。下一个是“绿色”,依此类推,例如……

  magick rose: -color-matrix ' 1 0 0
                                0 1 0
                                0 0 1 '   matrix_noop.png
[IM Output]
相当于应用以下方程式……
red' = 1 * red + 0 * green + 0 * blue
green' = 0 * red + 1 * green + 0 * blue
blue' = 0 * red + 0 * green + 1 * blue
在这种特定情况下,对图像没有进行任何更改。该矩阵构成一个特殊的数组,称为“单位矩阵”。通过混合行,您可以使用它来交换各种通道。例如,在这里我交换红色和蓝色通道值。

  magick rose: -color-matrix ' 0 0 1
                                0 1 0
                                1 0 0 '  matrix_red_blue_swap.png
[IM Output]
或者只是将红色通道复制到另外两个通道中,以提取或分离“红色通道”(另请参见 分离通道图像)……

  magick rose: -color-matrix ' 1 0 0
                                1 0 0
                                1 0 0 '  matrix_red_channel.png
[IM Output]
或者使用 2/5/3 灰度比例将图像转换为灰度图像(参见 将颜色转换为灰度)……

  magick rose: -color-matrix ' .2 .5 .3
                                .2 .5 .3
                                .2 .5 .3 '  matrix_grayscale.png
[IM Output]

您可以使用更大的矩阵,最多包含 6 行和 6 列。它们对应于以下通道:“Red”、“Green”、“Blue”、“Black”(如果设置)、“Alpha”(如果设置)和一个常数。请注意通道:“Black”和“Alpha”;即使值本身可能不存在或未使用,如果矩阵大小为 6,则仍然必须提供它们。最后常数列只是公式的简单加法(如果为负则为减法)。如果给出第 6 行(如果给出),则会简单地忽略它,不使用。默认情况下,“矩阵”定义遵循与 用户定义的形态/卷积核 相同的结构,如果未指定大小几何体,则被视为“方形”核。核的偏移量目前未使用。给定的“值数组”随后被叠加在一个更大的“6x6 单位矩阵”(对角线为 1)上,然后应用于图像。这种内部处理意味着您实际上可以通过仅提供几行数字来简化矩阵值,而不是所有数字。这在您需要在颜色计算中包含“常数”,或者只想修改一个通道时特别有用。例如,反转(取反)图像。

  magick rose: -color-matrix '6x3: -1  0  0 0 0 1
                                     0 -1  0 0 0 1
                                     0  0 -1 0 0 1'  matrix_negate.png
[IM Output]
将所有红色通道值设置为最大值(使用“常数”)……

  magick rose: -color-matrix '6x1: 0,0,0,0,0,1'  matrix_red_max.png
[IM Output]
由于在单位矩阵上叠加,其他通道值都不会受到影响,尽管它们仍然在内部重新计算。
在 IM v6.6.1-0 之前,“-color-matrix”被称为“-recolor”。

颜色矩阵示例

棕褐色,或者至少是该操作的线性形式

  magick rose: -color-matrix ' 0.393 0.769 0.189
                                0.349 0.686 0.168
                                0.272 0.534 0.131  ' matrix_sepia.png
[IM Output]
鲜艳的颜色,使用一种称为 数字维维亚 的技术……

  magick rose: -color-matrix '  1.2 -0.1 -0.1
                                -0.1  1.2 -0.1
                                -0.1 -0.1  1.2 ' matrix_vivid.png
[IM Output]
此矩阵使每个颜色通道变亮,同时从其他通道中减去颜色,使 RGB 图像中的颜色更加鲜艳。这与使用 调制 将图像的颜色饱和度提高 20% 不完全相同,但与之类似。宝丽来颜色……

  magick rose: -color-matrix \
            '6x3:  1.438 -0.122 -0.016  0 0 -0.03
                  -0.062  1.378 -0.016  0 0  0.05
                  -0.062 -0.122 1.483   0 0 -0.02 ' matrix_polaroid.png
[IM Output]
未来:使用颜色矩阵进行色调旋转...
Grafica Obscura 网页所述。
有关使用颜色矩阵的更多信息,请参见……但是要注意,大多数这些实现使用矩阵的 对角线转置 形式,其中列构成方程式,而不是行。或者涉及较少的通道(较少的行/列)。

太阳化着色

要“-solarize”图像,基本上就是将最亮的色彩“烧成”黑色。色彩越亮,太阳化后的色彩就越深。这在化学胶片曝光过度时会发生在摄影中。

  magick rose:  -solarize 90%     solarize.jpg
[IM Output]
基本上,高于给定灰度级别的任何东西都会被取反。因此,如果您给出“0%”的参数,您基本上就拥有了一个简陋的 取反运算符。例如,以下是用“-fx”数学公式模拟的“-solarize”。

  magick rose:  -fx  '.9>u ? u : 1-u'     solarize_fx.jpg
[IM Output]
该运算符特别适合从图像中提取中色调灰度颜色。例如,这里我使用非常强的 S 形对比度 操作,在 70% 灰色处产生一种“模糊”阈值。然后我 太阳化 结果以生成一个模糊的尖峰,而不是一个模糊的阈值。最后进行一次级别调整将尖峰带到最大亮度以生成“灯丝”效果。

  magick -size 10x300 gradient: -rotate 90 \
                         -sigmoidal-contrast 50x70%   fuzzy_thres.png
  magick fuzzy_thres.png  -solarize 50%   fuzzy_spike.png
  magick fuzzy_spike.png  -level 0,50%    filament.png
[IM Output] ==> [IM Output] ==> [IM Output]
旁注: 以上显示梯度“轮廓”图的图像,是使用 IM 示例中的“im_profile”生成的,脚本 目录。请注意,任何白色都变成黑色,而中心尖峰周围的中色调灰色得以保留。尖峰的模糊度和位置由“-sigmoidal-contrast”运算符决定。我称它为“灯丝”,因为结果通常看起来非常像发光的电灯丝或闪电放电。参见 随机磁通,以了解此效果的另一个示例。从位图形状中提取中色调灰色在生成 边缘轮廓 以及 两个偏差梯度的相乘 的技术中也得到了很好的应用。该操作的另一种新颖用法是确定图像基本上是纯黑白色素描或图画(例如来自书籍),而不是阴影灰度或彩色图像,参见 确定图像是否为:纯黑白色或灰度

使用查找表重新着色图像

虽然您可以使用如上所示的各种直方图颜色调整来重新着色图像,但还有另一种重新着色图像的技术,只需从预先准备的颜色梯度或“颜色查找表”(颜色 LUT 或 CLUT)中“查找”修改后的值。颜色 LUT 有两种类型:简单的一维或“每通道”LUT 和 3D 颜色 LUT。通道 LUT 有三个独立的查找表:一个用于 R 通道,一个用于 G 通道,一个用于 B 通道。通道 LUT 中的每个条目都将输入通道值映射到输出通道值。输出图像的红色通道只受输入图像的原始红色值的影響。然而,3D 颜色 LUT 允许将整个颜色作为整个输入颜色的函数进行替换。也就是说,红色通道的输出值可以取决于输入的红色、绿色和蓝色值的任意一个或所有值。这有时被称为通道串扰。

颜色(通道)查找表

图像处理工具的常见需求是能够从预先准备的颜色表中替换整个颜色范围。这使您能够将一组颜色(通常为灰度)的图像转换为完全不同的颜色集,只需从特殊图像中查找其替换颜色即可。当然,您需要一个“查找表”图像来读取替换颜色。对于接下来的几个示例,我选择使用颜色垂直梯度作为 LUT,以便 IM 的“gradient:”生成器可以用来简化“颜色查找表”的生成。好了,理论到此为止。让我们尝试通过重新着色一个简单的 灰度等离子体 图像来试用一下,用深蓝色到淡白色的颜色梯度替换灰度。


magick -size 100x100 plasma:fractal -virtual-pixel edge -blur 0x5 \ -shade 140x45 -normalize \ -size 1x100 xc:black -size 9x100 gradient: \ +append gray_image.jpg magick -size 10x100 gradient:navy-snow gradient_ice-sea.png magick gray_image.jpg gradient_ice-sea.png -clut gray_recolored.jpg
[IM Output] ==> [IM Output] ==> [IM Output]
-clut”运算符接受两个图像。第一个是要替换颜色值的图像,第二个是一个梯度图像,它要么是一行,要么是一列。
-clut”运算符是在 IM v6.3.5-8 中添加的。
如果您的 IM 太旧,无法理解“-clut”运算符,或者您想做一些不寻常的事情,例如二维颜色查找表,那么您可以使用 通用 DIY 运算符,FX 来自己实现。例如,以下是一个速度较慢但与上述命令等效的命令。

  magick gray_image.jpg  gradient_ice-sea.png \
          -fx 'v.p{0,u*v.h}'  gray_recolored_fx.jpg
[IM Output]
问题是,即使对于像上面这样的简单过程,"-fx" 运算符也非常慢,并且必须专门针对行或列 LUT 设计。但是它确实有效。LUT 不必很大。例如,这里我们使用一个非常小的 LUT,并且颜色数量非常有限。

  magick -size 1x6 gradient:navy-snow  gradient_levels.png
  magick gray_image.jpg  gradient_levels.png  -clut  gray_levels.jpg
[IM Output] ==> [IM Output] ==> [IM Output]
我放大了网页上显示的渐变图像,否则它会太小而无法正常看到。LUT 实际上只有 6 个像素大小。但是,如果您查看结果,您会发现颜色查找运算符将这 6 种颜色平滑地转换为平滑的渐变。发生的事情是,IM 对 LUT 图像进行了 插值查找。也就是说,它不仅会选择找到的颜色,还会对所有附近的颜色进行加权平均,以更好地表示 LUT。在本例中,它使用了默认的 '双线性' 设置,它只是将每个彩色像素与线性线段连接起来。不同的 "-interpolate" 设置在使用非常小的颜色 LUT 时会生成不同程度的颜色平滑。例如,这里我展示了各种类型的 LUT 颜色插值平滑。

  magick gray_image.jpg  gradient_levels.png \
          -interpolate Integer         -clut  gray_levels_integer.jpg
  magick gray_image.jpg  gradient_levels.png \
          -interpolate NearestNeighbor -clut  gray_levels_nearest.jpg
  magick gray_image.jpg  gradient_levels.png \
          -interpolate Average         -clut  gray_levels_average.jpg
  magick gray_image.jpg  gradient_levels.png \
          -interpolate Blend           -clut  gray_levels_blend.jpg
  magick gray_image.jpg  gradient_levels.png \
          -interpolate BiLinear        -clut  gray_levels_bilinear.jpg
  magick gray_image.jpg  gradient_levels.png \
          -interpolate Catrom          -clut  gray_levels_catrom.jpg
  magick gray_image.jpg  gradient_levels.png \
          -interpolate Spline          -clut  gray_levels_spline.jpg
[IM Output]
整数
[IM Output]
最近邻
[IM Output]
平均
[IM Output]
混合
[IM Output]
双线性
[IM Output]
Catrom
[IM Output]
样条曲线
'整数' 和 '最近邻' 设置是特殊的,因为它们根本不会平滑颜色。也就是说,不会添加任何新的“混合颜色”,只有存在的精确颜色值将用于对灰度图像进行着色。但是请注意,这两种方法的颜色查找方式不同。这是一个细微的差别,但可能非常重要。“平均” 设置另一方面也生成了颜色带,但只使用颜色的混合,导致的颜色比颜色查找表图像的大小少一个。“混合” 然而混合了“平均” 和“最近邻”,以添加更多像素。这种类型的颜色“条带”(或 块状伪影)实际上在地理地图和温度图中相当常见,因为它更好地表示了地图的精确形状。锐利的边界边缘被称为等高线。在最终图像上添加一个轻微的单像素 模糊 可以改善这些边缘的外观,使其看起来更平滑,而不会破坏颜色条带。“双线性” 设置也会生成条带,但只有在颜色急剧变化时才以急剧的渐变变化的形式出现(在本例中没有)。而“Catrom” 将使颜色变化平滑。最后,“样条曲线” 会模糊颜色,并且可能不会生成给定 CLUT 中的任何颜色。为了避免插值问题,或更好地定义颜色渐变,最好的方法是使用更长的 LUT。理想情况下,这应该涵盖所有可能的强度值范围。对于 ImageMagick Q16(用 16 位质量编译),这需要 LUT 的高度为 65536 像素。但 像素插值 允许您使用更合理的 500 像素 LUT 渐变图像,适合大多数图像重新着色任务。请注意,以上示例中使用的垂直渐变 LUT 在我们眼中看起来是倒置的,因为黑色或 '0' 索引位于图像的顶部。通常,我们人类更喜欢看到黑色水平位于底部的渐变(这要归功于我们的进化历史)。如果您想以“正确的方式”保存渐变图像,则可以在读取图像时对图像进行 "-flip"。例如,让我们尝试一个更复杂的 LUT,在将它用于图像之前翻转垂直渐变。

  magick -size 1x33 gradient:wheat-brown gradient:Brown-LawnGreen \
          gradient:DodgerBlue-Navy   -append  gradient_planet.png
  magick gray_image.jpg \
          \( gradient_planet.png -flip \) -clut   gray_planet.jpg
[IM Output] ==> [IM Output] ==> [IM Output]
正如您所看到的,对于垂直渐变,在使用之前翻转它非常有意义。有关生成渐变的更多示例,请参见 颜色渐变。您可能还会对使用每个灰度级别的图像平铺灰度图像的方法感兴趣,这可以产生更好的“地图”状图像。请参见 使用图案抖动

函数到颜色 LUT 转换

这些预先准备好的“查找表图像”(或 LUT)也可以用来极大地提高非常复杂且因此很慢的 "-fx" 运算速度,因此 IM 不必每像素解释 3 或 4 次功能字符串,而是可以更快地查找替换颜色。执行此操作的过程非常简单,要么将函数应用于未修改的线性渐变,要么将函数中的 'u' 替换为值 '(i/w)' 或 '(j/h)',以根据其位置计算替换值。例如,在高级 '水下' 效果 示例中,我使用了一个复杂的 "-fx" 函数来调整 阴影运算符 的灰度输出”。同样,由于这种灰度调整也叠加在“DodgerBlue”形状上,因此这两个运算符的结果没有理由不能合并到单个渐变查找表中。也就是说,我们从 "-fx" 公式和颜色叠加生成一个 LUT。同样,对于这些示例,我决定生成一行像素,而不是像之前那样生成一列像素。

  magick -size 1x512 gradient: -rotate 90 -alpha off \
          -fx '3.5u^3 - 5.05u^2 + 2.05u + 0.3' \
          -size 512x1 xc:DodgerBlue -compose Overlay -composite \
          aqua_gradient.png
[IM Output]
上面的多项式 "-fx" 现在可以使用 多项式函数 更直接、更快地生成。例如
"-function Polynomial 3.5,-5.05,2.05,0.3"
此预生成的 LUT 现在可以更快地应用于阴影形状,只需存储一个非常小的图像的最低成本。

  magick -font Candice -pointsize 72 -background None label:A \
          -trim +repage  aqua_mask.png
  magick aqua_mask.png -alpha Extract -blur 0x6 -shade 120x21 \
          -alpha On -normalize  aqua_shade.png
  magick aqua_shade.png  aqua_gradient.png -clut aqua_font.png
[IM Output] ==> [IM Output] ==> [IM Output]
警告:以上内容不完整(边缘尚未变暗)
正如您所看到的,结果非常有效,一旦生成了合适的 LUT 渐变,就可以重复使用相同的渐变,只要您愿意就可以重复使用多少次。

CLUT 和透明度处理

"-clut" 运算符受 "-channel" 设置控制,但实际上,它只替换图像中的单个通道值。这意味着,通常源图像的每个通道都被用来从颜色查找表中“查找”仅该通道的替换值。这包括 alpha 通道,它通常很不方便,而且难以应用。通常 "-clut" 运算符用于对灰度源图像进行着色(请参见前面的示例),或者用于使用灰度 CLUT(颜色查找表)对彩色图像进行直方图调整。换句话说,通常其中一张图像将是灰度图像。从 IM v6.4.9-8 开始,如果 "-channel" 设置指定要替换/调整图像的 alpha 通道(存在一个 'A'),并且“源”图像或“CLUT”图像都没有定义 alpha 通道,那么 IM 将假设该图像为灰度图像,并相应地采取行动。例如,这里我生成一个简单的模糊三角形,作为一个灰度图像。然后,我可以使用包含透明度的颜色查找表进行着色。这一次我没有翻转 CLUT 图像,因此黑色替换将在顶部,白色替换将在底部。

  magick -size 100x100 xc:  -draw 'polygon 50,10 10,80 90,80' \
          -blur 0x10  blurred_shape.jpg
  magick -size 1x5 xc:none \
          -draw 'fill red    point 0,2' \
          -draw 'fill yellow rectangle 0,0 0,1'   gradient_border.png
  magick blurred_shape.jpg -alpha off    gradient_border.png \
          -channel RGBA  -interpolate integer -clut  clut_shape.png
[IM Output] ==> [IM Output] ==> [IM Output]
请记住,只有当灰度图像没有 alpha 通道(使用 "-alpha off" 或 "-alpha off" 关闭)并且您指定也想要查找 alpha 通道值(使用 "-channel RGBA")时,以上操作才能按预期工作。以下是另一种特殊情况,其中我们有一个具有透明度(和 alpha 通道)的图像,需要使用灰度直方图调整渐变(没有启用 alpha 通道)进行调整。

  magick -size 100x100 xc:none -draw 'polygon 50,10 10,80 90,80' \
          tile_disks.jpg -compose In -composite shape_triangle.gif
  magick shape_triangle.gif -channel A -blur 0x10 +channel shape_blurred.png
  magick -size 1x50 gradient: xc:black -append -flip \
          -sigmoidal-contrast 6x0%  feather_histogram.jpg
  magick shape_blurred.png \( feather_histogram.jpg -alpha off \) \
          -channel A    -clut    shape_feathered.png
[IM Output] ==> [IM Output] ==> [IM Output] ==> [IM Output]
以上是一个典型的 图像羽化 问题。中间图像中的“黑色”光晕是由 "-blur" 操作使包围三角形的完全透明区域变得可见造成的。由于完全透明具有未定义的颜色,因此 IM 默认使用黑色。CLUT 图像本身的设计确保任何透明度小于 50% 的像素都将变为完全透明,从而有效地使图像中以前完全透明的部分再次透明。对于此示例,我过度使用了初始“模糊”,然后过度校正了 alpha 通道调整。结果是三角形的尖端被严重圆化。对于正常的图像羽化,通常会对 "-blur" 和 "-sigmoidal-contrast" alpha 调整使用更小的值。 Fred Weinhaus 在他的 "feather" 脚本中实现了一种模糊羽化技术,以使其更易于使用。

Hald 3D 颜色查找表

从 IM v6.5.3-4 开始,您现在还可以使用完整的 3D 颜色查找表,它可以用来直接替换多个图像的所有颜色。也就是说,它不只是像上面 CLUT 中那样将每个颜色通道的值作为独立实体进行查找,而是使用整个颜色来查找新颜色。但是,3D 颜色表通常需要使用特殊的格式来正确存储 3D 颜色值数组。然而,通过使用颜色值的特殊排列,可以将 3D 表存储到称为 Hald 颜色 LUT 的 2D 图像中。这只是一个普通的图像,因此可以使用任何好的图像文件格式来保存 Hald 3D 颜色 LUT。有关 HALD 图像的更多详细信息和示例,请参见官方网站 Hald 图像,Clut 技术。要生成 Hald 3D 颜色表,请使用 'HALD:{level}' 图像生成器。例如,这里有一个很小的,我已经放大了,所以你可以看到各个像素...

  magick  hald:3    hald_3.png
[IM Output]
该表包含一个边长为 '{level}2' 色彩或 9 色彩的色立方体。完整的色立方体包含 '9 × 9 × 9' 种色彩,共计 729 种色彩,存储在一个大小为该数字平方根的图像中,即 27x27 像素。色彩的存储方式是,前 9 种色彩(左上角)形成从 '纯黑色' 到 '纯红色' 的渐变。每隔 9 种色彩,就会形成 '绿色' 的渐变,每隔 81 种色彩,就会形成 '蓝色' 的渐变。右下角的最后一种色彩为 '纯白色'。你可以将该图像看作是一个更简单的 1D 像素数组,它被引用为一个 3D 色立方体,这样更容易理解。现在这只是一张小的 HALD CLUT 图像。通常,你至少会使用一个 level 8 的 Hald(*默认*),它将包含一个每边 64 种色彩的色立方体,即 64^3 = 262144 种色彩,并生成一个大小为 512x512 像素的图像,并保存为大约 10Kbyte 的 PNG 图像。这并非所有 8 位色彩,但已经相当不错。对于一个包含所有 8 位色彩的 HALD 图像,你需要一个 level 16 版本,生成一个 4096x4096 的图像。这仅仅是为了证明,即使是普通的数码相机图像通常也不能包含所有可能的 8 位色彩。然而,可以采用更小的 Hald 图像,因为 IM 将从 Hald 中插值邻近的 8 种色彩,以计算出查找替换的最终色彩。它只是不会像更大版本的表示那样好。不建议使用大于 8 的 Hald 图像,因为它们需要非常大的图像,并且每个值的深度至少为 16 位才能存储。现在这些生成的 hald 图像是 '身份' 或 '无操作' CLUT 图像。也就是说,它们是形成 3D 色立方体的正常色彩值,因此不会对图像产生任何改变。例如,让我们使用 "-hald-clut" 运算符应用 '无操作' Hald 图像...

  magick rose:  hald_3.png -hald-clut   rose_hald_noop.png
[IM Output] ==> [IM Output] ==> [IM Output]
这张图像与原始图像完全相同,并且 Hald 图像中没有包含任何更改。然而,通过修改 Hald 图像,无论是手动修改还是使用颜色修改,你都可以用修改后的色彩替换原始色彩。例如,这里我创建一个混合的棕褐色色调方案...

  magick hald_3.png \( +clone -sepia-tone 60% \) -evaluate-sequence mean hald_sepia.png
  magick rose.png   hald_sepia.png -hald-clut   rose_hald_sepia.png
[IM Output] ==> [IM Output] ==> [IM Output]
当然,如果你可以将特定的颜色修改应用于 Hald 图像,你也可以直接将其应用于实际图像。但是,你也可以保存颜色修改以供重用,然后可以根据需要应用任意次。这意味着你可以在 halt 上花费精力,并将其保存起来以备将来使用。你还可以发送或下载 Hald CLUT 图像供其他人使用,甚至供其他应用程序使用。你甚至可以直接在 Hald 中编辑色彩,使用 "Gimp" 或 "Photoshop" 等图像编辑器,或者如果保存在 枚举的像素文本图像 中,可以使用纯文本编辑器!所有这些都特别适用于非常复杂的颜色修改 *请将你发现有趣或有用的任何 Hald CLUT 图像发邮件给我,我将在本文中以示例形式展示它们。你将在这里获得相应的署名!*

Hald CLUT 限制

与使用 CLUT 运算符 进行更简单的 1 维渐变查找不同,你可以使用 Hald CLUT 来旋转色彩。例如,交换红色和蓝色。这是一种更加通用的 CLUT 方法。然而,它不适合用于更简单的事情,例如给灰度图像着色,或进行颜色值的直方图调整。它还可以通过在 Hald CLUT 图像中保存这样的替换色彩,将色彩替换为透明或半透明的值。然而,这种替换查找仅根据色彩进行。你无法使用它以特定的方式替换透明色彩。毕竟它不是一个 4D 色彩查找超立方体!

使用 Hald CLUT 替换颜色

现在,由于整个颜色值被用于查找颜色替换,你也可以将其作为一种直接用其他颜色替换图像中所有颜色的方法。然而,由于 IM 目前对 Hald 进行线性插值查找,你需要在 3D 色立方体的所有 8 个相邻颜色单元格中设置替换颜色。
正在建设中
这需要更多工作,可能需要一个 '最近邻' Hald 查找设置(例如使用 -interpolate),而不是一个 3D 线性插值查找,以便更好地用于特定颜色替换。此外,一些用于在 Hald 中定位特定颜色的简单方法(最近邻,或 8 个邻居)将使这变得更容易。 如果你有任何想法、建议,或者更重要的是一些小的示例,请通过发邮件给我或 IM 讨论论坛进行贡献。 另一个想法是,如果你有两个图像,一个是原始图像,一个是转换后的图像,那么应该可以通过比较这两个图像来填充 Hald CLUT 图像。当填充了直接的色彩后,剩余的色立方体应该能够至少大致通过对现有的色彩进行曲线拟合而推导出。也就是说,从发现的颜色变化中创建一个 4D 颜色曲面。完成之后,你就可以将 Hald CLUT 应用于任何其他图像,以便对任何其他图像进行相同的颜色变换(在任何方向)。

完整颜色映射替换

未来:用另一个颜色映射中的色彩替换一个颜色映射中的所有色彩。欢迎提出有关如何最好地实现此目的的建议,或让程序员实现一些图像颜色映射功能。一种方法可能是使用 符号抖动 中介绍的想法。目前已知最好的解决方案(但并不理想)由 Fred Weinhaus 在他的 "mapcolors" 脚本中提供。该脚本本质上是逐个映射每个色彩,将一个图像中涉及的像素遮罩到一个新的初始空白图像中。另一个想法是将 3 维色彩替换映射到 HALD 颜色表 中。这不仅会映射指定的色彩,还会以逻辑的方式重新映射指定色彩之间的色彩。需要 HALD 生成器。
正在建设中
More color options yet to be looked at in detail...

  -contrast
  -brightness-contrast

Color Cycling?
    -cycle     shift colormap (for animations of fractals???)

Chromaticity Color Points???
   –white-point x,y
   –red-primary x,y
   –green-primary x,y
   –blue-primary x,y


Thresholds  (after negation)
  Specifically  -white-threshold and -black-threshold