このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
今まで出てきた画像処理をコード化してみよう。
BMP を読み込み、処理し、BMP を書き出す流れとなる。
まずは、BMP を読み込むルーチンを作ろう。
処理の特性上、24、32 ビットの BMP に限定することとする。
面倒なので、標準入力から BMP を読み込むこととしよう。
RGB を独立してデータを持たせると非常に大きくなるので、
1 ピクセルは、RGB を格納した 32 ビット値とし、
1 行は、ピクセルの配列とする。
データ全体は、行のリファレンスの配列としよう。
つまり、関数は戻り値として、行配列のリファレンスを返す。
my $data = read_bmp;
こうしておけば、scalar(@$data) で画像の高さが、
$data->[0] で先頭行へのリファレンスが、
scalar(@{$data->[0]}) で、先頭行の幅(=画像の幅)が、
$data->[0][0] で、左上のピクセル値が取り出せるわけだ。
sub read_bmp () {
use integer;
my $chunk;
binmode(STDIN);
# BITMAPFILEHEADER を読み込む。
read STDIN, $chunk, 14 or die $!;
my ($signature, $data_offset) = unpack('a2x8V', $chunk);
# フォーマットのチェック。
$signature eq 'BM' or die 'Invalid BITMAP.';
# BITMAPINFOHEADER を読み込む。
read STDIN, $chunk, 40 or die $!;
my ($bih_size, $width, $height, $planes, $bit_count, $compression) = unpack('VVVvvVx20', $chunk);
# フォーマットのチェック。
$planes == 1 or die 'Invalid BITMAP.';
$bit_count == 24 or $bit_count == 32 or die 'Not supported bit count.';
$compression == 0 or die 'Not supported compression.';
# 残りのヘッダを無視し、データ部まで読み飛ばす。
if ($data_offset - 54) {
read STDIN, $chunk, $data_offset - 54 or die $!;
}
# データ読み取り開始。
my @data = ();
# ピクセルデータのサイズ+詰め物サイズ=行のサイズ。
my $part_size = $width * $bit_count / 8;
my $padding_size = (-$part_size) & 0x3;
# 解説していなかったが、高さが負の場合、
# トップダウンの BMP である。(上から下に格納)
my $is_top_down = $height < 0;
# 高さを正の値に直しておく。
$height = -$height if $is_top_down;
foreach (1 .. $height) {
# ピクセルデータ読み出し。
read STDIN, $chunk, $part_size or die $!;
# 1 行を RGB の 32 ビット整数配列にデコード。
my $line;
if ($bit_count == 32) {
# 32 ビットなので自然にデコードできる。
$line = [ unpack('V*', $chunk) ];
} else {
$line = [];
# 3 バイトずつ切り出し、最後に 0 のバイトを
# つけて 32 ビット長とし、デコード。
push @$line, unpack('V', $& . "\0")
while $chunk =~ /.{3}/sg;
}
if ($is_top_down) {
# トップダウンの場合は先頭行から。
push @data, $line;
} else {
# ボトムアップ(通常)の場合は末尾行から。
unshift @data, $line;
}
# 詰め物を読み捨てる。
if ($padding_size) {
read STDIN, $chunk, $padding_size or die $!;
}
}
\@data;
}
ネガポジ反転なら、以下のように書ける様になる。
use integer;
my $data = read_bmp;
foreach my $line (@$data) {
foreach my $pixel (@$line) {
my ($red, $green, $blue) =
unpack('xCCC', pack('N', $pixel));
$red = 255 - $red;
$green = 255 - $green;
$blue = 255 - $blue;
$pixel =
unpack('N', pack('xCCC', $red, $green, $blue));
}
}