
甚麼是圖像抖色?Bayer矩陣、Floyd–Steinberg誤差擴散與GIF、ASCII藝術
當可用顏色有限,漸變可能變成一條條可見的色帶。抖色(dithering,亦稱遞色)透過重新排列像素點,營造中間亮度或顏色的觀感。它不會增加儲存的顏色數目,而是以空間紋理換取較平滑的色調觀感。
不過,GIF並非一定要抖色,ASCII藝術也不一定使用誤差擴散。本文以公式比較Bayer有序抖色與Floyd–Steinberg誤差擴散,並分清調色盤選擇、GIF壓縮及字符映射各自的作用。
日文原文發布: 2026-04-26
量化與抖色有何分別?
量化是把輸入映射至有限輸出值的處理。以黑白二值圖像為例,每個像素只可輸出黑或白。若全部採用同一個閾值,原本平滑的漸變也可能變成大片黑色和白色區域。
抖色可以按位置改變閾值,或把量化誤差傳到尚未處理的像素,以點的密度表現中間色調。在細小紋理能被視覺混合的觀看距離,色調可能較平滑;放大後則會見到顆粒或規則圖案。細節和文字有時不抖色反而較清晰,所以這不是必然提升畫質的處理。
用途包括黑白印刷、調色盤圖像及降低位元深度。即使使用PNG等無損壓縮儲存,若先前已減色,相對於原圖仍屬有損轉換。抖色亦不是能還原原始像素值的可逆編碼。
有序抖色:Bayer矩陣
B. E. Bayer於1973年的文獻探討以兩個輸出色階呈現連續色調圖像。這裏採用把4×4閾值矩陣重複鋪在圖像上的黑白教學模型。輸入為0至255,白色為255、黑色為0。以下是說明用的偽代碼,並非可以直接執行的JavaScript。座標從0起算,x向右增加、y向下增加,%代表餘數。
D4 = [[0, 8, 2, 10],
[12, 4, 14, 6],
[3, 11, 1, 9],
[15, 7, 13, 5]]
threshold = (D4[y % 4][x % 4] + 0.5) / 16
output = (input / 255 > threshold) ? 255 : 0閾值設在16個區間的中點,並非直接把0至15的兩端對齊至0至255。輸入0會全黑,255會全白,127.5則令一個區塊內的8個像素為白色。對均勻輸入而言,白色像素數可由0至16,即17種覆蓋程度;每個像素本身仍只有兩種值。這是數值比例的模型,不代表編碼後RGB的50%等於物理光量的50%。
D1 = [[0]]
D(2n) = [[4*Dn, 4*Dn + 2*Un],
[4*Dn+3*Un, 4*Dn + Un]]
Un = 所有元素均為1的 n × n 矩陣遞迴式每一項都是n×n矩陣,方括號表示把這些矩陣上下左右拼接的分塊矩陣。依此可產生2×2、4×4及8×8矩陣。每個像素只依賴座標與輸入,適合在GPU上平行運算。不過,即使矩陣固定於螢幕座標,移動的圖像或變化的顏色仍可能跨越閾值,令圖案改變;這不保證影片完全沒有閃爍。
Floyd–Steinberg:誤差權重與圖像邊界
Floyd與Steinberg在1976年的論文,是空間誤差擴散的原始文獻之一。按左至右、上至下的次序處理時,先把已包含先前收到誤差的當前值v量化為q,再將e = v − q傳至後續未處理的像素。
(x+1, y) += e * 7/16
(x-1, y+1) += e * 3/16
(x, y+1) += e * 5/16
(x+1, y+1) += e * 1/16權重總和為1,卻不是平均分配。只有全部接收位置均存在,而且中途沒有取整或將數值限制在某個範圍內,計算上才會把該份誤差全數傳出。若實作捨棄落在圖像外的分量,邊界便會流失誤差,所以有限大小圖像的輸出平均值未必與輸入完全相同。
例如2×1輸入[100, 100],大於127.5就輸出白色,否則輸出黑色,並捨棄圖像外的誤差。第一個像素變成黑色0,誤差為100。右鄰變成100 + 100×7/16 = 143.75,因而輸出白色255。結果[0, 255]的總和不等於輸入總和200。不能因為權重和為1,就斷言整張圖像的平均亮度完全保留。
應使用浮點數等適當表示方式儲存累積誤差。若每步都寫回8位元陣列而截斷數值,或將數值限制在0至255,輸出可能不同。掃描次序、邊界處理及取整方法也是演算法的條件。
衍生方法:誤差傳往哪裏、傳出多少?
Jarvis–Judice–Ninke、Stucki、Burkes、Sierra及Atkinson等方法,使用不同的誤差接收位置或權重。不要只憑名稱或發表年份比較優劣,應查看實際擴散核心和輸出。下表對照gif.js的GIFEncoder.js內三種方法;所有數值均假設接收位置完整位於圖像內。
如表格未能完整顯示,可左右捲動。
| 方法 | 接收位置 | 權重總和 | 核心的特點 |
|---|---|---|---|
| Floyd–Steinberg | 4個像素;當前橫行及下一橫行 | 16/16 = 1 | 分配至較小的鄰近範圍 |
| Stucki | 12個像素;當前橫行及其後兩橫行 | 42/42 = 1 | 分配至較大的鄰近範圍 |
| Atkinson | 6個像素;當前橫行及其後兩橫行 | 6/8 = 3/4 | 各接收1/8,餘下1/4不再傳遞 |
表格來源:GitHub: gif.js/src/GIFEncoder.js at master · jnordberg/gif.js。這是實作權重的核對,並非確認各方法的發明年份。
擴散範圍較大,需要讀取及更新的像素也較多,但觀感不一定更好。Atkinson這類只擴散部分誤差的方法,可能失去細微明暗層次。應以照片、輪廓及期望的復古效果比較實際輸出。
彩色量化與GIF:先選調色盤,再映射顏色
使用任意調色盤減色時,先選定可用顏色,再把各像素映射至調色盤項目。若只將RGB各通道獨立取整,可能產生調色盤內沒有的顏色組合。這與選取調色盤中的輸出色,再把差異當作色彩向量擴散,是不同的處理。
單純RGB距離或考慮感知的距離,以及在線性光還是編碼後RGB中計算誤差,都會影響結果。只指定CIEDE2000或OKLab名稱,不保證減色更自然;還需要正確的色彩空間轉換及符合用途的評估。相關原理可參閱CSS color-mix()與OKLCH文章。
pngquant結合調色盤生成及適應性抖色。簡單的「RGB最近鄰+標準Floyd–Steinberg」四步驟,不足以說明其完整實作。
GIF色彩表最多有256個項目。擁有局部色彩表的圖像可以採用自己的調色盤,因此整段動畫不一定只用同一組256色。抖色並非GIF規格的必要步驟,也與LZW壓縮分開。GIF與LZW專利文章有進一步解說。
NanToo的GIF製作工具使用gif.js,但目前呼叫時沒有指定抖色,預設值為false。程式庫支援Floyd–Steinberg等方法,與工具已啟用該功能,是兩回事。
ASCII藝術:字符映射不等於誤差擴散
把字符由視覺上較密到較疏排列,可以將亮度映射至字符選擇。不過,字符看起來有多深,取決於字型、字距、文字及背景顏色。N個字符不保證形成感知上等距的N個色階。僅憑字符密度讓圖像可辨認,不能判定使用了Floyd–Steinberg。
NanToo的ASCII藝術轉換工具先將透明像素合成至白色背景,再把各通道取整,然後套用以下權重與索引映射。這也是偽代碼。N為字符集的元素數,至少為2;idx是從0起算的索引,floor表示向下取整。R、G、B為合成後0至255的值。
L = 0.299*R + 0.587*G + 0.114*B
mapped = invert ? 255-L : L
idx = min(N-1, max(0, floor(mapped/255 * (N-1))))這是使用BT.601之luma係數計算的明亮程度近似值。把這些係數套用至Canvas RGB,不代表已實作整套BT.601訊號處理,也不同於線性光的物理輝度。
黑色映射至第一個字符,白色映射至最後一個;反轉設定會反轉亮度映射。現有轉換沒有誤差擴散,而且可選字符集不只一種,因此不應說它「總是用70個字符忠實重現256個色階」。應連同字符排列及實際字型顯示一起評估。
紋理、掃描方向與壓縮大小的取捨
誤差擴散也可能產生規則圖案或條紋。蛇形掃描會在每個橫行交替左右方向;由右至左處理時,擴散核心的水平位移也要鏡像反轉。這有助減少單一方向的偏差,但不保證消除所有圖案。
有序抖色可在同一座標重用相同閾值,代價是重複的區塊紋理可能較顯眼。藍雜訊閾值排列,以及沿曲線傳遞誤差的Riemersma等,也是其他選擇。ImageMagick提供Riemersma及Floyd–Steinberg誤差擴散,以及有序抖色設定。不能只憑產品名稱,就假定每個模式均採用相同核心和掃描方式。
抖色在平坦區域加入細微變化,可能令壓縮後檔案更大。它是在色調觀感、輪廓、顆粒感、處理時間與檔案大小之間取捨,並非無代價地增加資訊。
降低位元深度與GPU運算
把高精度圖像量化至較低位元深度時,抖色是令色帶較不明顯的選項之一。不過,HDR轉SDR的色調映射是在改變亮度範圍,並不等於把數值精度由16位元改為8位元。應將色調映射、色域轉換、量化與抖色視為不同階段。
Bayer公式由每個像素的座標及輸入決定,適合在GPU上平行執行;一般誤差擴散則令後續像素依賴先前結果。這項差異會影響實作方式。
圖像的空間抖色、時間方向取樣,以及機器學習模型的INT8量化,都涉及有限的表示方式,但並非同一演算法。這個共通點不足以證明Floyd–Steinberg或Bayer普遍用於AI推論,也不能支持它們接近資訊理論最佳解的說法。
重點整理:改變點的排列,而非增加顏色數目
- 量化限制輸出值,抖色則改變紋理配置,調整中間色調的觀感。
- Bayer按座標設定閾值;Floyd–Steinberg先量化包含累積誤差的數值,再以不相等的權重擴散新誤差。
- 即使權重和為1,圖像邊界、取整及範圍限制仍可能令整體平均值不能完全保留。
- GIF調色盤限制、減色時的抖色與LZW壓縮是不同機制。
- ASCII字符映射也有別於誤差擴散,需核對實作、設定及字型。
- 應按用途,以實際圖像比較顆粒感、輪廓、處理速度及檔案大小。
參考資料及來源
- JST J-GLOBAL: An adaptive algorithm for spatial greyscale. ↗(Floyd與Steinberg,1976年,Proc. SID 17(2):75–77。此頁只提供書目資料,並非論文全文。)
- Wikipedia: Ordered dithering - Wikipedia ↗
- Computer Graphics and Image Processing: A survey of techniques for the display of continuous tone pictures on bilevel displays ↗
- Wikipedia: Error diffusion ↗
- mitpress.mit.edu: Digital Halftoning ↗
- ACM Transactions on Graphics: Digital halftones by dot diffusion ↗
- ITU: BT.601 : Studio encoding parameters of digital television for standard 4:3 and wide screen 16:9 aspect ratios ↗
- Network: Computation in Neural Systems: Kohonen neural networks for optimal colour quantization ↗
- NDL Search: An Optimum Method for Two-Level Rendition of Continuous-Tone Pictures ↗(Bayer,1973年。此頁為書目資料,並非論文全文。)
- CompuServe: Graphics Interchange Format, Version 89a ↗
- GitHub: gif.js/src/GIFEncoder.js at master · jnordberg/gif.js ↗
- pngquant: pngquant ↗
- ImageMagick: Command-line Options ↗
- Joel Yliluoma: Joel Yliluoma's arbitrary-palette positional dithering algorithm ↗
編輯說明
本文使用 AI 協助編輯,並在發布前由編輯核對。內容仍可能包含事實、理解或時效上的錯誤;作出重要決定前,請查閱所列的一手資料或官方文件。

