Upload a resized image with PHP

by   PHP

Saturday, 02 April 2016

Resizing and formatting an image before it is uploaded on your server can be a long and tedious task. Why not doing so during the upload process? The next few lines of code will show you how.

Context

There are a couple of PHP extensions that deal with image processing, like Gmagick and ImageMagick. However, here we are going to make use of some native PHP functions to resize an image to a desired format, while keeping most of its properties.

Process

Say you want to convert a 4/3 aspect ratio image to a more panoramic 16/9 aspect ratio that best fit your website need. To prevent any kind of distortions, original image must be cropped. There is no way you can do it without losing some bits of information, sorry. However, we want to make sure cropping will apply equally on our image margins (both left/right and top/bottom) so that our subject remain at the center.

How do we do that? Well, first we need to proportionally reduce our original image to the new width OR height, whichever results in the less amount of final cropping. If the desired image ratio (width/height) is larger than the original ratio, it means the resulting image will be more panoramic than the original and therefore we will reduce it to the given width, adjusting the height accordingly before cropping the extra height. If on the contrary the desired ratio is less than the original ratio, it means the resulting image will be more of a portrait look and we will reduce it to the given height while adjusting for the width, all that before cropping the extra width. Basically, the image goes through a 3 steps transformation process:

1. First, we have the original image.
2. An intermediate image is generated that has the desired height or width while keeping the original ratio.
3. We create the final image which is a cropped version of the intermediate image with the desired height and width.

Naturally, it isn't a good idea to have your final image with larger width or height than the original since this will result in distortions. Let us therefore restrict to the case where the final image is of equal or smaller size than the original. Back to our example, going from 4/3 to 16/9 corresponds to an increase in ratio, so our new image will first be reduced to the given width. The new height can be computed as the old height divided by the proportional change in width, so that our intermediate image keep the original proportions in place before cropping. The difference between the intermediate height and the desired height will equal the total number of pixels to be cropped to obtain the desired format. Easy peasy, uh? OK I know, this may not make much sense, but it is the best way I found to describe the process in words. It will become clearer once you look at the code.

In PHP we will make use of the imagecopyresampled function. What it does basically is to copy a rectangular portion of an image to another image. So rather than performing crop on the original image, it will simply extract a portion of the original image and copy it to a newly created image. Note that this function take quite a few parameters, in particular we need to provide the exact position (in width and height) from where to start copy on the original image. Nothing fancy for the width, it will start right at the border (position 0). However, we need to compute this value for the height, taking the crop into account. So rather than starting at the corner, there will be a shift (a vertical one in this case). To compute the shift that will equally crop the original image from top and bottom is where things get interesting. We can't just compute the total crop and divide it by 2 to get the bottom shift, because we never get to produce the intermediate image. The shift has to be computed from the original image instead. So we need to take into account the proportional change in height from the original image to the intermediate image. The vertical shift can be computed as:

$$vertical\, shift = \left(\frac{(h_1 - h_2)*(\frac{h_0}{h_1})}{2}\right), where$$ $$h_0 = original\, height$$ $$h_1 = intermediate\, height$$ $$h_2 = final\, height$$

$extension = Input::file('image')->getClientOriginalExtension(); // Get image extension$now = new \DateTime(); // Get date and time
$date =$now->getTimestamp(); // Convert date and time in timestamp
$fileName =$date . '.' . $extension; // Give name to image$destinationPath = 'images'; // Define destination path
$img = Input::file('image')->move($destinationPath, $fileName); // Upload image to destination path$image_path = $destinationPath . '/' .$fileName; // Write image path in DB

if (Input::has('width') && Input::has('height')) {
$new_width = Input::get('width');$new_height = Input::get('height');
$new_ratio =$new_width/$new_height; // Resize image$size = getimagesize($image_path); // Get image size$original_width = $size[0];$original_height = $size[1];$original_ratio = $original_width/$original_height; // Get original ratio width/height

if ($original_ratio >$new_ratio) { // If ratio is greater than optimal
$width =$original_width/($original_height/$new_height);
$height =$new_height;
$shift_x = (($width - $new_width)*($original_width/$width))/2;$shift_y = 0;
} elseif ($original_ratio <$new_ratio) { // If ratio is lesser than optimal
$width =$new_width;
$height =$original_height/($original_width/$new_width);
$shift_x = 0;$shift_y = (($height -$new_height)*($original_height/$height))/2;
} else { // If ratio is already optimal
$width =$new_width;
$height =$new_height;
$shift_x = 0; // No need to crop horizontally$shift_y = 0; // No need to crop vertically
}

$src = imagecreatefromstring(file_get_contents($image_path));
$dst = imagecreatetruecolor($new_width,$new_height); imagecopyresampled($dst, $src, 0, 0,$shift_x, $shift_y,$width, $height,$original_width, $original_height); imagedestroy($src); // Free up memory
imagejpeg($dst,$image_path, 100); // adjust format as needed
imagedestroy(\$dst);
}


End Result

If you'd like to experiment a working example of this code, please check out the demo.