Search code examples
phpimagemagickimage-manipulationimagick

Improve imagick performance when resizing animated gifs


I am resizing an animated gif and adding a background to it using the imagick extension in PHP.

This is the code I am using:

$image = new Imagick('myfile.gif');
$image = $image->coalesceimages();

$final = new \Imagick();

foreach ($image as $frame){

    $frame->thumbnailImage($width, $height, true);
    $geometry = $image->getImageGeometry();

    $x = ( $width - $geometry['width'] ) / 2;
    $y = ( $height - $geometry['height'] ) / 2;

    $canvas = new \Imagick();
    $canvas->newImage( $width, $height, $background);

    $canvas->compositeImage( $image, \Imagick::COMPOSITE_OVER, $x, $y );

    $final->addimage($canvas);
}

$final->desconstructimages();
$final->writeImages('resized.gif', true);

The code works and the resized images are perfect. However, when resizing animated gifs, it takes quite a while.

With the above code, it takes about 10 seconds to resize a 36kb gif with 60 frames. With non-animated images, it takes about half a second.

I am running ImageMagick 6.8.1-0 2012-12-20 on Ubuntu 12.10 32-bit.

Is there anyway to speed up the resizing?

Update:

I found out that thumbnailImage does not need to be called on every frame, so this is now what I am using:

$image = new Imagick('myfile.gif');
$image = $image->coalesceimages();

$final = new \Imagick();

$image->thumbnailImage($width, $height, true);
$geometry = $image->getImageGeometry();

$x = ( $width - $geometry['width'] ) / 2;
$y = ( $height - $geometry['height'] ) / 2;

foreach ($image as $frame){

    $canvas = new \Imagick();
    $canvas->newImage( $width, $height, $background);

    $canvas->compositeImage( $image, \Imagick::COMPOSITE_OVER, $x, $y );

    $final->addimage($canvas);
}

$final->desconstructimages();
$final->writeImages('resized.gif', true);

By only doing the thumbnailing once, I am now saving on average, 1 second (10%!) compared to before! So, to the guy who said, I should be happy with the performance and not complain, this is proof that you should investigate and improve things that you were previously happy with, otherwise you will always be a mediocre developer.

I am of course, trying to decrease the conversion time further, and will be doing some profiling to determine which calls are the most expensive and then target those.


Solution

  • After spending a while trying to optimize things, I ended up with an average time of around 6.5 seconds, which is much better than the initial 10 seconds.

    I am now resizing the animation outside the loop and using optimizeImageLayers which is faster than deconstructImages and also keeps the filesize down.

    $image = new Imagick('myfile.gif');
    $image = $image->coalesceimages();
    
    $final = new \Imagick();
    
    foreach ($image as $frame){
    
        $image->thumbnailImage($width, $height, true);
        $geometry = $image->getImageGeometry();
    
        $x = ( $width - $geometry['width'] ) / 2;
        $y = ( $height - $geometry['height'] ) / 2;
    
        $canvas = new \Imagick();
        $canvas->newImage( $width, $height, $background);
    
        $canvas->compositeImage( $image, \Imagick::COMPOSITE_OVER, $x, $y );
    
        $final->addimage($canvas);
    }
    
    $final = $final->optimizedImageLayers();
    $final->writeImages('resized.gif', true);
    

    This is currently what I am using and am quite happy with it. However, I am will be looking at tweaking imagemagick's settings and playing with the color depth, etc to make it better, once I find some spare time of course :)