2006 年 2 月 22 日 22 時 54 分

任意のカラーテーブルを使った減色


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


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


さて、昨日の考察を元に、減色をしてみよう。

適当なカラーテーブルが必要なので、某ペイントソフトで、
256 カラーテーブルを出力してみた。
テーブルの色配置は、今回の画像に合っているとは言えないが、
テストとして丁度いいので使ってみよう。


# 適当なカラーテーブルを準備する。
my $colors = [
    00000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080,
    0x008080, 0x808080, 0xc0dcc0, 0xa6caf0, 0x2a3faa, 0x2a3fff,
    0x2a5f00, 0x2a5f55, 0x2a5faa, 0x2a5fff, 0x2a7f00, 0x2a7f55,
    0x2a7faa, 0x2a7fff, 0x2a9f00, 0x2a9f55, 0x2a9faa, 0x2a9fff,
    0x2abf00, 0x2abf55, 0x2abfaa, 0x2abfff, 0x2adf00, 0x2adf55,
    0x2adfaa, 0x2adfff, 0x2aff00, 0x2aff55, 0x2affaa, 0x2affff,
    0x550000, 0x550055, 0x5500aa, 0x5500ff, 0x551f00, 0x551f55,
    0x551faa, 0x551fff, 0x553f00, 0x553f55, 0x553faa, 0x553fff,
    0x555f00, 0x555f55, 0x555faa, 0x555fff, 0x557f00, 0x557f55,
    0x557faa, 0x557fff, 0x559f00, 0x559f55, 0x559faa, 0x559fff,
    0x55bf00, 0x55bf55, 0x55bfaa, 0x55bfff, 0x55df00, 0x55df55,
    0x55dfaa, 0x55dfff, 0x55ff00, 0x55ff55, 0x55ffaa, 0x55ffff,
    0x7f0000, 0x7f0055, 0x7f00aa, 0x7f00ff, 0x7f1f00, 0x7f1f55,
    0x7f1faa, 0x7f1fff, 0x7f3f00, 0x7f3f55, 0x7f3faa, 0x7f3fff,
    0x7f5f00, 0x7f5f55, 0x7f5faa, 0x7f5fff, 0x7f7f00, 0x7f7f55,
    0x7f7faa, 0x7f7fff, 0x7f9f00, 0x7f9f55, 0x7f9faa, 0x7f9fff,
    0x7fbf00, 0x7fbf55, 0x7fbfaa, 0x7fbfff, 0x7fdf00, 0x7fdf55,
    0x7fdfaa, 0x7fdfff, 0x7fff00, 0x7fff55, 0x7fffaa, 0x7fffff,
    0xaa0000, 0xaa0055, 0xaa00aa, 0xaa00ff, 0xaa1f00, 0xaa1f55,
    0xaa1faa, 0xaa1fff, 0xaa3f00, 0xaa3f55, 0xaa3faa, 0xaa3fff,
    0xaa5f00, 0xaa5f55, 0xaa5faa, 0xaa5fff, 0xaa7f00, 0xaa7f55,
    0xaa7faa, 0xaa7fff, 0xaa9f00, 0xaa9f55, 0xaa9faa, 0xaa9fff,
    0xaabf00, 0xaabf55, 0xaabfaa, 0xaabfff, 0xaadf00, 0xaadf55,
    0xaadfaa, 0xaadfff, 0xaaff00, 0xaaff55, 0xaaffaa, 0xaaffff,
    0xd40000, 0xd40055, 0xd400aa, 0xd400ff, 0xd41f00, 0xd41f55,
    0xd41faa, 0xd41fff, 0xd43f00, 0xd43f55, 0xd43faa, 0xd43fff,
    0xd45f00, 0xd45f55, 0xd45faa, 0xd45fff, 0xd47f00, 0xd47f55,
    0xd47faa, 0xd47fff, 0xd49f00, 0xd49f55, 0xd49faa, 0xd49fff,
    0xd4bf00, 0xd4bf55, 0xd4bfaa, 0xd4bfff, 0xd4df00, 0xd4df55,
    0xd4dfaa, 0xd4dfff, 0xd4ff00, 0xd4ff55, 0xd4ffaa, 0xd4ffff,
    0xff0055, 0xff00aa, 0xff1f00, 0xff1f55, 0xff1faa, 0xff1fff,
    0xff3f00, 0xff3f55, 0xff3faa, 0xff3fff, 0xff5f00, 0xff5f55,
    0xff5faa, 0xff5fff, 0xff7f00, 0xff7f55, 0xff7faa, 0xff7fff,
    0xff9f00, 0xff9f55, 0xff9faa, 0xff9fff, 0xffbf00, 0xffbf55,
    0xffbfaa, 0xffbfff, 0xffdf00, 0xffdf55, 0xffdfaa, 0xffdfff,
    0xffff55, 0xffffaa, 0xccccff, 0xffccff, 0x33ffff, 0x66ffff,
    0x99ffff, 0xccffff, 0x007f00, 0x007f55, 0x007faa, 0x007fff,
    0x009f00, 0x009f55, 0x009faa, 0x009fff, 0x00bf00, 0x00bf55,
    0x00bfaa, 0x00bfff, 0x00df00, 0x00df55, 0x00dfaa, 0x00dfff,
    0x00ff55, 0x00ffaa, 0x2a0000, 0x2a0055, 0x2a00aa, 0x2a00ff,
    0x2a1f00, 0x2a1f55, 0x2a1faa, 0x2a1fff, 0x2a3f00, 0x2a3f55,
    0xfffbf0, 0xa0a0a4, 0x808080, 0xff0000, 0x00ff00, 0xffff00,
    0x0000ff, 0xff00ff, 0x00ffff, 0xffffff,
];

# 近さを求める。大小の比較だけにしか使わないので、
# 距離の計算の平方根処理を行わない。整数演算で高速化できる。
sub get_nearness ($$) {
    my ($pp, $qq) = @_;
    ($pp->[0] - $qq->[0]) ** 2
      + ($pp->[1] - $qq->[1]) ** 2
      + ($pp->[2] - $qq->[2]) ** 2;
}

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

# 32 ビット整数値から RGB 配列に分解しておく。
foreach my $line (@$data) {
    foreach my $pixel (@$line) {
        $pixel = [ unpack_rgb($pixel) ];
    }
}

# カラーテーブルも RGB 配列に分解しておく。
my $colors_rgb = [ map { [ unpack_rgb($_) ] } @$colors ];

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

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

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

        # ピクセルの RGB 値配列を得る。
        my $rgb = $data->[$yy][$xx];

        # 最も近い色を探す。

        # まずは 0 番と仮定しておき、
        my $index = 0;
        my $best = get_nearness($rgb, $colors_rgb->[0]);

        # 最適なものを検索する。
        foreach (1 .. $#$colors) {
            my $nn = get_nearness($rgb, $colors_rgb->[$_]);
            if ($nn < $best) {
                $best = $nn;
                $index = $_;
            }
        }

        # 誤差分散処理。
        foreach (0 .. 2) {

            # 本来の値との不足分を求め、
            my $diff = $rgb->[$_] - $colors_rgb->[$index][$_];

            # 右、左下、下、右下のピクセルに責任転嫁する。

            # Floyd & Steinberg による誤差配列。

            # - - -
            # - x 7
            # 3 5 1

            $data->[$yy][$xx + 1][$_] += $diff * 7 / 16
                if $xx + 1 < $width;

            $data->[$yy + 1][$xx - 1][$_] += $diff * 3 / 16
                if $yy + 1 < $height and $xx - 1 >= 0;

            $data->[$yy + 1][$xx][$_] += $diff * 5 / 16
                if $yy + 1 < $height;

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

        }

        # カラーテーブルの番号に置き換える。
        $data->[$yy][$xx] = $index;

    }

}

# 8 ビットの BMP を書き出す。
write_bmp($data, 8, $colors);


さすがに計算量が多いため、 Perl では厳しくなってきた。
Web セーフカラーのときと比較して写真を見ると、
地面は悪くなっているが、台座の部分は良くなっている。
つまり、パレットによる差が出ているということだ。



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