• Flickr
  • Last.fm
  • Twitter
  • Linkr
  • About
  • Favourites
  • Comments
  • XBox

Cropping with GD

Posted on April 16, 2006 at 05:56PM

Thumbnails are something we use all the time. The problem is that often we just resize them down. Depending on the size of the image, this can make the thumbnail almost useless. To get around this, sometimes cropping is the answer. I actually thing a cropped thumbnail can be more effective because it doesn't show the entire image, making you more interested in seeing the full image.

As it turns out, GD is very capable of cropping images as well as resizing them. I've taken both methods to create cropped thumbnails of a slightly resized image.

The first thing I do is take the image and make it half its original size. From there I I create a box in the middle of the image. It centers itself using a bit of math. Then I take that box of the image and crop.

The code is actually fairly simple. The first thing I do is get the dimensions of the image I'm working with as well as set variables for the height and width of my cropped image.

<?php
$width = 100;
$height = 75;
$dimensions = getimagesize('path/to/image');
?>

Now we want to prepare a canvas with our defined height and load our original image into memory.

<?php
$canvas = imagecreatetruecolor($width,$height);
$piece = imagecreatefromjpeg('path/to/image');
?>

What we do at this stage is very important. This is where we do all our basic math to get some important numbers. The first numbers being the dimensions divided by 2 in order to reduce the image in half. The second numbers are gather co-ordinates within the resized numbers so that it can crop in the right place. I should not here that this code doesn't take into consideration as to whether the cropped thumbnail size is bigger than the original file or whether it's bigger than the resized copy of the original.

<?php
$newwidth = $dimensions[0] / 2;
$newheight = $dimensions[1] / 2;
$cropLeft = ($newwidth/2) - ($width/2);
$cropHeight = ($newheight/2) - ($height/2);
?>

Now we want to actually generate our cropped image using the height and width we specified before as well as place our cropping and resize the original image. When that's completed, we try to write the file to our server. If you wish, you could just immediately display it to the browser with some modification to the code, but I won't cover that here.

<?php
imagecopyresized($canvas, $piece, 0, 0, $cropLeft, $cropHeight, $width, $height, $newwidth, $newheight);
if (imagejpeg($canvas,'path/to/save/file',90)) {
echo 'Image crop successful';
} else {
echo 'Image crop failed';
}
?>

And finally we clear up some memory and finish the script.

<?php
imagedestroy($canvas);
imagedestroy($piece);
?>

Here's everything all together with some commented code.

<?php// Set our crop dimensions.
$width = 100;
$height = 75;
// Get dimensions of existing image
$dimensions = getimagesize('path/to/image');
// Prepare canvas
$canvas = imagecreatetruecolor($width,$height);
$piece = imagecreatefromjpeg('path/to/image');
// Prepare image resizing and crop -- Center crop location
$newwidth = $dimensions[0] / 2;
$newheight = $dimensions[1] / 2;
$cropLeft = ($newwidth/2) - ($width/2);
$cropHeight = ($newheight/2) - ($height/2);
// Generate the cropped image
imagecopyresized($canvas, $piece, 0,0, $cropLeft, $cropHeight, $width, $height, $newwidth, $newheight);
// Write image or fail
if (imagejpeg($canvas,'path/to/save/file',90)) {
echo 'Image crop successful';
} else {
echo 'Image crop failed';
}
// Clean-up
imagedestroy($canvas);
imagedestroy($piece);
?>

Hope this helps some developers out there. It works very well if put into a function or class with some basic modifications.

Comments

wow, nice tutorial!

I really learned something.

Very nice and useful tutorial. I must say I've been looking for a long time for something like this.

I have one problem though: I wish to end up with a square cropped image (50*50) but the script distorts the end result depending on the image it is fed.

Do you have any idea how I can work around this?

please excuse the intrussion and the petty post above. 10 minutes later and I found the sollution.

If it's of any help to any one trying to get square crops here's the trick: the scale ratio between normal height and normal width ( ex: 768*1024) is about 1.3.

Use that value to disproportionate the photo's newheight/newwidth (depending on a portrait or landscape photo)

Ex: for a landscape photo the fix is:
$newheight = ($dimensions[1] * 1.3) / 2;

Hope this helps anyone.

nice chapter for creating crop gd image ,very useful

And here's some code to do padded resizes (forces the exact width/height specified even if the image is skewed. It takes width and height as the first two arguments and the third is a RRGGBB colour like you'd use in CSS.

Some notes:
- $this->gd is an object which contains a GD class - in this case it's the original image that we're doing a resize of (hence why the original image's filename etc isn't ever passed - it's in $this->gd. Feel free to make your own class or just replace this with whatever works for you.

This is the web2gd method:
static function color_web2gd($image, $webColor) {
if(substr($webColor,0,1) == "#") $webColor = substr($webColor,1);
$r = hexdec(substr($webColor,0,2));
$g = hexdec(substr($webColor,2,2));
$b = hexdec(substr($webColor,2,2));

return imagecolorallocate($image, $r, $g, $b);

}

And the paddedResize method:
function paddedResize($width, $height, $backgroundColor = "FFFFFF") {
if(!$this->gd) return;

$width = round($width);
$height = round($height);


$newGD = imagecreatetruecolor($width, $height);
$bg = GD::color_web2gd($newGD, $backgroundColor);
imagefilledrectangle($newGD, 0, 0, $width, $height, $bg);

$destAR = $width / $height;
if ($this->width > 0 && $this->height > 0) {
// We can't divide by zero theres something wrong.

$srcAR = $this->width / $this->height;

// Destination narrower than the source
if($destAR > $srcAR) {
$destY = 0;
$destHeight = $height;

$destWidth = $height * $srcAR;
$destX = ($width - $destWidth) / 2;

// Destination shorter than the source
} else {
$destX = 0;
$destWidth = $width;

$destHeight = $width / $srcAR;
$destY = ($height - $destHeight) / 2;
}

imagecopyresampled($newGD, $this->gd, $destX, $destY, 0, 0, $destWidth, $destHeight, $this->width, $this->height);
}
$output = new GD();
$output->setGD($newGD);
return $output;
}

Hope the base code at least helps someone who sucks at Maths like I do :P

Thanks for a great tutorial!!!

Hi.

Great tutorial!

I'm still a big n00b when it comes to php (although I'm trying to learn... unsuccessfully), anyway I was wondering how you would combine this with a simple upload form? Ive tried several things but they never seem to work :(

Thanks.

Hi.
Sorry about that Ive fixed the problem.

Hi.
Sorry didn't notice your post before I entered mine... Eventually I figured out the way you mensioned.

Thanks anyway tho!

why is it double posting?

If you replace:
$newwidth = $dimensions[0] / 2;
$newheight = $dimensions[1] / 2;

With:
$newwidth = (($dimensions[0]-$width) / 3)*2;
$newheight = (($dimensions[1]-$height) / 3)*2;

It means it that (as long as teh original image is larger than the thumbnail size) the image won't scale itself down to a smaller size than the thumbnail size.

Its a bit better...

Just a thought I had, but as an addition to this, if you set an input type as "image" (<input type="image" .... >) with the src being the path to the image, when you click the image, it submits the form, but also throws in two more form variables--the x and y coords of where the click was performed (relative to the image). This could be useful for a thumbnail creation script by centering the box about the click origin rather than the center of the image.

H, come back to us in #surpass. We miss you.

This helped me get started with image manipulation, but it's not quite getting where I want.

I have a 480 x 480 image. I want to resize it o 240 x 240 and then crop out the middle "portrait" shaped rectangle 150px wide by 200px high. I've tried different combinations of numbers for the offsets, but I can't seem to get that rectangle without stretching the image.

Many thanks in advance!

Hello,

nicefull script, but it's not a cropping function ... it's a resizing function to create Thumb.

Good work

Here is a Zoom Cropped Script, after reading this it only added black bars to my images and not really cropped them, so I used imagecopy() to do cropping.

http://gorilla3d.com/v4/index.php/blog/entry/31

oh and the line 51 is

if (imagejpeg($canvas ,'images/thumbs',90 )) {

ok so i love this idea but there is a problem that i am having.

Warning: imagejpeg() [function.imagejpeg]: Unable to open 'images/thumbs' for writing in /home/.../upload_images.php on line 51
Image crop failed

i have set my folder to CHMOD 777. so i didn't think that there would be a problem. i will post my code so you can see what the problem might be.

$width = 100;
$height = 100;
// Get dimensions of existing image
$dimensions = getimagesize('images/large/'.$_FILES['file']['name']);
// Prepare canvas
$canvas = imagecreatetruecolor ($width,$height );
$piece = imagecreatefromjpeg( 'images/large/'.$_FILES['file']['name']);
// Prepare image resizing and crop -- Center crop location
$newwidth = (($dimensions[0]-$width) / 3)*2;
$newheight = (($dimensions[1]-$height) / 3)*2;
$cropLeft = ($newwidth/ 2) - ($width/2 );
$cropHeight = ($newheight/ 2) - ($height/2 );
// Generate the cropped image
imagecopyresized( $canvas, $piece, 0 ,0, $cropLeft, $cropHeight, $width, $height , $newwidth, $newheight);
// Write image or fail
if (imagejpeg($canvas ,'images/thumbs',90 )) {
echo 'Image crop successful';
} else {
echo 'Image crop failed' ;
}
// Clean-up
imagedestroy( $canvas);
imagedestroy( $piece);

any help would be much appreciated. thanx

Thanks for the code. For the disproportionate problem I have this fix:

$iCropWidth = 83;
$iCropHeight = 83;

$aDimensions = getimagesize($sUploadFile);

$iWidth = $aDimensions[0];
$iHeight = $aDimensions[1];

$iRatioWidth = 1;
$iRatioHeight = 1;

if ($iWidth > $iHeight)
{
$iRatioHeight = number_format($iWidth / $iHeight,1,'.','');
}
else if ($iWidth < $iHeight)
{
$iRatioWidth = number_format($iHeight / $iWidth,1,'.','');
}

$oCanvas = imagecreatetruecolor($iCropWidth,$iCropHeight);

switch ($sExtension)
{
case "gif":
$oPiece = imagecreatefromgif($sUploadFile);
break;
case "jpg":
$oPiece = imagecreatefromjpeg($sUploadFile);
break;
case "png":
$oPiece = imagecreatefrompng($sUploadFile);
break;
}

$iNewWidth = ($iWidth * $iRatioWidth) / 2;
$iNewHeight = ($iHeight * $iRatioHeight) / 2;
$iCropLeft = ($iNewWidth / 2) - ($iCropWidth / 2);
$iCropTop = ($iNewHeight / 2) - ($iCropHeight / 2);
imagecopyresized($oCanvas, $oPiece, 0, 0, $iCropLeft, $iCropTop, $iCropWidth, $iCropHeight, $iNewWidth, $iNewHeight);

Add comment

Comments for this post have been disabled.