このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
今日は 3 ビット 8 色使ってみよう。
8 色あると、色付き減色への道が開ける。
白黒の場合、明度値という 1 次元の値だけ見ればよかった。
基本を 8 ビットで考えた場合、0~255 までの値しかない。
直線定規の目盛りのように考えることができる。
しかし、RGB カラーとなると 3 次元の世界になる。
RGB それぞれに独立した明度があるため、
単純に定規は表現できず、3 次元空間で考える必要がある。
数学でやった、XYZ の 3 次元座標空間を考えればいい。
ぞれを、RGB に置き換えれば色空間の完成だ。
ルービックキューブやさいころなどを持っている場合は、
それを眺めてみればイメージが沸くかもしれない。
8 ビットの場合、各座標は 0~255 の範囲の整数値を取る。
色は、空間内の任意の点として表現できるのだ。
フルカラーの画像の場合、空間内の点が自由に使えるが、
カラーテーブルを使った画像の場合、
空間内でいくつかの点を決めて、
その点のみを使って画像を構成することになる。
今日は 3 ビットなので 8 色しかない。
RGB 色空間は立方体なので頂点が 8 個ある。
丁度それらを使って色を割り当てることにしよう。
RGB それぞれが 0 か 255 の値を取るので、
色要素ごとに独立して 2 値化の計算を行うことができる。
# BMP 読み込み。
my $data = read_bmp;
# 32 ビット整数値から RGB 配列に分解しておく。
foreach my $line (@$data) {
foreach my $pixel (@$line) {
$pixel = [ unpack_rgb($pixel) ];
}
}
# 高さと幅を取得。
my $height = @$data;
my $width = @{$data->[0]};
# 行ごとの処理。
for (my $yy = 0; $yy < $height; ++$yy) {
# ピクセルごとの処理。
for (my $xx = 0; $xx < $width; ++$xx) {
# 色要素ごとの処理。
for (my $ee = 0; $ee < 3; ++$ee) {
# 元の明度値 0~255 に対して、
my $src = $data->[$yy][$xx][$ee];
# 128 を閾値として最明値か最暗値か決める。
my $dest = $src >= 128 ? 255 : 0;
# 本来の値との不足分を求め、
my $diff = $src - $dest;
# 右、左下、下、右下のピクセルに責任転嫁する。
# Floyd & Steinberg による誤差配列。
# - - -
# - x 7
# 3 5 1
$data->[$yy][$xx + 1][$ee] += $diff * 7 / 16
if $xx + 1 < $width;
$data->[$yy + 1][$xx - 1][$ee] += $diff * 3 / 16
if $yy + 1 < $height and $xx - 1 >= 0;
$data->[$yy + 1][$xx][$ee] += $diff * 5 / 16
if $yy + 1 < $height;
$data->[$yy + 1][$xx + 1][$ee] += $diff * 1 / 16
if $yy + 1 < $height and $xx + 1 < $width;
# 計算結果は、0 か 1 として格納する。
$data->[$yy][$xx][$ee] = $dest == 0 ? 0 : 1;
}
}
}
# 0-1 配列からカラーテーブルインデックスに変換する。
# $red | $blue << 1 | $green << 2 で求められる。
foreach my $line (@$data) {
foreach my $px (@$line) {
$px = $px->[0] | $px->[1] << 1 | $px->[2] << 2;
}
}
# カラーテーブルを定義。
my @colors = ();
foreach my $bb (0, 255) {
foreach my $gg (0, 255) {
foreach my $rr (0, 255) {
push(@colors, pack_rgb($rr, $gg, $bb));
}
}
}
# 残り 8 色は未使用。
push(@colors, (0) x 8);
# 4 ビットの BMP を書き出す。
write_bmp($data, 4, \@colors);
誤差拡散がうまく働いていることが分かる。
RGB それぞれは 0 か 255 しか取っていないにも拘らず、
元の色合いが想像できる位の画質となってるのは驚きだ。
誤差拡散による色の散らばりが激しいので、
写真は JPEG の最高品質で出力している。
(でないと劣化してしまうため)
なので、環境によっては表示できないかもしれない。