Sharing is caring… Especially with code

PHP + ImageMagick to Crop, Square, and Pad an Image

Written by

This isn’t anything new as there are plenty of scripts and tools out there to accomplish the given goal, but sometimes it’s easier to implement your own solution. Frankly, I got tired of manually editing images that followed the same formatting guidelines so I wrote this script to speed up the conversion process. I used PHP, however any language like Python, Ruby, etc would work as long as it can execute shell commands. If you have a plugin extension for ImageMagick then that would be a better option, but I didn’t want to bother with PECL.

The use case here is I have a bunch of given product images of varying sizes and pixel paddings i.e. empty surrounding solid color space. For each image I’d load it in GIMP, run a crop to remove the excess padding, take the higher of the two values (width or height), multiply it by a scale factor and round up, edit the canvas width and height to that new value, center the image content, drop in a new white background layer, bring the original layer to the top, merge down, save to overwrite the image. This is a very painful manual process and batch processing images should be easier — hence this script.

Before you try it it, keep in mind I was doing this on Windows so the executable names may vary for ImageMagick on Linux or Mac. It’s been a while since I’ve used it on Linux and I vaguely remember the tool being called convert. Running that program of the same name on Windows wanted to modify my hard disk. Yikes! ImageMagick’s install location is in my environment variables so there are no paths in the script. Also, I didn’t include any error checking like if images don’t get generated. Feel free to add that yourself.

Run it from the command line and pass in the image filename (local or url) as the first argument. The second argument, image output filename, is optional.

C:\Users\Tony> php -f process_image.php image.jpg

It currently works on a single image. Yes, I know, that does contradict my earlier statement of batch processing, but I was in a rush. I’ll make an update and do a follow-up post soon.

<?php

/*******************************************************

	VIA IMAGEMAGICK
		CROP THE EXCESS SPACE AROUND THE IMAGE
		CALCULATE NEW WIDTH & HEIGHT FROM SCALE
		RE-FIT THE IMAGE TO NEW SIZE WITH PADDING

*******************************************************/

// get input filename
$img1 = $argv[1];
if (!$img1) die("Specify input local image or url\n");

// get output filename
$img2 = $argv[2];

// path info
$path_parts = pathinfo($img1);
$basename = $path_parts["basename"];
$filename = $path_parts["filename"];
$extension = $path_parts["extension"];

// input filename
echo "input file => ";
echo $img1;
echo "\n";

// only checking for http(s) prefix
if (preg_match("/^https?:\/\//i", $img1)) {
	$data = file_get_contents($img1);
	file_put_contents($basename, $data);
	$img1 = $basename;
}

// output filename not set
if (!$img2) $img2 = sprintf("%s_out.%s", $filename, $extension);

// temp names
$tmp1 = sprintf("%s_tmp1.%s", $filename, $extension);
$tmp2 = sprintf("%s_tmp2.%s", $filename, $extension);

// scale factor
$scale_factor = 1.125;

// first crop
$crop = sprintf('magick %s -fuzz 1%% -trim +repage %s', $img1, $tmp1);
$result = shell_exec($crop);

// then get size
$identify = sprintf('identify -ping -format "%%[width] %%[height]" %s', $tmp1);
$result = shell_exec($identify);

// width x height
$dimensions = @explode(" ", $result);

// see dimensions
echo "dimensions => ";
echo sprintf("%sx%s", $dimensions[0], $dimensions[1]);
echo "\n";

// bigger of the two
$max_size = max($dimensions);
echo "max size => ";
echo $max_size;
echo "\n";

// scale factor from above
echo "scale factor => ";
echo $scale_factor;
echo "\n";

// calculate new size (width and height will be the same)
$new_size = ceil(($max_size * $scale_factor) / 10) * 10;
echo "new size => ";
echo $new_size;
echo "\n";

// make new image
$make = sprintf('magick %s -background White -gravity center -extent %sx%s %s', $tmp1, $new_size, $new_size, $img2);
$result = shell_exec($make);

// remove temp file
unlink($tmp1);

// output filename
echo "output file => ";
echo $img2;
echo "\n";

?>

Update 4/24/2024

Here is a quick and dirty wrapper script — I decided to leave the original as is. You can pass it a file containing a list of image filenames and/or URLs as the only argument. It’s going to use the default generated output names, but you could upgrade it. CSV, JSON, or another format where you could specify both the input and output filenames.

C:\Users\Tony> php -f image_batch.php list.txt
<?php

/*******************************************************

	WRAPPER SCRIPT FOR BATCH PROCESSING IMAGES

*******************************************************/

// list of images input file
$batch_file = $argv[1];
if (!$batch_file) die("Specify list of images input file\n");

// read in the image filenames / urls to an array
$images = file($batch_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

// shell exec the image processing script
foreach ($images as $image) {
	echo shell_exec(sprintf("php -f process_image.php %s", $image));
	echo "\n";
}

?>

Resources:

Leave a Reply

Your email address will not be published. Required fields are marked *