Perlで窓関数を描く
FIRフィルタとか使う時に必要なので、描いてみた。
use v5.14; use strict; use warnings; use Imager; use Math::Trig qw/pi/; use constant STEP_X => 4; use constant SCALE_Y => 256.0; use constant N => 101; my ( $width, $height ) = ( 601, 400 ); my ( $x0, $y0 ) = ( int($width / 2), ($height - 50) ); { my $img = Imager->new( xsize => $width, ysize => $height, channels => 4 ); draw_graduation( $img, Imager::Color->new(64,64,64) ); draw_window( $img, kaiser_window(N, 0.000), 'red' ); draw_window( $img, kaiser_window(N, 3.395), 'green' ); draw_window( $img, kaiser_window(N, 5.653), 'orange' ); draw_window( $img, kaiser_window(N, 7.857), 'yellow' ); $img->write( file => sprintf('kaiser_%02d.png', N) ) or die $img->errstr; } { my $img = Imager->new( xsize => $width, ysize => $height, channels => 4 ); draw_graduation( $img, Imager::Color->new(64,64,64) ); draw_window( $img, hamming_window(N), 'red' ); draw_window( $img, hann_window(N), 'green' ); draw_window( $img, kaiser_window(N, 7.857), 'yellow' ); $img->write( file => sprintf('window_%02d.png', N) ) or die $img->errstr; } sub kaiser_window { my ( $n, $alpha ) = @_; my @ret = map { bessel( $alpha * sqrt(1.0 - ($_ ** 2.0)) ) / bessel( $alpha ); } map { ($_ - int($n / 2)) / int($n / 2); } 0..($n - 1); return \@ret; } sub hann_window { my $n = shift; my @ret = map { my $x = 2.0 * pi() * $_; 0.5 + (0.5 * cos($x)); } map { ($_ - int($n / 2)) / ($n - 1); } 0..($n - 1); return \@ret; } sub hamming_window { my $n = shift; my @ret = map { my $x = 2.0 * pi() * $_; 0.54 + (0.46 * cos($x)); } map { ($_ - int($n / 2)) / ($n - 1); } 0..($n - 1); return \@ret; } sub bessel { my $x = shift; my $total = 1; my $factorial = 1; foreach my $n ( 1..20 ) { $factorial *= $n; my $tmp = (($x / 2) ** $n) / $factorial; $total += ($tmp * $tmp); } return $total; } sub draw_window { my ( $img, $data, $color ) = @_; my $img_tmp = Imager->new( xsize => $img->getwidth(), ysize => $img->getheight(), channels => 4 ); my $n = scalar( @{$data} ); for (my $i=0; $i<$n; $i++) { my $dx = $i - int($n / 2); my $dy = int($data->[$i] * SCALE_Y); $img_tmp->line( x1 => ($x0 + ($dx * STEP_X)), y1 => $y0, x2 => ($x0 + ($dx * STEP_X)), y2 => ($y0 - $dy), color => $color ); } $img->compose( src => $img_tmp, opacity => 0.25, combine => 'add' ); for (my $i=0; $i<$n; $i++) { my $dx = $i - int($n / 2); my $dy = int($data->[$i] * SCALE_Y); my ( $x, $y ) = ( $x0 + ($dx * STEP_X), $y0 - $dy ); $img->box( xmin => $x - 1, ymin => $y - 1, xmax => $x + 1, ymax => $y + 1, color => $color, filled => 0 ); } } sub draw_graduation { my ( $img, $color ) = @_; $img->box( filled => 1, color => 'black' ); $img->line( x1 => $x0, y1 => $y0, x2 => $x0, y2 => 0, color => $color ); $img->line( x1 => 0, y1 => $y0, x2 => ($width - 2), y2 => $y0, color => $color ); }
カイザー窓の元ネタはこの本だけど、ハン窓とハミング窓は調べて追加した。
ハミング窓(赤)、ハン窓(緑)、カイザー窓(黄)はこんな感じ。
で、さっきの本だとFIRフィルタの係数を算出できなくて困ったので、
家にある適当な本を使って算出した。
たぶん、sinc関数を使うべきところで、sinc関数に触れてなかったので、
最初はソースコードが載っている入門本が良いと思う。
追記 2016/06/04
ソースコードの59行目と72行目の分母が間違ってたので修正しました。
なので、2枚目のグラフも差し替えました。
— ここまで追記 —
おしまい。
Leave a Comment