so i need to resize an image within perl. this is for games::risk, a perl implementation of the famous board game (btw, try it and tell me if you like it!). i want the background image to fit the window size on resize events. of course, i want it to be fast - but i also want something maintainable.
looking on cpan, i found 3 modules: gd, image::magick and image::imlib2. using gd to resize a picture is a nightmare, therefore i ditched it for image::resize which is a convenient wrapper around gd.
here are the needed incantations to resize:
- with imlib2
- with image::magick - note that the operation is done inplace, so one needs to clone the image first to compare the same things.
- with image::resize
my $old = Image::Imlib2->load($src);
my $new = $old->create_scaled_image($w, $h);
my $old = Image::Magick->new;
$old->Read($src);
my $new = $old->Clone;
$new->Scale(width=>$w, height=>$h);
my $old = Image::Resize->new($src);
my $new = $old->resize($w, $h);
however, image::imlib2 has a drawback: the only way to get back the new image is to save it to a file, where one can get the new image as a scalar directly:
- image::resize with $img->jpeg
- image::magick with $img->ImageToBlob
therefore, one must also take this into account in the benchmarking! so here's the latest version of the bench:
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
use Benchmark qw{ :all };
use Image::Size;
use Image::Resize;
use Image::Imlib2;
use Image::Magick;
my $src = "src.jpg";
my $dst = "dst.jpg";
my ($w, $h) = imgsize($src);
my @sizes = (
[10,10], [100,100], [1000,1000],
[640,400], [840,600], [1024,768],
[$w,$h], [$w/2,$h/2], [$w/4,$h/4],
[$h,$w], [$h/2,$w/2], [$h/4,$w/4],
);
my $imlib2 = Image::Imlib2->load($src);
my $resize = Image::Resize->new($src);
my $magick = Image::Magick->new; $magick->Read($src);
foreach my $s ( @sizes ) {
my ($width, $height) = @$s;
say "-> ${width}x${height}";
local $/;
cmpthese( -3, {
imlib2 => sub {
my $img = $imlib2->create_scaled_image($width, $height);
$img->save($dst);
open my $fh, '<', $dst;
return <$fh>;
},
magick => sub {
my $img = $magick->Clone;
$img->Scale(width=>$width,height=>$height);
return $img->ImageToBlob;
},
resize => sub {
my $img = $resize->resize($width,$height);
return $img->jpeg; # $img->png
},
} );
say '';
}
and i was quite astonished to see that image::imlib2 is still 2 or 3 times faster that image::magick or image::resize!
so, imlib2 may not the best api around, but sure it's fast...
How does resulting images quality compares, though? ;)
ReplyDeleteThere's also Imager, which has been great for most my image-needs.
ReplyDelete@cedric: according to imagemagick display, they all have the same 75% quality which is a sane defaut.
ReplyDelete@lars: unfortunately, imager suffers from the same api uncompleteness, so i'm forced to save to a temp file:
my $img = $imager->scale(xpixels=>$width, ypixels=>$height);
$img->write(file=>$dst);
open my $fh, '<', $dst;
return <$fh>;
which is really too big a disadvantage for imager to succeed. typical comparison:
Rate imager resize magick imlib2
imager 24.4/s -- -73% -80% -92%
resize 91.7/s 276% -- -23% -70%
magick 119/s 388% 30% -- -61%
imlib2 303/s 1143% 231% 155% --
Um, using Imager, why can't you just write the image data to a scalar rather than a file?
ReplyDeletemy $imager = Imager->new;
$imager->read( file => $src );
my $data;
$imager->scale( xpixels => $width, ypixels => $height )->write( type=> 'png', data=> \$data );
return $data;
What dou you mean by 75% quality?
ReplyDeleteI'm referring to the fact that many down/upscaling algorithms exists... with varying qualities ;)
@steve: oh, sorry, i'm not familiar at all with Imager. so, using what you suggest (640x400):
ReplyDeleteRate resize imager magick imlib2
resize 12.5/s -- -20% -45% -72%
imager 15.6/s 25% -- -31% -64%
magick 22.7/s 81% 45% -- -48%
imlib2 44.0/s 252% 181% 94% --
that's better, but still not on par with imlib2.
@cedric: 75% as percentage of original quality, which i guess means 25% loss. and i don't know which algorithms are used. i don't know that much about images, i just know i needed to resize one for a game background... :-)
You can set the image qualities by:
ReplyDeleteGD / Image::Resize
$img->jpeg(100)
Imlib2
$img->set_quality(100)
Imager
$img->write(data => \$data, type => 'jpeg', jpegquality => 100)
PerlMagick
$img->Set(quality=>100);
I am a big fan of 'imlib2' because of it's speed, but it is not practical for any website developer for 2 reasons:
1.) no file handle input
2.) no ability to save data to variable (for printing to screen or furthor manipulation) like in your script