このアーカイブは同期化されません。 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 の責任分担は、
縦横に同じ色が続くパターンが少し目立つ。
画像の真ん中あたり、迷路のような模様が見えるだろうか。
分散の配列を変えることによって微妙な品質の差が出るのだ。
明日は、精度が高いと言われる有名な配列を紹介しよう。
参考として、グラデーションの写真も載せておく。