Resizing Images with PHP
Of all the memory-intensive processes that you can do with PHP image resizing is one of the most abused. It may be tempting to have the power of requesting any image in any size, passing the parameters in to a script and boom, having a custom fitted image, but it's not a great idea. There are several huge steps that PHP has to make, including loading the entire raw image into memory before plopping it into a new file, and it will slow down your site's reaction and annoy end users. A better solution, the one I use on this blog, is to pre-build several different image sizes, saving multiple versions of the file with predictable name changes, and then pull your image dynamically. So, let's start with resizing an image.
If you go with Image GD (I've heard that ImageMagick has better memory handling, Image GD just usually comes bundled with PHP builds and does not require a separate install) there are four main steps.
- Load the image into memory (imagecreatefromjpeg)
- Create the new image template (imagecreatetruecolor)
- Copy the old image into the new, resizing along the way (imagecopyresampled)
- Save the new image to disk (imagejpeg)
$image_root = '';
$image_root .= getcwd();
$image_root .= WHATEVER_DIR_YOUR_IMAGE_IS_IN;
$original_image_path = "{$image_root}OLD_IMAGE_NAME.jpg";
$resized_image_path = "{$image_root}NEW_IMAGE_NAME.jpg";
$original_image = imagecreatefromjpeg($original_image_path);
list($original_width, $original_height) = getimagesize($original_image_path);
// yup, we're making a standard 800x640 in this example
$resized_width = 800;
$resized_height = 640;
$resized_image = imagecreatetruecolor($resized_width, $resized_height);
imagecopyresampled(
$resized_image,
$original_image,
0, 0, 0, 0,
$resized_width,
$resized_height,
$original_width,
$original_height);
imagejpeg($resized_image, $resized_image_path);
In my case I wanted to do something a bit more interesting than resize a single image. I wanted to have a bulk image resizer, a script that would chew through a directory and create multiple new images with dynamic suffixes and size. There are a few more things I also did, like inserting some meta data into a local mysql table and maintaining album structure, but that's a bit specialized for my case. So here's a more beefy script that can process a whole bunch of images.
// yeah, this script could take a bit of time
set_time_limit(0);
// configuration for the different image sizes wanted
$image_size_list = array(
array(
'height' => 1200,
'width' => 1600,
'suffix' => 'full'),
array(
'height' => 768,
'width' => 1024,
'suffix' => 'large'),
array(
'height' => 375,
'width' => 500,
'suffix' => 'medium'),
array(
'height' => 180,
'width' => 240,
'suffix' => 'small'),
array(
'height' => 600,
'width' => 800,
'suffix' => 'standard'),
array(
'height' => 75,
'width' => 100,
'suffix' => 'thumb'));
$image_root = '';
$image_root .= getcwd();
$image_root .= WHATEVER_DIR_YOUR_IMAGE_IS_IN;
// this will put the resized image dir in the same as your images
$resized_image_directory = 'resized';
if(file_exists("{$image_root}{$resized_image_directory}") == FALSE)
mkdir("{$image_root}{$resized_image_directory}");
$images = glob("{$image_root}*");
foreach($images as $image)
{
// all we want is the name, not the full path
$image_path_array = explode(DIRECTORY_SEPARATOR, $image);
$image_name = array_pop($image_path_array);
// you may need to add additional conditions here
// depending on how your structure is laid out
if($image_name == $resized_image_directory)
continue;
// all we want is the name, not the extension
$image_name_array = explode('.', $image_name);
$image_name = array_shift($image_name_array);
$original_image = imagecreatefromjpeg($image);
list($original_image_width, $original_image_height) = getimagesize($image);
foreach($image_size_list as $image_size)
{
$resized_image = imagecreatetruecolor($image_size['width'], $image_size['height']);
imagecopyresampled(
$resized_image,
$original_image,
0, 0, 0, 0,
$image_size['width'],
$image_size['height'],
$original_image_width,
$original_image_height);
$resized_image_path = '';
$resized_image_path .= $image_root;
$resized_image_path .= $resized_image_directory;
$resized_image_path .= DIRECTORY_SEPARATOR;
$resized_image_path .= "{$image_name}-size-{$image_size['suffix']}.jpg";
imagejpeg($resized_image, $resized_image_path);
imagedestroy($resized_image); // helps with the memory usage
}
}
This is not production code! There are multiple things that are not checked for, like if the files passed in are actually JPGs, possible errors with filenames, or the aspect ratio of images (if there's a portrait passed into this it's going to have a bad time). However, it wouldn't be hard to build off of this for any special use cases.
Once I had the script built and my images resizing just fine there was one last piece, one last headache to deal with. I wanted to add custom EXIF headers to each image, tagging them with some copyright and other information. This ended up being a hefty project, as PHP does not have nice helper functions for this stuff like it does for resizing/creating new images. I'll go over that logic in the next post.
Comments (0)