Making & Cropping Thumbnails Square Using PHP & GD
Recently I made an image uploader for my other website & I noticed that there isn’t a lot of tutorials around on how to crop thumbnails into squares. This tutorial requires that GD is enabled in PHP, I’m not sure whether you need GD2 or not but I don’t see any reason for it not to work in older versions of GD.
Copy The Large Image
Just incase you don’t know how to do it, you can copy the original image across like this.
1 |
copy($_FILES["Filedata"]["tmp_name"],"path/to/folder/".$_FILES['Filedata']['name']); |
Let’s Get Onto The Thumbnails
Ok now let’s get onto the code for the thumbnails.
1 2 3 4 5 |
$source_img = @imagecreatefromjpeg($_FILES["Filedata"]["tmp_name"]); //Create a copy of our image for our thumbnails... if (!$source_img) { echo "could not create image handle"; exit(0); } |
We create a copy of the image using imagecreatefromjpeg()
. A quick note here is that this code will only work for uploads of jpg & jpeg file types. You could have a switcher checking for the mime type and use imagecreatefromjpeg
, imagecreatefromgif
& imagecreatefrompng
to also do those file types. The switcher could be created by putting the image through getimagesize
and handing the returned arrays ‘mime’ value through image_type_to_mime_type
, but that’s not what this tutorial is about.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$new_w = 120; $new_h = 120; $orig_w = imagesx($source_img); $orig_h = imagesy($source_img); $w_ratio = ($new_w / $orig_w); $h_ratio = ($new_h / $orig_h); if ($orig_w > $orig_h ) {//landscape $crop_w = round($orig_w * $h_ratio); $crop_h = $new_h; } elseif ($orig_w < $orig_h ) {//portrait $crop_h = round($orig_h * $w_ratio); $crop_w = $new_w; } else {//square $crop_w = $new_w; $crop_h = $new_h; } $dest_img = imagecreatetruecolor($new_w,$new_h); imagecopyresampled($dest_img, $source_img, 0 , 0 , 0, 0, $crop_w, $crop_h, $orig_w, $orig_h); |
Mmmk. So first we set a width & height for our cropped thumbnails. They don’t have to be square, but for layout purposes square thumbnails create a nice foundation. We grab the image’s width & height. We create a ratio by dividing the new width by the original width, we do the same for the height too.
Next we do a check to see what shape our image is landscape, portrait or square. If the image is checked we use the ratio to get the right size for either the width or height. If it’s square it doesn’t matter. Really with a square thumbnail the check doesn’t matter since in our case the $crop_w
& $crop_h
variables should always be 120, but if you chose rectangular cropped thumbnails then it is very important or your images will be out of proportion.
Finally we create a blank image at the size of our thumbnail and then use imagecopyresampled
to place our copied image into the blank image with the new crop sizes.
There’s only one more thing left to do & that is to save our image somewhere.
1 2 3 4 5 6 7 |
if(imagejpeg($dest_img, "path/to/folder/".$_FILES['Filedata']['name'])) { imagedestroy($dest_img); imagedestroy($source_img); } else { echo "could not make thumbnail image"; exit(0); } |
As I said before, this part of the script will only work with jpg and jpegs. If you want other formats you would need to use a switcher. If it was successful we destroy all the images used to free up memory.
That’s It
Yep, that’s it. You should end up with a nice cropped thumbnail at your desired size. If you have any problems let me know and I’ll see what I can do to help. 🙂
A Little Tip
Just a little after thought. In the code to copy the full image I just use the copy
function. If you are going to use that instead of move_uploaded_file
it is probably a good idea to make sure the file was uploaded via a form using is_uploaded_file
. You could use something like this to cover all bases.
1 2 3 4 |
if (!isset($_FILES["Filedata"]) || !is_uploaded_file($_FILES["Filedata"]["tmp_name"]) || $_FILES["Filedata"]["error"] != 0) { echo "invalid upload"; exit(0); } |
How To Center The Square Cropping Effect
There have been a few requests about how to take the square crop from the center of an image. Here’s how to do it. To do that we need to calculate the X & Y positions. This requires the code to be altered slightly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
if ($orig_w > $orig_h ) {//landscape $crop_w = round($orig_w * $h_ratio); $crop_h = $new_h; $src_x = ceil( ( $orig_w - $orig_h ) / 2 ); $src_y = 0; } elseif ($orig_w < $orig_h ) {//portrait $crop_h = round($orig_h * $w_ratio); $crop_w = $new_w; $src_x = 0; $src_y = ceil( ( $orig_h - $orig_w ) / 2 ); } else {//square $crop_w = $new_w; $crop_h = $new_h; $src_x = 0; $src_y = 0; } $dest_img = imagecreatetruecolor($new_w,$new_h); imagecopyresampled($dest_img, $source_img, 0 , 0 , $src_x, $src_y, $crop_w, $crop_h, $orig_w, $orig_h); |
Here is what we are doing. We need to figure out the X & Y positions from which to crop the image, to do that we do some math. If the image is landscape we work out the X position using the sum ((new image width / 2) – (thumbnail width /2)). We set the height to 0 as it has already been set to the height of the thumbnail.
We do the same again for portrait images except we do the sum on the height and set the width to 0. If the image is square we don’t need to do anything since it will fit straight into the square thumbnail with no cropping.
I hope that helps with centering the crop when cropping square thumbnails. Thank you to Elpres, and Deryk Wenaus in the comments for the sum to work out the X & Y positions.
Resizing Images Held In A Directory
Huge thanks to Ray for contributing this snippet of code in the comments:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
$folder = $_POST['folder']; //POST if from form, GET if from URL $thisdir = getcwd(); if(!file_exists($thisdir ."/"."$folder/thumbs")) { mkdir($thisdir ."/"."$folder/thumbs" , 0777); } function returnimages($dirname="") { $pattern="(\.jpg$)|(\.png$)|(\.jpeg$)|(\.gif$)"; //valid image extensions $handle = opendir($dirname); while(false !== ($filename = readdir($handle))) { if(eregi($pattern, $filename)){ //if this file is a valid image $files[] = $filename; } } if (count($files)<>0) { sort($files); } $curimage=0; while($curimage !== count($files)){ $cropfile=$dirname.'/'.$files[$curimage];echo '<br>'.$cropfile; $source_img = @imagecreatefromjpeg($cropfile); //Create a copy of our image for our thumbnails... if (!$source_img) { echo "could not create image handle"; exit(0); } $new_w = 120; $new_h = 120; $orig_w = imagesx($source_img); $orig_h = imagesy($source_img); $w_ratio = ($new_w / $orig_w); $h_ratio = ($new_h / $orig_h); if ($orig_w > $orig_h ) {//landscape from here new $crop_w = round($orig_w * $h_ratio); $crop_h = $new_h; $src_x = ceil( ( $orig_w - $orig_h ) / 2 ); $src_y = 0; } elseif ($orig_w < $orig_h ) {//portrait $crop_h = round($orig_h * $w_ratio); $crop_w = $new_w; $src_x = 0; $src_y = ceil( ( $orig_h - $orig_w ) / 2 ); } else {//square $crop_w = $new_w; $crop_h = $new_h; $src_x = 0; $src_y = 0; } $dest_img = imagecreatetruecolor($new_w,$new_h); imagecopyresampled($dest_img, $source_img, 0 , 0 , $src_x, $src_y, $crop_w, $crop_h, $orig_w, $orig_h); //till here if(imagejpeg($dest_img, $dirname."/thumbs/".$files[$curimage])) { imagedestroy($dest_img); imagedestroy($source_img); } else { echo "could not make thumbnail image"; exit(0); } $curimage++; } } returnimages($name=$folder); |
PEAR Modules
A few people have asked me why I don’t use the two PEAR modules HTTP_Upload & Image_Transform… Well one reason is that HTTP_Upload is (according to the PEAR directory) without any maintainers at the moment. Also my host & quite a few others don’t allow you to install additional PEAR packages. I am lucky enough in that my host will however allow me to set up my own PEAR install which will then allow me to install additional packages, but this tutorial is useful for those who do not have that option. It might be
39 Comments
petr
nice. but i’m not really a PHP guy and have no idea for to execute this to do it’s thing.
Could you help ?
Isara
You need to install php. Recommended to install on a web server. Then executed via a browser.
Veneficus Unus
Not quite sure I understand. All the code is there. Just join all the parts together in a file call it something like upload.php & then create a form on the page you want images to be uploaded on. A standard form like this:
In the part of the PHP script you copied that looks like this:
Change it to this:
Change path/to/upload/complete/file.html to a file you want to show if the file was uploaded successfully. That’s about all there is too it. You might need to seek out some code for image type validation, but that isn’t in the scope of this tutorial.
Jessen
Thanks for the tutorial and source code! I’m searching for this code for quite some times already. 🙂
Elpres
The image is not centered when you require a square thumbnail from a non-square image, it starts at the top-left of the image. To get around this you’ll need to change the code to calculate how far you need to start in either X or Y axis:
1-(width or height of new resized-image / 2) – (width of thumb/2)
Tomás
Ok, thanks for the help, but where do we use that?
Paul Robinson
Hey,
you would need to add a little bit to the code. I’ll sort out how to do it for you and add it to the bottom of the post.
Check back soon. 😉
Paul Robinson
I’ve updated the bottom of the post so it explains how to center the crop. I hope that helps. 😉
Tomás
Oh! Thankyou so much! You were so fast!
It works perfectly.
Paul Robinson
No problem. A big thanks to Deryk too for pointing out the mistake I make the first time. 😉
Veneficus Unus
Yes, I know. This tutorial only gives info on how to create square cropped thumbnails of a certain size. For the purpose I was using them it wasn’t needed to center them. Using the top-left of the image gave some form of mystery & that’s exactly what was needed.
Thanks for telling everyone how to center then though. 😉 However it is a lot easier using imagemagick it’s a shame it isn’t supported as well by hosting providers as it should be. 🙁
Brian
Thanks for this tutorial, exactly what I was looking for!
Paul Robinson
No problem I’m glad you found a use for it. 🙂
Salman
I wrote a similar function that could crop the image so that it “best-fits” inside given dimension, its described here:
http://911-need-code-help.blogspot.com/2009/04/crop-to-fit-image-using-aspphp.html
I now realize its probably the same thing as yours.
Paul Robinson
It is indeed, but you do give an ASP version.
Since I don’t use ASP it’s great you’ve linked to it there for anyone who stumbles into my site looking for a ASP GD resize function. 🙂
Deryk Wenaus
Hi, There is an error in your code for the centre square crop.
The error happens when you scale the thumbnail any degree and shifting occurs so it is not centered.
line 4 should reaad
$src_x = ceil( ( $orig_w - $orig_h ) / 2 );
likewise line 10 needs to be
$src_y = ceil( ( $orig_h - $orig_w ) / 2 );
more good examples are here: http://php.net/manual/en/function.imagecopyresampled.php
Paul Robinson
Hi,
Thanks for posting that correction. I never got a chance to check the code properly, and never noticed it was off center.
I’m busy doing coding for the redesign of the site. That’s teach me to rush through edits on posts, lol.
I’ve checked it, made sure it works (which it does), and updated the post. 😉
Thanks again.
emptywalls
I found this useful. I integrated some of the thumbnail code into my CakePHP app. Thanks very much for the post!
Paul Robinson
No problem. Glad it helped you out. 😉
Ray
Great script, I used it for an upload form in a gallery, but now I´m trying to change the first bit to make it work as a batch script for an entire directory. @imagecreatefromjpeg($files[$curimage]) doesn´t work. Any idea? Thanks in advance
Paul Robinson
I’m not sure. I’ve never tried to resize images from a directory rather than an upload.
From looking at the little bit of code you have wrote I’m not sure how you are going about it, but I would think a normal loop through files & passing the path to the image to
imagecreatefromjpeg()
would work.If it doesn’t you could always take off the ‘@’ to see what error it’s throwing back and work from there. I’d give it a go, but I don’t have access to my localhost at the moment to test it out.
Ray
Got it, thanks
Ray
Got it working:
<?php
$folder=$_GET['folder'];
$thisdir = getcwd();
if (!file_exists($thisdir ."/"."$folder/thumbs")) {
mkdir($thisdir ."/"."$folder/thumbs" , 0777);}
function returnimages($dirname="") {
$pattern="(\.jpg$)|(\.png$)|(\.jpeg$)|(\.gif$)"; //valid image extensions
$handle = opendir($dirname);
while (false !== ($filename = readdir($handle))) {
if(eregi($pattern, $filename)){ //if this file is a valid image
$files[] = $filename;}
}
if (count($files)0) {
sort($files);}
$curimage=0;
while($curimage !== count($files)){
$cropfile=$dirname.’/’.$files[$curimage];echo $cropfile;
$source_img = @imagecreatefromjpeg($cropfile); //Create a copy of our image for our thumbnails…
if (!$source_img) {
echo “could not create image handle”;
exit(0);
}
$new_w = 120;
$new_h = 120;
$orig_w = imagesx($source_img);
$orig_h = imagesy($source_img);
$w_ratio = ($new_w / $orig_w);
$h_ratio = ($new_h / $orig_h);
if ($orig_w > $orig_h ) {//landscape from here new
$crop_w = round($orig_w * $h_ratio);
$crop_h = $new_h;
$src_x = ceil( ( $orig_w – $orig_h ) / 2 );
$src_y = 0;
} elseif ($orig_w
Davide71
Hi, to let it run I used these lines:
if ($orig_w > $orig_h ) {
# LandScape
$crop_w = round($orig_w * $h_ratio);
$crop_h = $new_h;
$src_x = round((($orig_w * $h_ratio) – $new_w) / 2);
$src_y = 0;
} elseif ($orig_w < $orig_h ) {
# Portrait
$crop_h = round($orig_h * $w_ratio);
$crop_w = $new_w;
$src_x = 0;
$src_y = round((($orig_h * $w_ratio) – $new_h) / 2);
} else {
# Square
$crop_w = $new_w;
$crop_h = $new_h;
$src_x = 0;
$src_y = 0;
}
Note that $new_h and $new_w in my case have different values.
Ray
sorry there´s more code but didn´t fit
Ray
Rest of the code for those who are interested:
Paul if it fills up your blog too much you can delete the post, thanks for your reponse
Paul Robinson
No problems. If you could email me the code I’ll add it to the post tomorrow, that’s assuming you don’t mind of course. 😉
Dominic
Thank you very much your solution worked great. Kudos!
Paul Robinson
No problem Dominic. Glad it was helpful.
WeB DiZaJN
TY for this very good info.
Angie
I’ve been trying everything and failing.
I’m trying to integrate this code with the one found here: http://davidwalsh.name/generate-photo-gallery. How would I do that?
Paul Robinson
Hi Angie,
Well it would be pretty much the same except for his
make_thumb()
function which would change a little. It would look something like:That should do it I think. I haven’t tested it as I’m unable too at the moment, but it should work. Just set
$new_w
and$new_h
to the cropped size you want.Lara
Great tutorial! Thanks!
Paul Robinson
Thanks Lara, glad it was helpful.
Muhammad Shidiq
awsome!!
workin like a charm!!
thanks dude.. 🙂
Cheyzerr Bondoc
double “thumbs” up!!! got this on my site… thank you very much 🙂