配列ディザリング
配列ディザリング(はいれつでぃざりんぐ)とは、画像をディザリングするアルゴリズムの一つである。
このアルゴリズムは通常、色深度が小さくて表示可能な色数が少ないディスプレイにおいて、なるべく滑らかな画像を表示するために使用される。たとえば、Microsoft Windowsは「VGA標準16色グラフィックモード」の時に配列ディザリング法を用いてグラフィックを表示する。画像にこのアルゴリズムを適用した場合、特徴的なクロスハッチング状のタイルパターンがハッキリと現れる。
このアルゴリズムは「しきい値マップ(M)」を使って色数を減らす。ディスプレイに表示される画像の各ピクセルの色の値と、対応する「しきい値マップ」のセルの値を比較し、画面に表示する色を決める。その過程で、元画像の色と、そのディスプレイで利用可能なカラーパレットの色との差分が切り捨てられる。
「しきい値マップ」の大きさは任意に決めて構わないが、普通は「2の累乗 かける 2の累乗」のマトリクスを使う。ちなみに、この「しきい値マップ」のことを「テーブル」という日本人がいるが、「マトリクス」が正しい(「テーブル」は「値」で構成された表、「マトリックス」は「行」と「列」で構成された表、つまり「行列」のこと)。
このマップは、回転や反転を行ったとしてもアルゴリズムの有効性に影響を与えない。両辺の長さがどちらも2の累乗になった、このようなしきい値マップは「インデックスマトリクス」または「ベイヤーマトリクス」とも呼ばれる。白黒2値で写真を表現するためにコダック社のブライス・ベイヤーが考案し、1973年に発表した。(ベイヤー以後にもディザリング用に独自のマトリクスを考案した者は存在するが、結局ベイヤーマトリクスが一番早くて奇麗なのでずっと使われている。ランダムな値のマトリクスを使う「ランダムディザリング法」も存在するが、綺麗にうまくいくシーンはそれほど多くない)
配列ディザリング法を使う場合は、上記のマトリクスをそのまま使うのが一般的だが、もし任意のサイズのしきい値マップを自力で作りたい場合は、簡単な法則を使って制作できる。まず、各スロットを連続した整数で埋める。次に、マップ内の2つの連続した数同士の平均距離ができるだけ大きくなるように並べ替え、テーブルが端で「折り返される」ようにするFebruary 2008。
次元が2の累乗であるしきい値マップの場合、マップは以下の定式で再帰的に生成できる。
\mathbf M_{2 n} = \frac{1}{(2n)^2} \times \begin{bmatrix}
4 \times \mathbf M_n & 4 \times \mathbf M_n + 2 \\
4 \times \mathbf M_n + 3 & 4 \times \mathbf M_n + 1
\end{bmatrix}
再帰式は、ビット演算のみを使用して明示的に計算できる。
M(i, j) = bit_reverse(bit_interleave(bitwise_xor(i, j), i)) / n ^ 2
ディザリングが実行されるハードウェアの精度にもよるが、しきい値マップを、 0 からn^2までの整数をn x nのマトリクスとして保持しておくという伝統的な方式よりも、前処理で浮動小数点の形式にしたものを保持していた方が便利な場合がある。
その場合、以下の式が使われる。
Mpre(i,j) = (Mint(i,j)+1) / n^2
これにより、「標準化(正規化)しきい値マトリクス」が生成される。
例えば2×2のしきい値マップは、
であるが、これをベースとして「しきい値マップ」に前処理をかけると、
となる。加えて言うと、前処理によって各値を正規化して合計を0に平均化するようなマップを作ることもできる。この場合、しきい値マップの全ての値から0.5を減算すればよい。
Mpre(i,j) = (Mint(i,j)+1) / n^2 - 0.5
これにより、次のような前処理済ディザマップが生成される。
配列ディザリングのアルゴリズムは画像を自然に表現できるが、しかし画像のピクセルの各々においては、そのピクセルの色の値がしきい値マップの対応するセルの値で置き換えられることになるので、ピクセルの色の値のしきい値を超えた部分がカットされ、元の色とは異なる色に量子化されることになる。
配列ディザリング法のアルゴリズムを組むに際して、ほとんどの場合、すべてのピクセルにしきい値を追加して、ピクセルの値をしきい値と比較するだけで充分ディザリングの実用を満たすので、わざわざ0.5を減算して「しきい値マップ」を正規化する必要まではない。元画像のピクセルの輝度の値がベイヤーマトリクスの対応するセルの値よりも小さかった場合は、そのピクセルを「黒」で描写し、そうじゃなかった場合は「白」で描写する、みたいなアルゴリズムで充分である。ただ、正規化しなかった場合、画像の平均輝度がわずかに増加し、「ほとんど白に近いグレー」のピクセルがディザリングされずにそのまま白になる、という欠点がある。もっともこれは、グレースケールパレット(もしくは色同士の相対的な距離が(ほぼ)一定なカラーパレット)を使用する場合は問題とはならない。人間の目は明色の階調差よりも暗色の階調差の方をより正確に認識するため、「ほとんど白に近いグレー」を白飛びさせて暗色の階調差に割り振るのは、むしろ望ましいディザリング結果になるとすら言える。一方で、色数の特に少ないカラーパレットや任意の色で構成されたカラーパレットを使用してディザリングを行った場合は、元画像と全然違った見た目の画像が生成されることになるため、アルゴリズムを組む人は面倒がらずに適切な正規化を行った方が良い。
逆の目線で言うと、アルゴリズムは、すべてのピクセルの各カラーチャンネル「c」に対して次の変換を実行する。
c' = \mathrm{nearest\_palette\_color}\mathopen{}\left(c + r \times \left(M(x \bmod n, y \bmod n) - \frac{1}{2}\right)\mathclose{}\right)
ここで、「M(i, j)」はi番目の行とj番目の列のしきい値マップ、「c′」は変換された色、「r」は色空間における広がりの量である。RGBの各色が0から255までのオクテットで表された、全「23N」色の等間隔な色で構成されるカラーパレットを想定すると、一つの色は通常はこのような形で選択される。
r \approx \frac{255}{N}
このアルゴリズムは1つ1つのピクセルのみを操作するもので、条件分岐もないため、非常に高速で、リアルタイム変換に適している。さらに、ディザパターンは表示フレームに対して常に同じ位置にあるため、誤差拡散法よりもジッター(いわゆる「カクツキ」「チラツキ」、現代のゲームエンジンのレトロゲーム機風シェーダーでも問題になる)が発生しにくく、アニメーションに適している。誤差拡散法のディザパターンよりも反復的なタイリングであるため、配列ディザリングを使用した画像の圧縮率は高くなる。配列ディザリングは、直線が真っ直ぐに変換され、揺らぎが少ないため、イラストなど線画系の画像に適している(写真などは誤差拡散法の方が奇麗に出る)。
カラーパレットに合わせてしきい値マップを自作する場合、しきい値マップの値同士の差は、カラーパレットの色同士の最小の差と同じ範囲になるようにスケーリングすることが望ましい。
(Graphics course project, Visgraf lab, Brazil)
(Lee Daniel Crocker, Paul Boulay and Mike Morra)