2006 年 2 月 10 日 22 時 18 分

単純誤差拡散による 2 値化


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


[写真] [写真] [写真]


段々と品質が上がってきたが、まだ不安がある。
パターンによるマッチングでは、
中間値やベタ塗りに対しては効果があったが、
格子状のパターンがどうしても目に付く。

パターン配列を大きく定義したとしても、
座標位置によって閾値が固定であるため、
明度と位置関係によっては、偏ってしまう可能性はある。

今までは白と黒のバランスという視点で考えていたが、
今日は、オリジナルと出力の差に着目しよう。

2 値化は、ピクセルがどのような明度であっても、
結果的に白か黒かに強制的に変更される。
その際に、元の明度値と出力値の差を記憶しておき、
後でその分の穴埋めをするという考え方だ。

例えば、元の明度値が 200 とする。
128 を閾とした場合、出力は白(255)となる。
元は 200 なので、55 過剰に出力したことになる。
この余分な 55 が差分値であり、後の処理で減算するのだ。

元の画像の明度値合計と出力値合計が、
なるべく同じになるように補正されるので、
全体的に見ると、最も有力な方法と考えられている。

では、この差分処理をどこに押し付けるか。
真っ先に思い浮かぶのは、周辺のピクセルである。
図で書いてみよう。■が処理中のピクセルだ。

×××
×■○
○○○

画像の計算処理は、左から右、上から下への順で行っている。
なので、計算処理がまだ行われていない、
○に差分処理を押し付けることができる。
逆に、×は処理が終わっているので、
■は差分値を押し付けてられているはずだ。

難しいのは○に対してどれくらいの責任を負わすかだ。
今日は、以下のような責任分担で考えてみよう。

×××
×■1
111


# BMP 読み込み。
my $data = read_bmp;

# とりあえずグレースケールに。
foreach my $line (@$data) {
    foreach my $pixel (@$line) {
        my ($red, $green, $blue) = unpack_rgb($pixel);
        no integer;
        $pixel = $red * 0.299 + $green * 0.587 + $blue * 0.114;
        $pixel = int($pixel);
    }
}

# 高さと幅を取得。
my $height = @$data;
my $width = @{$data->[0]};

# 行ごとの処理。
for (my $yy = 0; $yy < $height; ++$yy) {

    # ピクセルごとの処理。
    for (my $xx = 0; $xx < $width; ++$xx) {

        # 元の明度値 0~255 に対して、
        my $src = $data->[$yy][$xx];

        # 128 を閾値として白か黒か決める。
        my $dest = $src >= 128 ? 255 : 0;

        # 本来の値との不足分を求め、
        my $diff = $src - $dest;

        # 右、左下、下、右下のピクセルに責任分担する。
        # 責任の重さを示す配列は以下とする。

        # - - -
        # - x 1
        # 1 1 1

        $data->[$yy + 0][$xx + 1] += $diff / 4
            if $xx + 1 < $width;

        $data->[$yy + 1][$xx - 1] += $diff / 4
            if $yy + 1 < $height and $xx - 1 >= 0;

        $data->[$yy + 1][$xx + 0] += $diff / 4
            if $yy + 1 < $height;

        $data->[$yy + 1][$xx + 1] += $diff / 4
            if $yy + 1 < $height and $xx + 1 < $width;

        # 計算結果は、カラーテーブルの番号として格納する。
        $data->[$yy][$xx] = $dest == 0 ? 0 : 255;

    }

}

# カラーテーブルを定義。0 = 黒、1 = 白。
my @colors = (0x00000000, 0x00ffffff);

# 白黒の BMP を書き出す。
write_bmp($data, 1, \@colors);


結果は、かなり良好となった。

あえて文句を言えば、1, 1, 1, 1 の責任分担は、
縦横に同じ色が続くパターンが少し目立つ。
画像の真ん中あたり、迷路のような模様が見えるだろうか。

分散の配列を変えることによって微妙な品質の差が出るのだ。
明日は、精度が高いと言われる有名な配列を紹介しよう。

参考として、グラデーションの写真も載せておく。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.