ディザリング技術の種類と応用 - 限られた色数で階調を表現する手法
ディザリングとは - 少ない色で豊かな階調を表現する技術
ディザリング (Dithering) は、限られた色数しか使用できない環境で、色の空間的な配置パターンによって中間色を知覚的に再現する技術です。人間の視覚系が隣接するピクセルの色を空間的に平均化して知覚する性質を利用しています。1970 年代のコンピュータグラフィックスで発展し、現在も GIF 変換、印刷、ゲーム開発、デジタルオーディオなど幅広い分野で使用されています。
ディザリングの原理:
例えば白と黒の 2 色しか使えない場合でも、白と黒のピクセルを交互に配置すれば、遠目には灰色に見えます。白の割合を変えることで、様々な明るさの灰色を表現できます。これがディザリングの基本原理です。カラー画像でも同様に、限られたパレットの色を適切に配置することで、パレットに含まれない中間色を知覚的に再現します。
ディザリングが必要な場面:
- GIF 変換: 24 ビットカラーを 256 色に減色する際のバンディング防止
- 印刷: CMYK の限られたインク色でフルカラーを再現 (ハーフトーン)
- レトロゲーム: 16 色や 256 色パレットでの表現力向上
- デジタルオーディオ: ビット深度削減時のノイズシェーピング
- ディスプレイ: FRC (Frame Rate Control) による擬似階調表示
量子化誤差とバンディング:
色数を削減すると、元の色と代表色の差 (量子化誤差) が生じます。この誤差がグラデーション領域で系統的に蓄積すると、等高線状の縞模様 (バンディング) として視認されます。ディザリングは量子化誤差をランダムまたは規則的なパターンに変換し、バンディングを目立たなくします。
誤差拡散法 - Floyd-Steinberg とその派生
誤差拡散法 (Error Diffusion) は、各ピクセルの量子化誤差を周囲の未処理ピクセルに分配する手法です。局所的な平均色が元画像に近くなるよう誤差を伝播させることで、高品質なディザリングを実現します。
Floyd-Steinberg ディザリング (1976):
最も広く使用される誤差拡散法です。現在のピクセルを量子化した後、誤差を 4 方向に分配します。右に 7/16、左下に 3/16、下に 5/16、右下に 1/16 の比率です。ラスタスキャン (左上から右下) 順に処理するため、誤差は未処理の右方向と下方向にのみ伝播します。
Jarvis-Judice-Ninke ディザリング:
Floyd-Steinberg の拡張で、誤差を 12 個の周囲ピクセルに分配します (5x3 のカーネル)。より広い範囲に誤差を分散させるため、パターンが目立ちにくく滑らかな結果が得られます。ただし計算コストは Floyd-Steinberg の約 3 倍になります。分配比率の合計は 48 で正規化されます。
Stucki ディザリング:
Jarvis-Judice-Ninke と同様の 5x3 カーネルですが、分配比率が異なります。中心に近いピクセルにより多くの誤差を分配するため、シャープな結果が得られます。合計は 42 で正規化されます。写真のディザリングに適しています。
Sierra ディザリング:
Sierra は 3 種類のバリエーション (Sierra, Two-Row Sierra, Sierra Lite) を提供します。Sierra Lite は Floyd-Steinberg よりも軽量で、右に 2/4、左下に 1/4、下に 1/4 の 3 方向のみに分配します。品質は若干劣りますが、処理速度が速く、リアルタイム処理に適しています。
蛇行スキャン (Serpentine Scanning):
通常のラスタスキャンでは誤差が右方向に偏って伝播し、方向性のあるアーティファクトが生じることがあります。蛇行スキャンは奇数行を右から左に処理することで、誤差の伝播方向を交互に変え、方向性アーティファクトを軽減します。
順序付きディザリング - Bayer 行列とパターンディザ
順序付きディザリング (Ordered Dithering) は、事前に定義された閾値マップ (Bayer 行列) を使用して各ピクセルの量子化先を決定する手法です。誤差拡散と異なり、各ピクセルが独立に処理されるため、並列処理や GPU 実装に適しています。
Bayer 行列の構造:
Bayer 行列は再帰的に定義される閾値マップです。2x2 の基本行列 [[0,2],[3,1]] から、4x4、8x8、16x16 と拡張されます。n×n の Bayer 行列は n² 段階の閾値を提供し、n² + 1 段階の階調を表現できます。8x8 Bayer 行列は 65 段階の階調を表現可能で、多くの用途に十分です。
適用方法:
各ピクセル (x, y) に対し、Bayer 行列の対応する位置 (x mod n, y mod n) の閾値を取得します。ピクセル値が閾値以上なら明るい色、未満なら暗い色に量子化します。カラー画像では R, G, B 各チャネルに独立に適用します。閾値を [0, 1] に正規化し、ピクセル値と比較するのが一般的な実装です。
特性と用途:
Bayer ディザリングは規則的なパターンを生成するため、テクスチャ的な外観になります。これはレトロゲームやピクセルアートでは意図的に活用される美的特性です。一方、写真のような自然画像では、規則的なパターンが目立ちやすいという欠点があります。GPU シェーダーでの実装が容易で、リアルタイムレンダリングでの透明度ディザリングに広く使用されています。
GLSL での実装例:
GPU シェーダーでは、4x4 Bayer 行列をテクスチャまたは定数配列として保持し、フラグメントシェーダーで各ピクセルの閾値を参照します。float threshold = bayer[int(gl_FragCoord.x) % 4][int(gl_FragCoord.y) % 4]; のように座標からインデックスを計算し、アルファ値と比較してディスカードするパターンが一般的です。
ブルーノイズディザリングと確率的手法
ブルーノイズ (Blue Noise) ディザリングは、高周波成分が支配的なノイズパターンを使用するディザリング手法です。人間の視覚系は低周波のパターンに敏感なため、高周波ノイズは知覚されにくく、最も自然な見た目のディザリングを実現します。
ノイズの色とスペクトル特性:
- ホワイトノイズ: 全周波数が均等。ランダムだがクランプ (点の集中) が発生しやすい
- ブルーノイズ: 高周波が支配的。点が均等に分散し、クランプが少ない
- グリーンノイズ: 中周波が支配的。ブルーノイズとホワイトノイズの中間
Void-and-Cluster 法:
ブルーノイズ閾値マップの生成手法の一つです。初期のランダムパターンから、最も密集した点 (cluster) を除去し、最も疎な領域 (void) に点を追加する操作を繰り返します。結果として、点が空間的に均等に分布した閾値マップが得られます。事前計算が必要ですが、一度生成すれば Bayer 行列と同様にタイル状に繰り返し使用できます。
確率的ディザリング (Stochastic Dithering):
各ピクセルの閾値をランダムに決定する最も単純な手法です。ホワイトノイズを閾値として使用するため、クランプが発生しやすく品質は低いですが、実装が極めて簡単です。ブルーノイズマスクを使用した確率的ディザリングは、ランダム性を保ちつつクランプを防止し、高品質な結果を実現します。
時間的ディザリング (Temporal Dithering):
動画やリアルタイムレンダリングでは、フレームごとにディザリングパターンをオフセットする時間的ディザリングが使用されます。TAA (Temporal Anti-Aliasing) と組み合わせることで、時間方向の平均化により実効的な階調数を増加させます。各フレームのノイズは目立ちますが、時間的に平均化されると滑らかな結果が知覚されます。
ディザリングの品質評価と比較
ディザリング手法の品質を客観的に評価し、用途に応じた最適な手法を選択するための指標と比較基準を解説します。
評価指標:
- PSNR (Peak Signal-to-Noise Ratio): 元画像との数値的な差異を測定。ただしディザリングの知覚品質とは必ずしも相関しない
- SSIM (Structural Similarity): 構造的類似性を評価。ディザリングの品質評価により適している
- スペクトル分析: ディザリングパターンの周波数特性を分析。ブルーノイズ特性を持つほど知覚品質が高い
- 主観評価: 最終的には人間の目による評価が最も信頼性が高い
手法間の比較:
品質の観点では、ブルーノイズ > 誤差拡散 (Floyd-Steinberg) > 順序付き (Bayer) > ホワイトノイズの順が一般的です。ただし、速度は逆順で、Bayer が最も高速です。用途に応じた選択が重要で、リアルタイム処理には Bayer、オフライン高品質処理には誤差拡散、最高品質にはブルーノイズが適しています。
色数とディザリングの関係:
使用可能な色数が多いほど、ディザリングの必要性は低下します。256 色では強いディザリングが必要ですが、数千色あればディザリングなしでも十分な品質が得られることがあります。8 ビット/チャネル (1677 万色) から 6 ビット/チャネル (26 万色) への削減では、グラデーション部分にのみ軽いディザリングを適用するのが効果的です。
アダプティブディザリング:
画像の領域ごとにディザリングの強度や手法を変える適応的アプローチも存在します。エッジ付近ではディザリングを弱め (シャープさを保持)、平坦な領域では強めることで、全体的な品質を向上させます。エッジ検出と組み合わせた実装が一般的です。
実装ガイド - Python と GPU シェーダーでのディザリング
ディザリングの実装を Python (CPU) と GLSL (GPU) の両方で解説します。用途に応じた実装パターンと最適化のポイントを紹介します。
Python での Floyd-Steinberg 実装:
NumPy を使用した実装では、画像を float32 に変換し、ラスタスキャン順に各ピクセルを処理します。量子化は np.round(pixel * (n_colors - 1)) / (n_colors - 1) で行い、誤差を計算して周囲に分配します。処理速度の向上には Numba の @jit デコレータが有効で、純粋な Python と比較して 50-100 倍の高速化が可能です。
Python での Bayer ディザリング実装:
Bayer 行列は再帰的に生成します。bayer_2x2 = np.array([[0, 2], [3, 1]]) を基に、bayer_n = np.block([[4*bayer_m, 4*bayer_m+2], [4*bayer_m+3, 4*bayer_m+1]]) で拡張します。正規化した Bayer 行列をタイル状に画像サイズに拡張し、ピクセル値と比較するだけで完了します。ベクトル化により全ピクセルを一括処理でき、Floyd-Steinberg より 10 倍以上高速です。
GPU シェーダーでの実装:
リアルタイムレンダリングでは、フラグメントシェーダーでディザリングを適用します。透明度ディザリング (Alpha-to-Coverage の代替) では、Bayer 閾値とアルファ値を比較し、閾値未満のフラグメントを discard します。これにより、半透明オブジェクトをアルファブレンディングなしで描画でき、デプスバッファとの互換性が保たれます。
Pillow と ImageMagick での実践:
Pillow では img.convert('P', palette=Image.ADAPTIVE, colors=16, dither=Image.FLOYDSTEINBERG) で減色 + ディザリングを適用できます。ImageMagick では convert input.png -colors 16 -dither FloydSteinberg output.gif が同等の処理です。-dither None でディザリングなし、-dither Riemersma で Hilbert 曲線ベースのディザリングも選択可能です。