Frage

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.

War es hilfreich?

Lösung

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 :)

Andere Tipps

I know this might be a little late but for those like me who end up here searching for a way to resize a .gif here is a little code that I wrote that also keeps a max of 25 frames in the image (this code keeps the original animation speed).

$original = new \Imagick('/path/to/original/image');
$new = new \Imagick();

$i = 0; 
$frameStep = ceil($original->getNumberImages() / 25);
foreach ($original as $frame) {
    if ($i % $frameStep === 0) {
        $delay = $frame->getImageDelay();

        $frame->thumbnailImage($x, $y);
        $frame->setImagePage($x, $y, 0, 0);
        $frame->setImageDelay($delay * $frameStep);

        $new->addImage($frame->getImage());
    }

    $i++;
}

file_put_contents('/path/to/NEW/file', $new->getImagesBlob());

$new->clear();
$new->destroy();
$original->clear();
$original->destroy();

If 60 frames take 10 seconds to resize, then each frame actually takes 1/6th of a second, which is even better, the your result for 1 frame, isn't it? You should not complain, when 60 time more work takes a bit longer.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top