Resize and Crop Photos with PHP's GD Library

April 18th, 2009 by joyce johnston

I was recently working on a personal project where I wanted to upload photos and, at the same time, carry out the following tasks:

  • create a thumbnail of the uploaded image
  • resize the image if either its width or height were over 600px and store the resized image rather than the original
  • store a caption for the image in a database table

I considered using phpThumb; however, I ran into problems with ImageMagick when I tried saving an image to a file. Googling the problem suggested that I needed to use an older version of ImageMagick, which I was reluctant to do. So I turned to PHP’s GD library, and wrote a class to handle the image-related tasks.


Here’s how you might use the photo class:
[php]
<?php
//connect to database
$db = new mysqli(‘localhost’, ‘user_name’, ‘password’, ‘table’);
$db->connect();

if($_POST['add_photo']) {

//Save photo
if(isset($_FILES['image'])) {

//first include the photo class
@include ‘photo.php’;
//pass the image array to the photo class constructor
$photo = new Photo($_FILES['image']);

// validate the uploaded file to make sure it is indeed an image and doesn’t
// violate any size restrictions you place on uploads
if(count($errors = $photo->validate()) == 0) {
// if it is valid, we’ll make the thumb, passing the thumb size (50 px)
// and the directory where we want to store it
$errors = $photo->doThumb(50,’media/photos/thumbs/’);
}
if(count($errors) == 0 ) {
//if the thumb-making is successful, let’s look at the original uploaded file
//are we going to have to resize?

if($photo->getWidth() > 600 || $photo->getHeight() > 600) {
// Uh oh! This is too big! We’ll resize, preserving its original proportions
// we’re passing the maximum dimension and the directory
// where the resized photo will go to the doResize function
$errors = $photo->doResize(600,’media/photos/bigs/’);
}
else {
// the photo’s not too big, so we’ll just move it, passing the directory
// where we want it to go to the move function
$errors = $photo->move(‘media/photos/bigs/’);
}

if(count($errors) == 0) {
// we’ve made it this far; we can now add photo data to database.
// I’m using mysqli’s statement object here, which takes care
// of escaping dangerous characters for you.

// first make sure the photo is not already in the database
$query = "SELECT id from photos where photo = ? LIMIT 1";
$stmt = $db->prepare($query);
$stmt->bind_param("s",$_FILES['image']['name']);
$stmt->execute();
$stmt->store_result();

if($stmt->num_rows == 0 ) {
$query = "INSERT INTO photos (photo, caption) VALUES ( ?,?)";
$stmt = $db->prepare($query);
$stmt->bind_param("ss",$_FILES['image']['name'],$_POST['caption']);
$stmt->execute();
}
}
}
else {
// if there were errors, we’re going to display them along with the caption we entered
extract($_POST);
}
}
}
// we’ll also display the photos we’ve already uploaded, along
// their captions
$query = "SELECT * from photos ORDER BY sort_order";
$pics = $db->query($query);
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" >
<head>
<title>Photo class example</title>
</head>
<body>
<div id="content">
<?php
if(isset($errors) && count($errors) > 0) { ?>
<p>
Please correct the following errors:
</p>
<ul>
<?php
foreach($errors as $error) {
echo ‘<li>’.$error.’</li>’;
}
?>
</ul>
<?php } ?>
<!– create a form to upload images –>
<form action="" enctype="multipart/form-data" method="post">
<dl>
<dt>Photo</dt>
<dd><input type="file" name="image" value="" /></dd>
<dt>Caption</dt>
<dd><input type="text" name="caption" value="<?php echo $caption; ?>" size="88" max="200" /></dd>
</dl>
<p>
<input type="submit" name="add_photo" value="Add Photo" />
</p>
</form>
<!–display the images that we have uploaded –>
<ul>
<?php
while($row = $pics->fetch_assoc()) {
echo ‘<li><img src="/media/photos/thumbs/’.$row['photo'].’" /></li>’;
}
?>
</ul>
</div>
</body>
</html>
[/php]

Other Handy Functions

I also included in the photo class two functions to crop photos. The first, doCenterCrop(), crops to a height and width you specify, always cropping to the center of the source image.

[php]
// don’t forget to include the photo class
@include photo.php;
// instantiate the photo class. Without a file upload, you pass
// an array of info about the photo you’re going to crop. Note that
// because we’re not cropping a file upload (though you could
// certainly do that), the tmp_name and the name are the same.
$photo = new Photo(array(‘name’=>’mydirectory/myfile’,'tmp_name=>’mydirectory/myfile’));
// we pass the width and height we want to crop to to the doCenterCrop function
$width = 190;
$height = 131;
$photo->doCenterCrop($width,$height);
[/php]
Original photo.
Original photo

Same photo after doCenterCrop()
Same photo after doCenterCrop

If you are feeling a little bit fussy and want to specify exactly where the photo should be cropped, you can do that with the doFullCrop() function:
[php]
$photo = new Photo(array(‘error’=>0,’name’=>’mydirectory’,'tmp_name’=>’mydirectory/myphoto’));
// this time, we’re passing the top and left coordinates, along with the final
// width and height to the doFullCrop() method.
$top = 25;
$left = 15;
$width = 120;
$height = 147;
$photoc->doFullCrop($left,$top,$width,$height);
[/php]
Original photo
Original photo

Same photo after doFullCrop()
Same photo after doFullCrop()

Download the photo class.

34 Responses to “Resize and Crop Photos with PHP's GD Library”

  1. [...] Resize and Crop Photos with PHP’s GD Library [...]

  2. [...] Read more: Resize and Crop Photos with PHP’s GD Library [...]

  3. moonbug says:

    Hi there,

    Seems like a very nice class, but there doesn’t appear to be a download link anywhere?

  4. Joyce says:

    Whoops! It’s there now. Thanks for your comment.

  5. Tanya says:

    thanks so much! this looks like exactly what i am looking, saved me lots of time and headache! :) you rock!

  6. [...] » Resize and Crop Photos with PHP’s GD Library » Uncontentio.us [...]

  7. Jonathan says:

    Thanks for the class Joyce, this should save a lot of time. Might want to be careful posting $_SERVER['PHP_SELF'] in the example though, leaves open an XSS attack.

  8. Joyce says:

    Jonathan, thanks for the heads up. I’ve changed the code in my example. For those of you who, like me, were unaware of this security issue, here’s a good post to read:

    http://seancoates.com/xss-woes

    Be sure to read all of the comments.

  9. Brian says:

    I just looked at this post and will later look at your code as I need to so something similar. I was wondering if you are saving the pictures into the a database table or just information about the photo’s. I have heard pros and cons of both and was planning on saving directory information in the database and the photo’s in a file directory structure. I am trying to get other developers users as I just started developing web apps last month. Thanks.

  10. Joyce says:

    Brian, the class saves the photo to a file in a directory that you specify. It does not automatically save information about the photo to a database table. That’s something you have to do on your own. Thanks for your comment!

  11. Hey Joyce, great class! The only thing I can’t get working for the life of me is the doCenterCrop on a file upload. I think I’m missing something every obvious. Could you give an example of using the cropping methods on a file upload?

    Thank you so much.
    Jon

  12. Me again. I got it working, sorry for the wasted post. The image I was trying to upload was to large and it kept throwing errors.

    Thanks again for a great class.
    -Jon

  13. Joyce says:

    Jon — glad you got it working. Thank you for your comments!

  14. sadfrog says:

    Joyce – You sure made this seem simple. Wish I could do it. The class works flawlessly. Thanks.

  15. Joyce says:

    sadfrog – Glad you could use the class. Thank you for your comment!

  16. Tim says:

    Hey! Thanks so much for your script. It’s exactly what I need.
    One extra thing though. I’m trying to change the name of each file.

    For instance, upload myimage.jpg, then crop it to myimage_sml.jpg or something similar.

    I would ideally like to call the center crop a few times, and have myimage_sml, myimage_med, myimage_lrg. I can easily work out how to put them in different folders, but not with different names.

    Help!?

    timfazio@gmail.com

    Cheerio

  17. Tim says:

    On the same note, why won’t something like this work?

    // write the resized image to disk.
    $suffix = ‘sml’;
    $filename = $directory.($this->name).$suffix;
    $success = @imagejpeg($this->tmp,$filename);

    I’m rather confused!

    timfazio@gmail.com

  18. Joyce says:

    Tim, as I’m sure you know, you’ll have to update the photo class to do that. You could pass an additional parameter to the resize function so that it’s something like:

    function resize($directory,$suffix = '') {
    ...
    $name = ($suffix == '' ?  $this->name : str_replace('.',$suffix.'.',$this->name));
    $filename = $directory.$name;
    ...
    }
    

    This will insert the suffix before the file type (‘.jpg’, for example). You’ll also have to add the suffix parameter to the doCenterCrop function (or wherever you want to use it) and pass it along to resize. Be careful not to change $this->name in case you want to crop the same image without changing the name. Note also that the above code will work only if the uploaded image name has only one ‘.’ in it. You’ll have to write something fancier if uploaded images can be named something like my.image.jpg.

    I hope this helps! Thanks for your comments.

  19. Tim says:

    Thanks Joyce. I had, of course, forgotten that there’s an extension! Clever me.

    Luckily, images won’t be able to be named with a ‘.’ in them, which does simplify things.

    Sadly, I can’t seem to make your code work, which makes me think the image is being named/saved elsewhere in your code.

    $name = ($suffix == ” ? $this->name : str_replace(‘.’,$suffix.’.',$this->name));
    $filename = $directory.$name;

    does not seem to work, so I named $suffix right before it…
    $suffix = ‘sml’;
    $name = ($suffix == ” ? $this->name : str_replace(‘.’,$suffix.’.',$this->name));
    $filename = $directory.$name;

    This didn’t work either.

    So i simplified the code:
    $name = str_replace(‘.’,'sml.’,$this->name);
    $filename = $directory.$name;

    And even this didn’t seem to work.

    $name = ‘tim.jpg’;

    Even this didn’t change the file name, and the image continued to be cropped to the standard name.

    Can you think of what’s going wrong?!

    Many thanks,

    Tim

  20. Tim says:

    All’s working well — it was my mistake. Your code works brilliantly. Thank you again!

  21. Christian says:

    Good day, what would happen when I specify a crop dimension to a picture which is more than its original size?

  22. Joyce says:

    Hi Christian, I think I did prevent cropping in the doCenterCrop function if the image is smaller than the dimensions to crop to, but I don’t think I checked for that anywhere else. You’ll have to take a look at the code. In other cases, the photo will be resized to the larger size, which as you know, will not improve the appearance of the image. In the script that calls the class, you’ll have to check for image sizes that are too small if you want to prevent that. Or update the photo class.

    Thank you for your comment!

  23. solich says:

    Hi Christian. Thank you for your nice foto class. But, please…
    Its possible:
    first resize (no-save) next crop (no-save) and next save to server?
    How to for this?
    Thank you very much.
    Jan

  24. Joyce says:

    Hi solich,

    The class is not set up to do what you are trying to do. It is probably possible however. Check out the image functions in the PHP manual.

  25. Gareth Rees says:

    Hi Joyce – brilliant class, works nicely however I cannot seem to get the doCenterCrop to work correctly – what part am i implementing to turn on he center crop – I need all my thumbnails to be 160px x 120px
    Thanks

  26. Joyce says:

    Hi Gareth, Sorry it’s taken me so long to get back to you. Can you be more specific regarding the problem you are having? The syntax is as follows:

    $photo->doCenterCrop(final_width, final_height, path_to_directory_to_store_image);

    Joyce

  27. Gareth Rees says:

    Hi Joyce,

    In the above you currently have this:

    $photo = new Photo(array(‘name’=>’mydirectory/myfile’,'tmp_name=>’mydirectory/myfile’));
    $width = 190;
    $height = 131;
    $photo->doCenterCrop($width,$height);

    A) Do i replace the original line from: $photo = new Photo($_FILES['image']);

    I am not sure that the whole: array(‘name’=>’mydirectory/myfile’,'tmp_name=>’mydirectory/myfile’) part is or as to where the additional code, Can you just detail where i should add the insertion? :)

    Thank you

  28. Gareth Rees says:

    Also is it possible to do a Center Crop which is propositional. i.e. I have pictures of cars 160×120 as they usually are wide image.

    Which i’d guess result in just showing the door – is it possible to do a full crop until the biggest x or y is met – then do center crop so that there is only a small amount of clipping usually on the sides? as the smallest would be 120?

    I think the above sounds right :)
    Thanks for your time!

  29. Joyce says:

    Hi Gareth,

    I hope I am answering your question… There are two steps in cropping a picture. First you have to instantiate the photo object and you do that by passing the $_FILES['image'] array to the class constructor like this:

    $photo = new Photo($_FILES['image']);

    Then you have to pass the height and width you would like to crop the image to and the directory where you want to store the cropped image to the doCenterCrop function.

    I have not set up the class to run a succession of cropping and resizing functions against a photo, but I think you could do it, either by checking out the GD functions at php.net or by resizing using my class, and then cropping. When you do the crop, you’d have to re-instantiate the class using the resized photo. It may take some fiddling with the class to get it to work. You’ll have to try it out.

    I hope this helps!

    Joyce

  30. Emmanuel says:

    Hey Joyce, thank you for this class, we are using it on our intranet and it works flawlessly!

    Have a good day.

  31. France says:

    Thank you so much Joyce, the crop function is what I was trying to do for days and days, I was going to give up! :-)

    France

  32. Joyce says:

    Emmanuel & France: Thank you for your comments! I’m glad this is proving useful to you!

  33. France says:

    Hello Joyce,
    is it possible to increase the quality of the uploaded resized image?
    I imagine I have to change something in photo.php but can’t find what to change..

    Thanks.
    France

  34. Joyce says:

    Hi France,

    I did not include an option to specify quality in the class, since the default seems to work well for web use. However, PHP’s imagejpeg function, which called as one of the steps in the class’s resize function, does take an optional quality parameter. You would obviously have to update the class to use it. Check out the PHP manual for details.

    Thanks for your comment!