2006 年 2 月 9 日 22 時 36 分

パターン法による 2 値化


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


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


昨日までの考え方では、明暗のバランスは取れても、
中間値のピクセルを上手く表現できなかった。
白か黒のどちらかに強制的に変換されるので、
どうしても違和感が残っていた。

今日は中間値を上手く表現することを考えてみる。
中間値のピクセルを表現するとき、ピクセル単位で考えず、
周辺を含む範囲のピクセルに対して処理をし、
白と黒を上手く組み合わせて使えば、
視覚的に中間値をつくれないだろうか。

例えば、4 x 4 のブロックで表現することを考えてみる。
以下のようなパターンを使えば、17 種類の濃淡を扱える。

■■■■ □■■■ □■■■ □■□■ □■□■
■■■■ ■■■■ ■■■■ ■■■■ ■■■■
■■■■ ■■■■ ■■□■ ■■□■ □■□■
■■■■ ■■■■ ■■■■ ■■■■ ■■■■

□■□■ □■□■ □■□■ □■□■ □□□■
■□■■ ■□■■ ■□■□ ■□■□ ■□■□
□■□■ □■□■ □■□■ □■□■ □■□■
■■■■ ■■■□ ■■■□ ■□■□ ■□■□

□□□■ □□□□ □□□□ □□□□ □□□□
■□■□ ■□■□ ■□■□ □□■□ □□■□
□■□□ □■□□ □□□□ □□□□ □□□□
■□■□ ■□■□ ■□■□ ■□■□ ■□□□

□□□□ □□□□
□□□□ □□□□
□□□□ □□□□
■□□□ □□□□

全て黒から、1 つずつ白に変えていくことで、
17 種類の濃度を仮想的に表現できるわけだ。
画面から思い切り目を離してみれば、濃淡に見えるかも。


では、この考え方を基準にして処理を考える。
まず、上のパターンには明るさ順に番号を振っておこう。

0, 1, 2, 3, 4
5, 6, 7, 8, 9
10, 11, 12, 13, 14
15, 16

そして、上のパターンの中で、
黒から白に変わる直前の番号を
4 x 4 の配列として表現すると以下のようになる。

0,  8,  2, 10
12,  4, 14,  6
3, 11,  1,  9
15,  7, 13,  5

例えば、パターンの一番左下は、5 では黒だが、
6 では白になるので、5 となる。
これをパターン配列と呼ぶことにする。

この配列の意味するところは閾値に似ている。
ピクセルの明度を 0~16 までの 17 段階に分類し、
パターン配列と明度を比較して、
パターン配列値が明度未満ならば白、
以上であれば黒とすれば、上の濃淡が再現できる。

実装は、まず画像全体のピクセルの明度を
0~16 までの 17 段階に分類する。
次に画像全体を 4 x 4 ブロックに分割して、
ブロックごとにパターン配列を当てはめて、
上の計算により白か黒かを決めるとよさそうだ。


# 4 x 4 のパターン配列。
my $pattern = [
    [  0,  8,  2, 10, ],
    [ 12,  4, 14,  6, ],
    [  3, 11,  1,  9, ],
    [ 15,  7, 13,  5, ],
];

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

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

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

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

        # ピクセル値取得。
        my ($red, $green, $blue) = unpack_rgb($data->[$yy][$xx]);

        no integer;

        # まずはグレースケールの計算を使って 256 段階に。
        my $lv = $red * 0.299 + $green * 0.587 + $blue * 0.114;

        # 次いで、17 段階に変換して、0~16 の整数値にしておく。
        $lv = int($lv * 16 / 255);

        # パターン配列の該当箇所を探し、閾をみて白か黒か決める。
        $lv = $lv > $pattern->[$yy & 0x03][$xx & 0x03] ? 1 : 0;

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

    }

}

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

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


結果は、元の画像のイメージは伝わりやすくなったが、
格子状のパターンが目立つ結果になった。

まあ、これは全ての処理について言えることだが、
対象画像が小さいほど、顕著に現れるので、
本当はもっと大きい画像で試してほしい。

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



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