はかますたいる!きょろの技的雑記

井上恭輔(@kyoro353)の私的かつ技的な日記です。米国サンフランシスコで暮らすエンジニアです。

Image::Magickによるサムネイル生成高速化

Image::Magickは汎用的な画像処理を行えるという利点があるが、速度的にはあまり早くない。
たとえば大量にアップされる一眼レフなどで撮影した大きなサイズのJPEGからサムネイルを生成するというような状況においては、処理速度がボトルネックになってしまうという問題がある。
ところが、どうやらインスタンス生成時にsizeオプションを指定するだけで処理速度が劇的に向上するらしい、ということを同期のエンジニアrai氏から聞いたので試してみた。

ベンチマーク内容
元画像 : http://photozou.jp/photo/show/228995/18479223
(元画像4272x2848を使用、カメラ小僧Gさんの画像をお借りします)
生成画像: 横160pxに固定し、アスペクト比を維持

サムネイル50枚の生成時間を比較する。

■ 検証コード

#!/usr/bin/perl  
use strict;  
use warnings;  
  
use Benchmark qw(timethese);
use Image::Magick;

timethese(50, {
    im_normal     => 'im_normal(    input_file  => "miko.jpg",
                                    output_file => "out_normal.jpg",
                                    width       => 160,
                                    );',
    im_optional   => 'im_optional(  input_file  => "miko.jpg",
                                    output_file => "out_optional.jpg",
                                    width       => 160,
                                    );',
});

sub im_normal {
    my %param = @_;

    my $input_file  = $param{input_file};
    my $output_file = $param{output_file};
    my $fix_width   = $param{width};

    my $image = Image::Magick->new();
   
    $image->Read($input_file);

    my ($width, $height) = $image->Get('width', 'height');

    my $n_width = $fix_width;
    my $n_height = $height * ($n_width / $width);
   
    $image->Thumbnail( width => $n_width, height => $n_height );
    $image->Write($output_file);
   
    return;
}

sub im_optional {
    my %param = @_;

    my $input_file  = $param{input_file};
    my $output_file = $param{output_file};
    my $fix_width   = $param{width};

    my $image = Image::Magick->new( size => $fix_width.'x'.$fix_width );
   
    $image->Read($input_file);

    my ($width, $height) = $image->Get('width', 'height');

    my $n_width = $fix_width;
    my $n_height = $height * ($n_width / $width);
   
    $image->Thumbnail( width => $n_width, height => $n_height );
    $image->Write($output_file);
   
    return;
}


■ 実行環境
Intel Core2Duo 2.6GHz / Memory 2GB / FreeBSD7.0R


■ 実行結果

%perl benche.pl
Benchmark: timing 50 iterations of im_normal, im_optional...
im_normal: 40 wallclock secs (35.78 usr +  3.70 sys = 39.48 CPU) @  1.27/s (n=50)
im_optional: 10 wallclock secs ( 9.70 usr +  0.28 sys =  9.98 CPU) @  5.01/s (n=50)

早っ!!?
インスタンス生成時に引数を加えるだけで速度4倍です。
出力画像のサイズが決まっている場合はsizeオプションを指定したほうが賢明ですね!!


【追記】
同期のhiroki氏と話していて出た話題で、「size指定をすると作業用キャンバスのサイズが限定されるので早くなるんじゃね?読み込みが高速化するんじゃね?」という意見が出たので$image->Readのみ同条件下でベンチしてみた。

■実行結果

%perl benche.pl 
Benchmark: timing 50 iterations of im_normal, im_optional...
im_normal: 37 wallclock secs (33.87 usr +  3.30 sys = 37.17 CPU) @  1.35/s (n=50)
im_optional:  8 wallclock secs ( 7.70 usr +  0.12 sys =  7.82 CPU) @  6.39/s (n=50)

おおお!やっぱりReadが激速化してる!!
逆に言えば縮小の計算コストってそんなに高くないのかもね(笑)