ImageMagick 範例 --
反鋸齒

索引
ImageMagick 範例 前言與索引
反鋸齒 簡介
只使用特定顏色繪圖
繪製細位元圖圓形
反鋸齒與 Flood Fill 問題
反鋸齒是 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 使用一種稱為「反鋸齒」的技術使圖像看起來更平滑、更美觀。這是一個花俏的術語,意思是它使用顏色混合甚至透明度來填充對象的邊緣像素,使對象看起來更平滑。如果沒有進行反鋸齒處理,那麼所有繪製對象的邊緣都會產生一種稱為「鋸齒」的階梯狀效果, though more commonly referred to as the 'jaggies'. 在這裡,我們再次繪製圖像,但這次我們要求 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