Home > Tutorials > PHP > Making & Cropping Thumbnails Square Using PHP & GD
Permalink to Making & Cropping Thumbnails Square Using PHP & GD

Making & Cropping Thumbnails Square Using PHP & GD

by on 02.21.2009 | 32 comments

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 [...]

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.

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.

$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.

$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.

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.

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.

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:

$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

Written by Paul Robinson

A Web coder in languages such as CSS, X/HTML, jQuery, but mostly PHP. Addicted to Girls Aloud, Jennifer Morrison, Carah Faye Charnow, TV Show Chuck, and completely in love with Yvonne Strahovski's smile.

Give something back!

If you LOVED this tutorial and would like to show your appreciation, please consider or a little something from our Amazon Wishlist.

Discussion: 32 Comments

  1. May 5th, 2009 @ 16:29:03

    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 ?


  2. May 5th, 2009 @ 20:03:05

    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:

    <form enctype="multipart/form-data" action="upload.php" method="post">
    Choose a file to upload: <input name="uploadedfile" type="file" /><br />
    <input type="submit" value="Upload File" />
    </form>
    

    In the part of the PHP script you copied that looks like this:

    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);
    }
    

    Change it to this:

    if(imagejpeg($dest_img, "path/to/folder/".$_FILES['Filedata']['name'])) {
    	imagedestroy($dest_img);
    	imagedestroy($source_img);
            header("Location: path/to/upload/complete/file.html");
    } else {
    	echo "could not make thumbnail image";
    	exit(0);
    }
    

    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.


  3. May 9th, 2009 @ 18:13:57

    Thanks for the tutorial and source code! I’m searching for this code for quite some times already. :)


  4. Jul 10th, 2009 @ 10:00:21

    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)


    • May 14th, 2010 @ 12:21:37

      Ok, thanks for the help, but where do we use that?


    • May 14th, 2010 @ 12:37:41

      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. ;)


    • May 14th, 2010 @ 14:03:42

      I’ve updated the bottom of the post so it explains how to center the crop. I hope that helps. ;)


    • May 14th, 2010 @ 19:55:27

      Oh! Thankyou so much! You were so fast!
      It works perfectly.


    • May 14th, 2010 @ 20:51:03

      No problem. A big thanks to Deryk too for pointing out the mistake I make the first time. ;)


  5. Jul 10th, 2009 @ 11:04:31

    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. :(


  6. Dec 19th, 2009 @ 22:49:01

    Thanks for this tutorial, exactly what I was looking for!


    • Dec 20th, 2009 @ 00:10:07

      No problem I’m glad you found a use for it. :)


  7. Apr 28th, 2010 @ 11:32:50

    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.


    • Apr 28th, 2010 @ 19:07:16

      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. :)


  8. May 14th, 2010 @ 16:24:25

    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


    • May 14th, 2010 @ 16:47:27

      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.


  9. Jun 3rd, 2010 @ 06:32:14

    I found this useful. I integrated some of the thumbnail code into my CakePHP app. Thanks very much for the post!


    • Jun 3rd, 2010 @ 14:05:38

      No problem. Glad it helped you out. ;)


  10. Jun 13th, 2010 @ 22:28:32

    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


    • Jun 13th, 2010 @ 22:37:23

      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.


  11. Jun 13th, 2010 @ 23:13:28

    Got it, thanks


  12. Jun 13th, 2010 @ 23:19:55

    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


    • Oct 21st, 2010 @ 10:41:51

      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.


  13. Jun 13th, 2010 @ 23:22:10

    sorry there´s more code but didn´t fit


  14. Jun 13th, 2010 @ 23:25:42

    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


    • Jun 13th, 2010 @ 23:30:21

      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. ;)


  15. Apr 2nd, 2011 @ 09:14:57

    Thank you very much your solution worked great. Kudos!


    • Apr 2nd, 2011 @ 10:13:11

      No problem Dominic. Glad it was helpful.


  16. Jan 31st, 2012 @ 08:25:24

    TY for this very good info.


Leave a comment

Please enclose code in [lang] tags. For example [php] echo 'hello world'; [/php]

* Name, Email, Comment are Required