スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書く事で広告が消せます。

「Image::MagickとFile::Find::Ruleを用いて簡単一括サムネイル作成」の続き

 以前書いたAutoResize.pmですが、サムネイルのサイズ指定やらファイル名等をコールバックで指定出来るようにすると便利なんじゃない?と思ったので息抜きがてらに修正。あとはFile::Find::RuleからFile::Find::Rule::MMagicに切り替えたので、MIMEタイプでファイルを指定出来るようになったりとか。


AutoResize.pm

package AutoResize;

use File::Find::Rule::MMagic;
use File::Basename;
use Image::Magick;
use Data::Dumper::Concise;

sub new{
  return bless {};
}

sub in{
  my $self = shift;
  $self->{'in'} = shift;
  return $self;
}

sub out{
  my $self = shift;
  $self->{'out'} = shift;
  return $self;
}

sub name{
  my $self = shift;
  $self->{'name'} = \@_;
  return $self;
}

sub magic{
  my $self = shift;
  $self->{'magic'} = \@_;
  return $self;
}

sub convert{
  my $self = shift;
  my $cb = shift;
  my $in_dir = $self->{'in'};
  my $out_dir = $self->{'out'};

  (-d $in_dir) || die("$in_dir : No such directory");
  (-d $out_dir) || (mkdir $out_dir) || die("$out_dir : Can't make thumbnail directory");

  my $rule = new File::Find::Rule::MMagic;
  $rule->name( @{$self->{'name'}} ) if defined $self->{'name'};
  $rule->magic( @{$self->{'magic'}} ) if defined $self->{'magic'};
  @pict_files = sort $rule->in($in_dir);

  # ファイル名にtitleが含まれるものは先頭に配置
  for(0..$#pict_files){
    my $tmp = $pict_files[$_];
    (splice(@pict_files,$_,1) && unshift(@pict_files,$tmp)) if (basename($tmp) =~ /^title/i);
  }

  for(@pict_files){
    my $image = { width => '', height => '',  name => $_};
    my $thumb = { width => '', height => '' , name => basename($_), dir => $out_dir };
    my $q = new Image::Magick;
    $q->Read($image->{name}) && (warn "ReadError:$image->{name} $!\n") && next ;
    ($image->{width}, $image->{height}) = $q->Get('width', 'height');

    $cb->($image, $thumb);
    next if ( ($thumb->{width} == '') and ($thumb->{height} == '') );

    $q->Resize('width' => $thumb->{width}, 'height' => $thumb->{height});
    $q->Write($thumb->{dir}.$thumb->{name}) && (warn "WriteError:$out_file $!\n");
  }
  return;
}

1;

シンプルな使用例:simple.pl

#! /usr/bin/perl

use strict;
use warnings;
use AutoResize;

my $pict_dir = './book/000000/';
my $thumb_dir = './thumb/000000/';

# コールバックで画像サイズや出力ファイル名の指定が出来ます
my $cb = sub{
  my $orig  = shift;
  my $thumb = shift;
  $thumb->{width}  = int($orig->{width} /2);
  $thumb->{height} = int($orig->{height}/2);
};

print "in: $pict_dir out:$thumb_dir\n";

my $q = new AutoResize;
$q->in($pict_dir)->out($thumb_dir)->name("*.jpg")->magic("image/jpeg")->convert($cb);

print "complete.\n";

1;

実際はこんな感じで使ってます:autoResize.pl

#! /usr/bin/perl

use strict;
use warnings;
use AutoResize;

my $id = shift || die('Usage: autoResize.pl BookID');
$id = sprintf('%06d',$id);
my $pict_dir = './book/'.$id.'/';
my $thumb_dir = './thumb/'.$id.'/';

print "in: $pict_dir out:$thumb_dir\n";

my $q = new AutoResize;
$q->in($pict_dir)->out($thumb_dir);
$q->name("*.jpg","*.JPG","*.jpeg","*.JPEG","*.png","*.PNG","*.gif","*.GIF","*.bmp","*.BMP");

my $count = 0;

my $cb = sub{
  
  my $orig  = shift;
  my $thumb = shift;
  
  if($orig->{width} < 150){
     $thumb->{width}  = int($orig->{width}/2);
     $thumb->{height} = int($orig->{height}/2);
  }else{
     my $ratio = $orig->{height} / $orig->{width};
     $thumb->{width}  = 150;
     $thumb->{height} = int(150 * $ratio);
  }

  my $name = sub{
    $count++ . ".jpg";
  };
  # 連番で保存
  $thumb->{name} = $name->();
};

$q->convert($cb);

print "complete.\n";

1;

 コールバックで条件書けるにしてみたけど、convert->("100x100")やらconvert->("50%")みたいに画像サイズを直接渡せるともっと便利なんじゃないかなという気もしますね。

テーマ : プログラミング
ジャンル : コンピュータ

ジョブキューサーバ Gearmanの紹介

 先週Gearman::Serverのコードを読んでいたのでその内容でも書こうかと思ったのですが、その前にGearman自体を紹介しなきゃいけないのかなと思って書いてみました。


Gearmanとは何か

 Gearmanは(LiveJournalやmemcachedの産みの親である)Brad FitzpatrickによってPerlで書かれたオープンソースの分散型ジョブキューサーバです。(後にBrian AkerとEric DayによってC言語で書き直されました)

Gearmanの特徴

  • オープンソース
  • 他言語対応(Perl, C, PHP, Ruby, Python, Java etc)
  • フレキシブル(特定のデザインパターンに縛られることなく、お手軽に実装可能)
  • シンプルなプロトコルとインタフェースを採用している為オーバーヘッドが小さい
  • 軽量かつコンパクトなので簡単にアプリケーションに組み込むことが出来る
  • 単一故障点がない(フォールトトレラントなシステムを構築することが可能)

Gearmanで出来ること

 時間が掛かる処理を非同期で実行させたり、分散して複数のサーバに実行させたりすることが可能です。

 非同期処理の例として、画像のアップローダがしばしば挙げられます。アップロードとリサイズを一連の処理として行う場合、画像のリサイズが終了するまで制御が戻らないのですが、非同期に処理を実行させることでリサイズ処理の終了を待たずにレスポンスを返すことが可能です。その他にも、大量のログのアナライズや動画のエンコード等、時間の掛かる処理をリアルタイム性を損なう事なく実行する事が可能です。

Gearmanを使うメリット

 非同期処理の実装は幾つかあります。例えば前の項で挙げたアップローダの場合、forkして子プロセスにリサイズ処理を委譲する形で実装することも可能ですが、その場合自分でプロセス管理をしなければなりません。

 Gearmanを使えば、キューをJobServerに投げるだけなので非常にシンプルに実装出来ます。また、Worker(実際にキューを受けて実行するプロセス)とApache等のフロントエンドを別サーバで動かすことで負荷を分散させたり、Workerを動かすサーバを増やすことでボトルネックを解消したりすることが可能です。

Gearmanを使う上での注意点

 Gearmanは標準ではジョブキューの永続化をサポートしていません。キューはメモリ上で管理されているので、異常終了時にジョブが消える可能性があることに留意する必要があります。RDBMSを利用した代替の選択肢としてはTheSchwartz等が挙げられます。(IBMの資料に永続化の話があったので公式サイトのドキュメントを読み直したらPersistent Queuesという項がありました。斜め読みなのですがgearmandの起動時のオプションでバックエンドとして利用するDBを選択することでジョブキューを永続化出来るようです)

参考文献


 とりあえずここまで。続きとしてはGearmanの構成やGearmanを使ったコーディングの実例、Gearmanの実装等を予定していたのですが、今はAnyEventと格闘しているので気が向いたら書きたいと思います。

テーマ : プログラミング
ジャンル : コンピュータ

dfの出力結果をスクリプトで見やすくカスタマイズする

小ネタです。

LVM使用時に、デバイス名が長くなるためにdfの出力が自動改行されるのが邪魔なので、好きなフォーマットで出力しようという話。ディスク監視のコード書いてる時に「改行うぜー」と思って調べたら--portabilityというオプションがあることを知りました。

df -hの出力結果:
1.gif

diskstat.plの出力結果:
2.gif


diskstat.pl:

#! /usr/bin/perl

use strict;
use warnings;

my @disk = `df --portability -h`; shift @disk;

for(@disk){
my($dev, $block, $use, $available, $per, $mount ) = split;
printf "%-20s : %5s(%4s)\n",$mount,$available,$per;
}

1;



参考:[linux-users:106733] Re: LVM使用時の df 出力結果について

テーマ : Linux
ジャンル : コンピュータ

File::Find::Rule::MMagicのベンチ

File::Find::Ruleに拡張子ベタ書きも品がないと思っていたところ、MIMEタイプでファイルを判別するFile::Find::Rule::MMagicというモジュールがあったのでテストしてみました。

テスト条件
ディレクトリ10個、画像ファイル300弱

#! /usr/bin/perl

use strict;
use File::Find::Rule::MMagic;
use File::Find::Rule;
use Time::HiRes;

my $s_time = Time::HiRes::gettimeofday;
my $find = new File::Find::Rule;
for(0..9){
my $id = sprintf("%06d",$_);
my @file = $find->file->name('*.jpg')->in("./$id");
}
my $e_time = Time::HiRes::gettimeofday;

print "File::Find::Rule\n";
print $e_time - $s_time ."\n\n";

my $s_time = Time::HiRes::gettimeofday;
my $find = new File::Find::Rule::MMagic;
for(0..9){
my $id = sprintf("%06d",$_);
my @file = $find->file->magic('image/*')->in("./$id");
}
my $e_time = Time::HiRes::gettimeofday;

print "File::Find::Rule::MMagic\n";
print $e_time - $s_time ."\n";

1;


結果

File::Find::Rule
0.0299019813537598

File::Find::Rule::MMagic
2.19269204139709

ファイルを毎回開いている分遅いですが、思った以上に遅かったです。
拡張子が信用できる状況であればわざわざ使う必要はないかも。

テーマ : プログラミング
ジャンル : コンピュータ

Image::MagickとFile::Find::Ruleを用いて簡単一括サムネイル作成

前置き:
所用で20万件ほどサムネイルを作成することになった
→シェルスクリプトからconvertを回してみたけど使いづらい。
→Image::MagickとFile::Find::Ruleを使えば幸せになれるんじゃないか。
→やってみた!

AutoResize.pm (File)

package AutoResize;

use File::Find::Rule;
use File::Basename;
use Image::Magick;

sub new{
return bless {};
}

sub in{
my $self = shift;
$self->{'in'} = shift;
return $self;
}

sub out{
my $self = shift;
$self->{'out'} = shift;
return $self;
}

sub serial{
my $self = shift;
$self->{'serial'} = shift;
return $self;
}

sub _outfile{
my $self = shift;
$self->{'out'}.$self->{'count'}++.'.jpg' if (defined $self->{'serial'});
}

sub _calc_pixel{
my ($orig_width, $orig_height) = (shift, shift);
my ($th_width, $th_height);

# width under 150px, size at 50%
if($orig_width < 150){
$th_width = int($orig_width/2);
$th_height = int($orig_height/2);
}else{
$th_height = int($orig_height/($orig_width/150));
$th_width = 150;
}
return $th_width, $th_height;
}

sub convert{
my $self = shift;
my $in_dir = $self->{'in'};
my $out_dir = $self->{'out'};

(-d $in_dir) || die("$in_dir : No such directory");
(-d $out_dir) || (mkdir $out_dir) || die("$out_dir : Can't make thumbnail directory");

my $rule = new File::Find::Rule;
my @pict_files = sort $rule->file()->name("*.jpg","*.JPG","*.jpeg","*.JPEG",
"*.png","*.PNG","*.gif","*.GIF","*.bmp","*.BMP")->in($in_dir);

# ファイル名にtitleが含まれるものは先頭に配置
for(0..$#pict_files){
my $tmp = $pict_files[$_];
(splice(@pict_files,$_,1) && unshift(@pict_files,$tmp)) if (basename($tmp) =~ /^title/i);
}

for(@pict_files){
my $in_file = $_;
my $out_file = $self->_outfile || $out_dir.basename($in_file);
my $q = new Image::Magick;
$q->Read($in_file) && (warn "ReadError:$in_file $!\n") && next ;
my($th_width, $th_height) = _calc_pixel( $q->Get('width','height') );
$q->Resize('width'=>$th_width, 'height'=>$th_height);
$q->Write($out_file) && (warn "WriteError:$out_file $!\n");
}
return;
}

1;


in(dir)で指定したディレクトリ以下の画像ファイルを探索して、out(dir)にサムネイルファイルを出力します。出力する際にサムネイルのディレクトリが存在しなければ作成します。サムネイルはアスペクト比を維持したまま、元ファイルが横幅150px以上であれば150pxに、150px未満であれば1/2のサイズにしてあります。serial()でサムネイルのファイル名を連番にすることが可能(デフォルトでは元ファイルと同じ名前)

使用例
autoResize.pl:

#! /usr/bin/perl

use strict;
use warnings;
use AutoResize;

my $id = shift || die('Usage: autoResize.pl BookID');
$id = sprintf('%06d',$id);
my $pict_dir = './book/'.$id.'/';
my $thumb_dir = './thumb/'.$id.'/';

print "in: $pict_dir out:$thumb_dir\n";

my $q = new AutoResize;
$q->in($pict_dir)->out($thumb_dir)->serial('on')->convert;

print "complete.\n";

1;



(2011/05/13 追記)
モジュール修正しました。
「Image::MagickとFile::Find::Ruleを用いて簡単一括サムネイル作成」の続き

続きを読む

テーマ : Linux
ジャンル : コンピュータ

最近の記事
カテゴリー
ブログ内検索
月別アーカイブ
最近のコメント
最近のトラックバック
プロフィール

feb28

Author:feb28
性別:男
年齢:ついに三十路入り

元々自転車ブログとして開設したのに
最近はPC関連の記事ばかりです。

たまに自転車や料理、旅行の話なども。

文章を綴るのはあまり得意ではないのですが、どうぞよろしくお願いします。

カウンター
RSSフィード
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード