ImageMagick 示例 --
抗锯齿

索引
ImageMagick 示例前言和索引
抗锯齿简介
仅使用特定颜色绘制
绘制细位图圆形
抗锯齿和填充问题
抗锯齿是 ImageMagick 中所有绘图操作的重要组成部分。不幸的是,它也可能导致许多问题。此页面尝试涵盖这些问题并提供解决方案。

抗锯齿简介

ImageMagick 在绘制图像时,采用一种非常特殊的方式。它使用称为“抗锯齿”的操作来绘制图像。为了演示,我将在透明背景上绘制一个图像,然后放大图像的一小部分,以便您可以看到正在发生的事情。

   magick -size 80x80 xc:none \
            -fill white  -draw "circle 40,40 15,20" \
            -fill black  -draw "line 5,30 78,2"    drawn.png
    magick drawn.png -crop 10x10+50+5 +repage -scale 80x80  drawn_mag.png
 
[IM Output] ==> [IM Output]
现在您可能会认为上面的图像只有三种颜色,“白色”、“黑色”和“透明”,因为这是我们要求 IM 使用的所有颜色。但是,正如您在放大图像时看到的那样,它具有各种颜色。通过这样做,ImageMagick 使图像看起来更平滑,更美观,使用一种称为“抗锯齿”的技术。这是一个花哨的术语,表示它用混合颜色甚至透明度填充对象的边缘像素,以使对象看起来更平滑。如果没有进行抗锯齿,则所有绘制对象的边缘都会出现阶梯状效果,称为“锯齿”,但更常称为“锯齿”。在这里,我们再次绘制图像,但这次我们要求 IM 关闭其自动抗锯齿操作,使用“+antialias”。

   magick -size 80x80 xc:none +antialias \
            -fill white  -draw "circle 40,40 15,20" \
            -fill black  -draw "line 5,30 78,2"    drawn_jaggies.png
    magick drawn_jaggies.png -crop 10x10+50+5 +repage -scale 80x80 \
            drawn_jaggies_mag.png
 
[IM Output] ==> [IM Output]
这次图像确实只有三种颜色。但结果一点也不好。在最新的 IM 上,绘制了一条像素单行,以阶梯状方式。在较旧的 IM 上,线条的外观也会相当粗,使其看起来更糟糕。基本上,这不是您通常想要做的事情。“锯齿”效果的阶梯,也常称为“锯齿”,是 IM 试图避免的。但是,如果您需要特定的颜色,则需要接受这一点,或者使用其他技术(例如颜色量化)来确保您只使用某些颜色。请注意,实际上存在两种形式的抗锯齿。第一种是在图像中混合白色和黑色,产生各种颜色阴影,在这种情况下为灰色。另一种形式是颜色和透明度的混合,以生成图像中的半透明像素。后者是您需要注意的事项,因为许多图像格式(例如 GIF)无法处理半透明像素,并且会将此类像素设为完全不透明或完全透明。上的示例GIF 布尔透明度演示了您可以控制保存到此类格式时处理半透明像素的方法。

总结

抗锯齿在任何类型的图像绘制中都非常重要,并且是您应该牢记的事情。如果不考虑 IM 抗锯齿生成的混合颜色和半透明像素,您自己的图像创作在某些格式中可能会看起来非常糟糕。当您在不允许半透明像素的图像格式中创建图像时,这一点变得更加重要,例如广泛使用的“GIF”格式。参见GIF 布尔透明度了解处理此问题的方法。IM 非常擅长抗锯齿颜色和透明度,但实际上非常不擅长绘制纯“锯齿”像素(例如匹配特定颜色映射)。我听说这将是 IM 后续版本的重点。

仅使用特定颜色绘制

建设中
无需抗锯齿即可进行绘制的更好方法,以生成具有精确颜色的图像。也就是说,对于“索引图像”。具体来说,绘制到透明画布中,对 alpha 通道进行阈值处理,然后覆盖,以便仅绘制完全不透明的像素。

绘制细位图圆形

在这里,我们尝试使用 IM 绘制位图“细线”圆形。通常,这是使用位图圆形绘制算法完成的,该算法通常称为 Bresenham 圆形算法,但更准确地称为中点圆算法。不幸的是,ImageMagick 中没有提供此功能,并且可能永远不会提供,因为它在完全抗锯齿的绘图环境中不需要。绘制圆形的另一种替代方法,我们稍后会看到,是使用形态学,以膨胀单个像素,使用特殊的环形内核
例如,IM 绘制圆形的正常方式会产生大量灰色抗锯齿颜色,以使圆形外观平滑。

  magick -size 15x15 xc: -fill none -stroke black \
          -draw 'translate 7,7 circle 0,0 5,0' \
          -scale 500%  circle_antialiased.gif
[IM Output]
但是,简单地关闭抗锯齿会产生不是漂亮的细“位图”线的圆形和线条。

  magick -size 15x15 xc: -fill none -stroke black +antialias \
          -draw 'translate 7,7 circle 0,0 5,0' \
          -scale 500%  circle_aliased.gif
[IM Output]
您需要做的也是调整“-strokewidth”,它默认为 1 像素宽,调整为更小的值,例如 0.5 像素宽。

  magick -size 15x15 xc: -fill none -stroke black +antialias \
          -strokewidth 0.5   -draw 'translate 7,7 circle 0,0 5,0' \
          -scale 500%  circle_thin_stroke.gif
[IM Output]
更好但不太正确。但是,您也可以使笔划宽度过小,尤其是在半径为奇数的情况下。

  magick -size 15x15 xc: -fill none -stroke black +antialias \
          -strokewidth 0  -draw 'translate 7,7 circle 0,0 5,0' \
          -scale 500%  circle_zero_stroke.gif
[IM Output]
这是一个以整数实际像素位置为中心的 5 像素圆形的良好解决方案。

  magick -size 15x15 xc: -fill none -stroke black +antialias \
          -strokewidth 0.4  -draw 'translate 7,7 circle 0,0 5,0' \
          -scale 500%  circle_perfect.gif
[IM Output]
但是经过多次实验,我找不到适用于所有半径和中心的“-strokewidth”。特别是稍微偏离中心的圆形。
没有适用于所有情况的理想解决方案
例如,这个圆形没有以像素或像素边界为中心,不仅顶部有间隙,而且底部也过厚!糟糕!

  magick -size 15x15 xc: -fill none -stroke black +antialias \
          -strokewidth 0.47  -draw 'translate 7,7.3 circle 0,0 5,0' \
          -scale 500%  circle_bad_stroke.gif
[IM Output]
这是一个用于生成特定半径的细单像素宽圆形的良好“-strokewidth”表。请注意,要使用的最佳值取决于圆形是位于实际像素(例如“5 , 5”)上,还是位于半个像素边界(例如“5.5 , 5.5”)上。
圆形半径 SW 实际 SW 半个
1 0.3   0.3 ¶
1.5   0.5 ¶ 0.3
2 0.3   0.3 §
2.5   0.5 ¶   0.3 ¤
3   0.3 ¤ 0.3
3.5 0.5   0.3 ¤
4   0.5 § 0.3
4.5 0.5 0.3
5 0.4 0.3
5.5   0.5 ¶ 0.3
6 0.3   0.5 §
6.5 0.5 0.43
7 0.5 0.434
7.5   0.5 §   0.5 §
8 0.4 0.5
¤ 非常好的小圆形
§ 未找到理想宽度
¶ 圆形非常糟糕
附注:要在图像中以绘图坐标(像素坐标)为中心绘制圆形,则为 (size-1)/2

抗锯齿和填充问题

由于 IM 的抗锯齿功能,填充操作(“-draw color floodfill”)在用于具有抗锯齿效果的图像时会出现问题。它在从“JPG”图像格式读取的图像中也存在类似的问题。基本上,由于 IM 中的大多数对象都是抗锯齿的(或从“JPG”格式的图像文件中读取),因此绘制对象边缘附近的颜色很少是您用于填充替换的特定颜色。这意味着填充不会填充您尝试填充区域的最边缘,除非您完全避免抗锯齿。从本质上讲,填充甚至颜色替换都不理解抗锯齿,也不使用抗锯齿技术本身。因此,填充通常会错过您正在填充区域的最边缘的像素。例如,在这里,我们执行一个典型的填充操作。绘制一个圆形,然后尝试用图案填充它...

    magick -size 60x60 xc:lightblue -strokewidth 2 \
            -fill none -stroke red -draw "circle 30,30 5,30" \
            -tile tile_weave.gif  -draw "color 30,30 floodfill" \
            tile_fill_1.gif
    magick tile_fill_1.gif -crop 10x10+35+4 +repage -scale 80x80 \
            tile_fill_1_mag.gif
 
[IM Output] [IM Output]
如您在图像的放大部分中看到的,填充操作完全错过了“异色”像素的一行,因为这些像素的颜色与您正在填充的区域的颜色不完全相同。改进此问题的一种方法是在您打算填充的区域中预先填充与您使用的图案匹配的颜色。图案仍然不会完全填充该区域,但至少看起来不会那么糟糕。

    magick -size 60x60 xc:lightblue -strokewidth 2 \
            -fill black -stroke red  -draw "circle 30,30 5,30" \
            -tile tile_weave.gif  -draw "color 30,30 floodfill" \
            tile_fill_2.gif
    magick tile_fill_2.gif -crop 10x10+35+4 +repage -scale 60x60 \
            tile_fill_2_mag.gif
 
[IM Output] [IM Output]
另一种方法是用您的图案填充该区域,使用较高的模糊因子,以强制图案完全填充该区域,一直到最边缘,而不会错过边缘像素。

    magick -size 60x60 xc:lightblue -strokewidth 2 \
            -fill none -stroke red  -draw "circle 30,30 5,30" \
            -fuzz 35% -tile tile_weave.gif -draw "color 30,30 floodfill" \
            tile_fill_3.gif
    magick tile_fill_3.gif -crop 10x10+35+4 +repage -scale 60x60 \
            tile_fill_3_mag.gif
 
[IM Output] [IM Output]
请注意,像这样高的“模糊因子”或过细的边框会导致填充图案从定义的区域“泄漏”。使用填充操作时始终需要小心。我实际上不建议将其作为通用解决方案,因为存在此问题。
问题在于,填充操作从其本质上讲本身不使用抗锯齿,因此填充区域的边缘会出现“锯齿”或锯齿效果。您可以通过将图像绘制分成单独的步骤来改善这种情况。创建一个彩色圆形,填充它,然后绘制边框。

    magick -size 60x60 xc:lightblue -fill black -draw "circle 30,30 5,30" \
            -tile tile_weave.gif -draw "color 30,30 floodfill" +tile \
            -fill none -stroke red  -strokewidth 2 -draw "circle 30,30 5,30" \
            tile_fill_4.gif
    magick tile_fill_4.gif -crop 10x10+35+4 +repage -scale 60x60 \
            tile_fill_4_mag.gif
 
[IM Output] [IM Output]
这是一种改进填充的简单方法。另一种方法是使用形状叠加,但这可能是一种难以解决的方法。稍后,我将研究对现有图像进行类似的修改。当然,如果您自己绘制正在填充的区域,而不是使用现有图像,则理想的解决方案是通过为原始绘制操作指定填充图案来避免填充。

    magick -size 60x60 xc:lightblue -strokewidth 2 \
            -tile tile_weave.gif -stroke red -draw "circle 30,30 5,30" \
            tile_fill_5.gif
    magick tile_fill_5.gif -crop 10x10+35+4 +repage -scale 60x60 \
            tile_fill_5_mag.gif
 
[IM Output] [IM Output]

FUTURE:  anti-aliasing issues on pre-exsiting images (especially JPG format).

For Example Recoloring and overlaying text or diagram image onto a color
or background.

Also re-adding transparency to GIF files, and rescaled JPEGs for icon use.

Smoothing or anti-alising images with limited color set
Specifically bitmap (pure black and white) images.

First anti-aliasing does not work on bitmap images.

Anti-aliasing involves using a mix of colors and transparences to try and
smooth the 'stair case' or 'jaggies' effect of slanted lines and color
boundaries.  If only two colors are available no anti-aliasing can NOT happen!

The image must be converted from B&W or grey scale at the minimum before
anti-aliasing can be used.

A simple way to smooth edges is to use a small amount of blur after reading in
a B&W image or an image with a tiny pallette size.

EG:   magick image.xbm  -blur 0x.3  smoothed_image.png