地図/画像/画像処理

誤差拡散 — はみ出しを隣に配る

あるセルを白か黒に丸めると、必ず誤差が出る。0.6 を白(1)にすると -0.4 のはみ出し。その誤差を捨てずに、まだ処理していない隣のセルに配る。配られた隣は少し暗くなり、次に丸めるとき黒へ倒れやすくなる。捨てた濃淡が隣で帳尻を合わせる。

誤差拡散。量子化の丸め誤差を未処理の近傍へ分配して、領域ごとの平均濃度を保つ二値化。Floyd–Steinberg は誤差を右に 7/16、左下に 3/16、下に 5/16、右下に 1/16 で配り、重みの合計が 16/16 で誤差が漏れない。Robert W. Floyd と Louis Steinberg が 1976 年に発表したもの。各セルの結果が次のセルの入力に効くので走査順に依存し、左上から右下への一筆書きで処理する。位置だけで閾値を決める順序ディザと違い、並列化しにくい代わりに網点が不規則にばらける。

場を nx × ny の配列に溜めて、左上から順に各セルを 0.5 で丸める。誤差を配らずに丸めるだけだと、グラデの途中が同じ色で塗りつぶされて段差が出る。0.5 付近の中間調が、白と黒の太い帯に潰れる。

中間調が黒の塊と白の抜けに割れて、元のグラデの帯がそのまま見える。丸めて捨てた 0.5 付近の濃淡が、どこにも残らない。

誤差を配る一行を足す。あるセル i の元の値 old01 に丸め、差 old - next を四方の未処理セルへ重みつきで足し込む。配るのは右・左下・下・右下の四つで、いずれもこれから走査するセル。

// old を 0 か 1 に丸めて、誤差を未処理の隣へ配る
const old = buffer[i]
const next = old < 0.5 ? 0 : 1
const err = old - next
if (gx + 1 < nx) buffer[i + 1] += (err * 7) / 16
if (gy + 1 < ny) {
  if (gx > 0) buffer[i + nx - 1] += (err * 3) / 16
  buffer[i + nx] += (err * 5) / 16
  if (gx + 1 < nx) buffer[i + nx + 1] += (err * 1) / 16
}

buffer[i + 1] は右隣、buffer[i + nx] は真下、±1 した先が左下と右下。配り終えてから次のセルを読むので、隣はすでに誤差を受け取った値で丸められる。この丸めと分配を左上から右下まで一筆書きで通す。

太い帯が消えて、点が不規則にばらけた網になる。中間調ほど黒と白の点が半々に混ざり、離れて見ると灰色に戻る。7/16 などの重みを崩して合計を 16/16 から外すと、誤差が増殖したり消えたりして濃さがずれる。配る向きを下優先に変えると点の流れ方も変わる。順序ディザの規則的な網目と違い、走査順に誤差を引きずるので網点が有機的にうねる。