## 自己紹介
- 仕事: .NETでGUIアプリ
- Twitter: @techno_neko
- 所属: Hokkaido.pm
## 例えば・・・?
- 車のハンドルと実際の動き
- 電子楽器
- 画像の輪郭抽出
## アクセスログに使うと・・・?
- スパイク(突発的なアクセス)の除去
- スパイクの生じた日時の抽出
(位相のずれを考えると、FIRの方が良いかと・・・。)
スパイク(突発的なアクセス)の除去
低域通過フィルタ(長い周期の信号を通過させる)
スパイクの生じた日時の抽出
高域通過フィルタ(短い周期の信号を通過させる)
## これ、どうやって描いたの?
- Imager
- Cassis
https://github.com/techno-cat/p5-Cassis
## Cassisを使って、ディタルフィルタが学べる!!1
## 試してみよう!
1. 波形の生成
1. 波形の足し算
1. フィルタを通す
## その前に
- 波形を描いてみる
- WAVファイルで出力
## 波形を描いてみる(1/5)
グラフの余白をあらかじめ決めると、後が楽になる
```perl
# 目盛の色
use constant TICK_COLOR => Imager::Color->new( 80, 80, 80 );
use constant TICK_X => 20; # x軸方向の目盛の間隔
use constant TICK_Y => 50; # y軸方向の目盛の間隔
use constant STEP_X => 5; # プロット間隔
use constant MARGIN => 20; # グラフ領域と画像サイズの隙間
use constant N => 80; # プロット数
```
## 波形を描いてみる(2/5)
原点の計算を済ませると描画処理が簡単になる
```perl
# グラフ領域の計算
my $graph_width = (STEP_X * N) + 1;
my $graph_height = 200;
# 原点
my $x0 = MARGIN;
my $y0 = MARGIN + $graph_height - 1;
# 波形データの生成
my @src = map {
0.5 + sin($_ * 0.1) * 0.25
} 0..(N - 1);
draw_graph( 'my_graph.png', \@src );
```
## 波形を描いてみる(3/5)
グラフ描画の手続き
```perl
sub draw_graph {
my ( $path, $src ) = @_;
my $img = Imager->new(
xsize => MARGIN + $graph_width + MARGIN,
ysize => MARGIN + $graph_height + MARGIN );
$img->box( filled => 1, color => 'black' );
draw_graduation( $img, TICK_COLOR );
draw_wave( $img, \@src, 'red' );
$img->write( file => $path ) or die $img->errstr;
}
```
## 波形を描いてみる(4/5)
目盛を描画
```perl
sub draw_graduation {
my ( $img, $color ) = @_;
my $x = 0;
while ( $x <= $graph_width ) {
$img->line( color => $color,
x1 => $x0 + $x, y1 => $y0,
x2 => $x0 + $x, y2 => $y0 - $graph_height - 1 );
$x += (TICK_X * STEP_X);
}
my $y = 0;
while ( $y <= $graph_height ) {
$img->line( color => $color,
x1 => $x0, y1 => $y0 - $y,
x2 => $x0 + $graph_width - 1, y2 => $y0 - $y );
$y += TICK_Y;
}
}
```
## 波形を描いてみる(5/5)
データをプロット
```perl
sub draw_wave {
my ( $img, $data, $color ) = @_;
my $y0 = int($img->getheight() / 2);
my $xmax = scalar(@{$data}) - 1;
my @points = map {
my $gain = TICK_Y * $data->[$_];
[ $_, $y0 - int(($gain < .0) ? ($gain - .5) : ($gain + .5)) ];
} 0..$xmax;
$img->polyline( points => \@points, color => $color );
}
```
## WAVファイルで出力
```perl
use Cassis;
use Math::Trig ':pi';
use constant SAMPLING_RATE => 44100;
my @src = map { sin(440 * 2.0 * pi * ($_ / SAMPLING_RATE)) } 0..(SAMPLING_RATE - 1);
Cassis::File::write(
file => '440.wav',
sf => SAMPLING_RATE,
channels => [ \@src ] );
```
## 波形の生成(1/2)
```perl
{
my $osc1_out = Cassis::Osc::Pulse->new( fs => N, freq => 2 )->exec( num => N );
my $osc2_out = Cassis::Osc::Sin->new( fs => N, freq => 4 )->exec( num => N );
my $img = Imager->new(
xsize => MARGIN + $graph_width + MARGIN,
ysize => MARGIN + $graph_height + 100 + MARGIN );
$img->box( filled => 1, color => 'black' );
draw_graduation( $img, TICK_COLOR );
draw_wave( $img, $osc1_out, 'red' );
draw_wave( $img, $osc2_out, 'green' );
$img->write( file => '01.png' ) or die $img->errstr;
}
```
波形の生成(2/2)
## 波形の足し算(1/2)
```perl
{
my $osc1_out = Cassis::Osc::Pulse->new( fs => N, freq => 2 )->exec( num => N );
my $osc2_out = Cassis::Osc::Sin->new( fs => N, freq => 4 )->exec( num => N );
my $mixer_out = Cassis::Mixer::mix(
{ src => $osc1_out, volume => 0.2 },
{ src => $osc2_out, volume => 0.1 }
);
my $img = Imager->new(
xsize => MARGIN + $graph_width + MARGIN,
ysize => MARGIN + $graph_height + 100 + MARGIN );
$img->box( filled => 1, color => 'black' );
draw_graduation( $img, TICK_COLOR );
draw_wave( $img, $mixer_out, 'orange' );
draw_wave( $img, $osc1_out, 'red' );
draw_wave( $img, $osc2_out, 'green' );
$img->write( file => '02.png' ) or die $img->errstr;
}
```
波形の足し算(2/2)
## フィルタを通す(1/2)
```perl
{
my $osc1_out = Cassis::Osc::Pulse->new( fs => N, freq => 2 )->exec( num => N );
my $osc2_out = Cassis::Osc::Sin->new( fs => N, freq => 4 )->exec( num => N );
my $mixer_out = Cassis::Mixer::mix(
{ src => $osc1_out, volume => 0.2 },
{ src => $osc2_out, volume => 0.1 }
);
# "linear => 1"を設定すると、一般的な方法で計算が使われる(音楽用途だと使いにくい)
my $q = 1.0 / sqrt(2.0);
my $lpf = Cassis::Iir2::LPF->new( cutoff => 0.04, q => $q, linear => 1 );
my $hpf = Cassis::Iir2::HPF->new( cutoff => 0.015, q => $q, linear => 1 );
my $img = Imager->new(
xsize => MARGIN + $graph_width + MARGIN,
ysize => MARGIN + $graph_height + 100 + MARGIN );
$img->box( filled => 1, color => 'black' );
draw_graduation( $img, TICK_COLOR );
draw_wave( $img, $mixer_out, 'orange' );
draw_wave( $img, $lpf->exec( src => $mixer_out ), 'red' );
draw_wave( $img, $hpf->exec( src => $mixer_out ), 'green' );
$img->write( file => '03.png' ) or die $img->errstr;
}
```
フィルタを通す(2/2)
## まとめ
- 使い道はいろいろ
- Cassisは難しい
- でも、Imagerは便利だよ!
## おまけ
アクセスログのグラフに使ったスクリプト
```perl
#!perl
use strict;
use warnings;
use Cassis;
use Imager;
use constant TICK_COLOR => Imager::Color->new( 80, 80, 80 );
use constant TICK_X => 20;
use constant TICK_Y => 50;
use constant STEP_X => 5;
use constant MARGIN => 20;
use constant N => 80;
my $graph_width = (STEP_X * N) + 1;
my $graph_height = 200;
my $x0 = MARGIN;
my $y0 = MARGIN + $graph_height - 1;
my @src = map {
(sin($_ * 0.1) * 0.01) + ($_ * 0.005);
} 0..(N - 1);
# スパイクの追加
$src[20] += 0.3;
$src[50] += 0.1;
$src[60] += 0.2;
my $q = 1.0 / sqrt(2.0);
{
my $f = Cassis::Iir2::LPF->new( cutoff => 0.04, q => $q, linear => 1 );
my $dst = $f->exec( src => \@src );
draw_graph( 'sample_lpf.png', \@src, $dst );
}
{
my $f = Cassis::Iir2::HPF->new( cutoff => 0.015, q => $q, linear => 1 );
my $dst = $f->exec( src => \@src );
draw_graph( 'sample_hpf.png', \@src, $dst );
}
sub draw_graph {
my ( $path, $src, $dst ) = @_;
my $img = Imager->new(
xsize => MARGIN + $graph_width + MARGIN,
ysize => MARGIN + $graph_height + MARGIN );
$img->box( filled => 1, color => 'black' );
draw_graduation( $img, TICK_COLOR );
draw_wave( $img, \@src, 'red' );
draw_wave( $img, $dst, 'green' );
$img->write( file => $path ) or die $img->errstr;
}
sub draw_graduation {
my ( $img, $color ) = @_;
my $x = 0;
while ( $x <= $graph_width ) {
$img->line( color => $color,
x1 => $x0 + $x, y1 => $y0,
x2 => $x0 + $x, y2 => $y0 - $graph_height - 1 );
$x += (TICK_X * STEP_X);
}
my $y = 0;
while ( $y <= $graph_height ) {
$img->line( color => $color,
x1 => $x0, y1 => $y0 - $y,
x2 => $x0 + $graph_width - 1, y2 => $y0 - $y );
$y += TICK_Y;
}
}
sub draw_wave {
my ( $img, $data, $color ) = @_;
my $y1;
my $n = scalar( @{$data} );
for (my $i=0; $i<$n; $i++) {
my $x = $x0 + ($i * STEP_X);
my $y = $y0 - ($graph_height * $data->[$i]);
$y = int( ($y < .0) ? ($y - .5) : ($y + .5) );
if ( 0 < $i ) {
my $x1 = $x - STEP_X;
$img->line(
x1 => $x1, y1 => $y1,
x2 => $x , y2 => $y,
color => $color )
}
$img->box(
xmin => $x - 1, ymin => $y - 1, xmax => $x + 1, ymax => $y + 1,
color => $color, filled => 0 );
$y1 = $y;
}
}
```