5

I have a PHP script which is used to resize images in a user's FTP folder for use on his website.

While slow to resize, the script has completed correctly with all images in the past. Recently however, the user uploaded an album of 21-Megapixel JPEG images and as I have found, the script is failing to convert the images but not giving out any PHP errors. When I consulted various logs, I've found multiple Apache processes being killed off with Out Of Memory errors.

The functional part of the PHP script is essentially a for loop that iterates through my images on the disk and calls a method that checks if a thumbnail exists and then performs the following:

$image = new Imagick();
$image->readImage($target);
$image->thumbnailImage(1000, 0);
$image->writeImage(realpath($basedir)."/".rescale."/".$filename);
$image->clear();
$image->destroy();  

The server has 512MB of RAM, with usually at least 360MB+ free.

PHP has it's memory limit set currently at 96MB, but I have set it higher before without any effect on the issue.
By my estimates, a 21-Megapixel image should occupy in the region of 80MB+ when uncompressed, and so I am puzzled as to why the RAM is disappearing so rapidly unless the Image Magick objects are not being removed from memory.

Is there some way I can optimise my script to use less memory or garbage collect more efficiently?
Do I simply not have the RAM to cope with such large images?

Cheers

jmillar
  • 355
  • 1
  • 3
  • 9
  • Try just using the 'convert' command line tool, it should go much quicker and won't count towards PHP's memory limit. – robbrit Dec 06 '10 at 14:11

3 Answers3

7

See this answer for a more detailed explanation.

imagick uses a shared library and it's memory usage is out of reach for PHP, so tuning PHP memory and garbage collection won't help.

Try adding this prior to creating the new Imagick() object:

// pixel cache max size
IMagick::setResourceLimit(imagick::RESOURCETYPE_MEMORY, 32);
// maximum amount of memory map to allocate for the pixel cache
IMagick::setResourceLimit(imagick::RESOURCETYPE_MAP, 32);

It will cause imagick to swap to disk (defaults to /tmp) when it needs more than 32 MB for juggling images. It will be slower, but it will not run out of RAM (unless /tmp is on ramdisk, in that case you need to change where imagick writes its temp files).

Community
  • 1
  • 1
MattBianco
  • 1,501
  • 2
  • 20
  • 30
  • 4
    This is old, but this comment is correct, and wrong as well. Value is in BYTES, not MB. `$magick->setResourceLimit(\imagick::RESOURCETYPE_MEMORY, 67108864); $magick->setResourceLimit(\imagick::RESOURCETYPE_MAP, 67108864);` for 64 MB limits; don't use the static call on new versions of imagick – Method Mar 31 '16 at 22:49
7

MattBianco is nearly correct, only change is that the memory limits are in bytes so would be 33554432 for 32MB:

// pixel cache max size
IMagick::setResourceLimit(imagick::RESOURCETYPE_MEMORY, 33554432);
// maximum amount of memory map to allocate for the pixel cache
IMagick::setResourceLimit(imagick::RESOURCETYPE_MAP, 33554432);
  • You should use the non-static variant of the call in newer versions of imagick; `$img->setResourceLimit(\imagick::RESOURCETYPE_MEMORY, 33554432);` – Method Mar 31 '16 at 22:52
  • That is incorrect, the documentation states the limits are in megabytes http://php.net/manual/en/imagick.setresourcelimit.php – Mikulas Dite Feb 20 '18 at 09:39
  • 3
    I take that back, sorry. It's the documentation that is incorrect. The values are in fact in bytes. – Mikulas Dite Feb 20 '18 at 10:19
3

Call $image->setSize() before $image->readImage() to have libjpeg resize the image whilst loading to reduce memory usage.

(edit), example usage: Efficient JPEG Image Resizing in PHP

Community
  • 1
  • 1
Steve-o
  • 12,678
  • 2
  • 41
  • 60
  • Thanks Steve-o, this proved to help memory usage considerably, and I got the entire method to complete by doing this. It should be noted however that for the larger desktop-wallpapers I'm trying to do (1920x1200+) that it's still bugging out on me. I guess I will either have to be very aggressive with the setSize(), have my users use smaller images,or use another method entirely. Thanks for the advice! – jmillar Dec 06 '10 at 16:02