Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Imagick is very slow when writing an image modified with ImageDraw in GIF format. #360

Open
ozahorulia opened this issue Nov 22, 2020 · 2 comments

Comments

@ozahorulia
Copy link

ozahorulia commented Nov 22, 2020

Here is an example of the code where I'm trying to draw 10 pictures with a simple line on them:

// Just an example / performance test, images won't be the same eventually
// I need several of them because they will be combined into an animated gif later
$pixel = new ImagickPixel( 'white' );
foreach (range(0, 9) as $x) {
    $image = new Imagick();
    $image->setFormat('gif');
    $image->newImage(320, 60, $pixel);

    $line = new ImagickDraw();
    $line->line(0, 0, 100, 100);
    $image->drawImage($line);

    $image->writeImage(__DIR__ . '/images/' . $x . '.gif');
}

For some reason this code takes ~0.5 sec to execute which is way too much for a highload application. I tried to do the same thing with PHP GD library and it works much faster (0.01 sec for the same 10 pics). The writeImage method takes the most time. But it doesn't look like an I/O issue, because if I use getImageBlob instead, it will give me the same result. So, whenever I modify picture ImageDraw and then trying to output the content (either write to disc or dump it anywhere using ImageDraw), it shows very low performance compared to other libraries.

Is this just Imagick problem and I can do nothing about it? Or am I doing something wrong and there is a reason caused by me why this takes so long time?

PHP 7.4, Ubuntu 20.04, ImageMagick 6.9.10-23. Also tested it on different machines (incl. docker) and environments with different php versions.

UPD: it works fine if I use JPG format for each image. But my eventual goal is to generate an animated GIF. And when I use Imagick::writeImages('result.gif', true), it takes 0.5 sec. to execute. So is there a problem with GIF format? GD's imagegif() works much faster.

Example with the animation:

<?php

$time = microtime(true);
$pixel = new ImagickPixel( 'white' );
$gif = new Imagick();
$gif->setFormat("jpg");

foreach (range(0, 9) as $x) {
    $image = new Imagick();
    $image->setFormat('jpg');
    $image->newImage(320, 60, $pixel);

    $draw = new ImagickDraw();
    $draw->setFillColor('Black');
    $draw->setFontSize(10);
    $image->annotateImage($draw, 10, 10, 0, $x);

    // I don't need to save each frame, just testing that it works fast for jpg format
    $image->writeImage(__DIR__ . '/images/' .  $x . '.jpg');

    $image->setImageDelay(100);
    $gif->addImage($image);
}

$gif->deconstructImages();

echo 'Generating frames: ' .  (microtime(true) - $time) . PHP_EOL;

$time = microtime(true);

$gif->writeImages(__DIR__ . '/result/imagick_animated.gif', true);

echo 'Writing GIF: ' . (microtime(true) - $time) . PHP_EOL;

Output:

Generating frames: 0.050290822982788
Writing GIF: 0.43121600151062
@ozahorulia ozahorulia changed the title Imagick is very slow when writing an image modified with ImageDraw Imagick is very slow when writing an image modified with ImageDraw in GIF format. Nov 22, 2020
@Danack
Copy link
Collaborator

Danack commented Nov 23, 2020

Is this just Imagick problem and I can do nothing about it?

I don't know. It sounds slightly long, but not massively slow.

You could try investigating it by running the code through strace with something like strace -c -ff php speed_test.php which would should all the system calls being made.

It's not unexpected that Imagick would be slower the GD. Imagick uses the ImageMagick library which delegates drawing to other programs, whereas GD does pretty much everything in memory (afaik). But there's a chance something funky is happening, e.g. I've seen some cases where ImageMagick doesn't know where it's config files are, so it scans a ridiculous number of directories to try to find them, which could maybe be tweaked.

which is way too much for a highload application.

tbh, I don't recommend processing images in a webserver at all. It's much better to put requests for images onto a queue, and then generate them as a background worker. That background worker can then be scaled separately to the main webserver.

To set your expectations, I'm not going to investigate this myself until there is a vastly more funding in place for this project, as this type of thing is just quite low on the priority list, but I'll leave it open rather than pretending it's not a problem.

@ozahorulia
Copy link
Author

ozahorulia commented Nov 23, 2020

@Danack thanks very much for the response! It became much more clear for me now. Just for a record, I was able to decrease time by using quantizeImages method before writing the result gif. It looks like this:

$gif = $gif->deconstructImages();
$gif->quantizeImages(256, Imagick::COLORSPACE_SRGB, 0, false, false);
$gif->writeImages(__DIR__ . '/result/imagick.gif', true);

And this helped me to obtain 0.02 sec instead of 0.5 without quantizeImages. I don't really know what it really does and why does it affect the performance so much, but the result looks pretty good to me. Maybe it may help somebody who is facing the same issue :)

If somebody could explain in few words why quantizeImages helped me to save almost 0.4 sec just in a few words - I would be really grateful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants