I'm using PHP Imagick
to resize images at runtime. The site has an image upload feature and we can't trust the user to use web-friendly JPEGs
, as during the mass-import
there are many 3
to 5MB
images, and even a few as large as 13MB
. Each image gets turned into a thumbnail (200x200
), and when going from list view into detailed view only one image is shown, so performance isn't a huge deal although we can't completely throw it by the wayside. Here's what we're doing so far:
$iMagick = new Imagick($file);
$iMagick->setImageResolution(72,72);
$iMagick->resampleImage(72,72,imagick::FILTER_UNDEFINED,1);
$geometry = $iMagick->getImageGeometry();
if ($geometry['height'] > 1920 || $geometry['width'] > 1080) {
$iMagick->scaleImage(1920, 0);
if($geometry['height'] > $resizeHeight) {
$iMagick->scaleImage(0, 1080);
}
}
$iMagick->setImageCompression(Imagick::COMPRESSION_JPEG);
$iMagick->setImageCompressionQuality($compression);
$iMagick->writeImage($file);
$Imagick->clear();
Side note: I just realized the flaw in my conditional logic here about height/width, so ignore that for the time being. I'll edit the question soon to reflect the correct condition comparisons. For those who didn't catch it it's scaling all images to 1920
wide even if it's supposed to be scaling the height, then after the scale it's sizing it back down to 1080 height
.
The image I've used to test starts as a 3MB 2398×2400
image. Scaling it to 1079x1080
results in a 1.5MB
image, then adding JPEG
compression at quality 70
brings it down to 750KB
. Using kraken.io the image was able to be compressed to just under 60KB. Are there any additional things I can do to this script since it needs to optimize the image at runtime?
I've read suggestions to use libjpeg, which is installed, but I can't find any documentation on what functions it enables or if there's a way to force Imagick to use it specifically. I'm not even sure that Imagick isn't using it already.
Edit: Solution
function itm_optimizeImage($file, $compression = 70, $maxDimensions = ['width' => null, 'height' => null]) {
$save = false;
$fi = new finfo(FILEINFO_MIME);
$mime = explode(';', $fi->file($file));
switch ($mime[0]) {
// possible to optimize other image types in the future
case 'image/jpeg':
try {
$iMagick = new Imagick($file);
if ($iMagick->getImageCompressionQuality() > $compression) {
$file = !itm_compressJPEG($file, $compression, $maxDimensions, $iMagick);
}
}
catch (Exception $e) {
error_log(__FUNCTION__ . " $path/$file failed: " . $e->getMessage());
return false;
}
if ($file) {
$pathParts = pathinfo($file);
rename($file, $pathParts['dirname'] . '/' . $pathParts['filename'] . '.large.' . $pathParts['extension']);
$iMagick->writeImage($file);
}
$iMagick->clear();
break;
}
return $file;
}
function itm_compressJPEG($file, $compression = 70, $maxDimensions = ['width' => null, 'height' => null], &$iMagick = null) {
try {
$iMagickCreated = true;
if ($iMagick) $iMagickCreated = false;
else $iMagick = new Imagick($file);
$iMagick->setImageResolution(72,72);
$iMagick->resampleImage(72,72,imagick::FILTER_UNDEFINED,1);
$geometry = $iMagick->getImageGeometry();
if (($geometry['width'] / $maxDimensions['width']) > ($geometry['height'] / $maxDimensions['height'])) {
// scale by width
$iMagick->scaleImage($maxDimensions['width'], 0);
} else {
// scale by height
$iMagick->scaleImage(0, $maxDimensions['height']);
}
$iMagick->setImageCompression(Imagick::COMPRESSION_JPEG);
$iMagick->setImageCompressionQuality($compression);
$iMagick->setImageFormat('jpg');
$iMagick->stripImage();
if ($iMagickCreated) {
$pathParts = pathinfo($file);
rename($file, $pathParts['dirname'] . '/' . $pathParts['filename'] . '.large.' . $pathParts['extension']);
$iMagick->writeImage($file);
$Imagick->clear();
}
return $file;
}
catch (Exception $e) {
error_log(__FUNCTION__ . " $path/$file failed: " . $e->getMessage());
return false;
}
}
Set the setImageCompressionQuality to a value of 70 and add these two lines after it.
$image->setImageFormat("jpg");
$image->stripImage();
This will dramatically reduce the size of the images.