Perlでジュリア集合を描く(後編)
だいぶ日が空いちゃったけど、それとなくレンダリングしてみた。
元々、Androidアプリのために書いたスクリプトだったけど、
トーンカーブ相当の機能を実現できるので流用してみた。
package ToneCurve;
use v5.14;
use strict;
use warnings;
sub calc_curve {
my ( $depth_src, $depth_dst, $dx0, $dx1, $dy0, $dy1, $y_max ) = @_;
die 'dx0 must be zero or positive number.' if ( $dx0 < 0 );
die 'dx1 must be zero or positive number.' if ( $dx1 < 0 );
my $p0 = [ 0.0 , 0.0 ];
my $p1 = [ 0.0 + $dx0, 0.0 + ($dx0 * $dy0) ];
my $p2 = [ 1.0 - $dx1, $y_max - ($dx1 * $dy1) ];
my $p3 = [ 1.0 , $y_max ];
my $rshift = $depth_src - $depth_dst;
my $x_max = int( 2 ** $depth_src ) - 1;
my $n = int( 2 ** $depth_src ); # 目安は出力する数くらい
my @dst = ();
my $x = 0;
for (my $i=1; $i<=$n; $i++) {
my $t = $i / $n;
my $xi = $p0->[0] * ((1 - $t) ** 3)
+ $p1->[0] * 3 * $t * ((1 - $t) ** 2)
+ $p2->[0] * 3 * ($t ** 2) * (1 - $t)
+ $p3->[0] * ($t ** 3);
$xi = int( $xi * $x_max );
if ( $x <= $xi ) {
my $yi = $p0->[1] * ((1 - $t) ** 3)
+ $p1->[1] * 3 * $t * ((1 - $t) ** 2)
+ $p2->[1] * 3 * ($t ** 2) * (1 - $t)
+ $p3->[1] * ($t ** 3);
$yi = int( $yi * $x_max );
$yi = ( $yi < 0 ) ? 0 : (($x_max < $yi) ? $x_max : $yi);
my $y = $yi >> $rshift;
for (; $x<=$xi; $x++) {
push @dst, $y;
}
}
}
return \@dst;
}
package main;
use v5.14;
use strict;
use warnings;
use List::Util qw/max/;
use Imager;
use Time::HiRes qw/time/;
use constant DEPTH_SRC => 10;
use constant DEPTH_DST => 8;
if ( scalar(@ARGV) != 1 ) {
say "Usage: perl $0 [dat file]";
exit 0;
}
my $src_file = $ARGV[0];
my ( $width, $height ) = $src_file =~ /_(\d+)_(\d+)\.dat/;
say "w: " . $width;
say "h: " . $height;
my @pixels = ();
open( my $fh, '<', $src_file ) or die "cannot open $src_file : $!";
binmode( $fh );
for ( 1..$height ) {
my $buf;
my $result = read( $fh, $buf, ($width * 2) );
if ( $result != ($width * 2) ) {
close( $fh );
die "read faild!";
}
my @tmp = unpack( 's*', $buf );
push @pixels, \@tmp;
}
close( $fh );
my $val_max = max( map { max @{$_}; } @pixels );
my $src_max = (2 ** DEPTH_SRC) - 1;
my $dst_max = (2 ** DEPTH_DST) - 1;
my $curve = ToneCurve::calc_curve(
DEPTH_SRC, DEPTH_DST, 0.4, 0.9, 0.0, 0.0, 1.0 );
my $img = Imager->new(
xsize => $width, ysize => $height );
$img->box( filled => 1, color => 'black' );
local $| = 1;
my $start = time();
printf( "%4d/%4d", 1, $height );
for (my $iy=0; $iy<$height; $iy++) {
my @rgba = map {
my $tmp = int( ($_ / $val_max) * $src_max );
my $v = ( $src_max < $tmp ) ? $curve->[-1] : $curve->[$tmp];
( $v, $v, $v, 255 );
} @{$pixels[$iy]};
$img->setscanline( y => $iy, pixels => pack('C*', @rgba) );
printf( "\r%4d/%4d", $iy + 1, $height );
}
printf( "\rcomplete! %.2fsec\n", (time() - $start) );
my $dst_file = ($src_file =~ s/\.dat//r) . '.png';
$img->write( file => $dst_file ) or die $img->errstr;
実行方法はこんな感じ。
$ perl aaa.pl hoge.dat
まず、Bスプライン曲線を利用してトーンカーブを生成する。
次に、0.0から1.0に正規化したデータを、
トーンカーブに従って0〜255に変換している。
本当はデータに合わせてトーンカーブを設定する必要があるけど、
とりあえず、この設定を使い回しても、それっぽくレンダリングされる。
前回のデータをレンダリングするとこんな感じ。
おしまい。




Leave a Comment