tags:

views:

1717

answers:

14

Hello ,

I need to resize a picture to a fixed size. But it has to keep the factors between the width and height.

Say I want to resize a picture from 238 (w) X 182 (h) to 210 / 150

What I do now is:

Original width / target width = 1.333333 Original Height / target Height = 1.213333

Now I take the smallest factor.

Now I always have the right width since 238 / 1.333333 = 210. But the height is still 160. How do I get the height down to 160 without ruining the pic?

Do I need to crop? If so how?

+2  A: 

Do you have Imagick? If so you can load the image with it and do something like thumbnailimage()

There you can skip either of the parameters (height or width) and it will resize correctly.

Ólafur Waage
well i prefer to use gd since that's what I am using for everything. don't know if i have imagemagick
sanders
+1  A: 

Maybe take look at PHPThumb (it works with GD and ImageMagick)

Hippo
A: 

From the PHP GD manual:

When developing code to resize images, it is best not to use GD. When using the current GD methodologies, you are reading content from an image and manipulating it. By then writing that content to a brand new file, you are losing the EXIF data.

For purposes when you want to retain EXIF data, it is recommended that you compile in and use the PECL Imagick extension. It has great resizing methods built right in and the EXIF data is retained.

tharkun
I don't really need to keep the exif info.
sanders
yes, it is from the manual, but it's from the comments. take them with a grain of salt, as the comments express the views of the respective author, not a fixed truth - i doubt exif data is too important when generating thumbnails.
Schnalle
sure. then again the comments are moderated plus exif is not the only argument. Imagick is just better suited to resizing tasks.
tharkun
A: 

You'll have to crop 5 px off the top and bottom to get to your target size, however this could ruin the picture.

Really you should have a target width or height, then adjust the other dimension by the same proportion.

ck
yes but then the height or the width might not corrspond to its required value
sanders
Having specific image sizes is not a good idea when you don't have complete control over the source of the images. You can only scale them to fit.
ck
+7  A: 

This doesn't crop the picture, but leaves space around the new image if necessary, which I think is a better approach (than cropping) when creating thumbnails.

$w = 210;
$h = 150;

$orig_w = imagesx($original);
$orig_h = imagesy($original);

$w_ratio = $orig_w / $w;
$h_ratio = $orig_h / $h;

$ratio = $w_ratio > $h_ratio ? $w_ratio : $h_ratio;

$dst_w = $orig_w / $ratio;
$dst_h = $orig_h / $ratio;
$dst_x = ($w - $dst_w) / 2;
$dst_y = ($h - $dst_h) / 2;

$thumbnail = imagecreatetruecolor($w, $h);

imagecopyresampled($thumbnail, $original, $dst_x, $dst_y,
                   0, 0, $dst_w, $dst_h, $orig_w, $orig_h);
Can Berk Güder
Have you tested this since in my example the heigth is still 160 px and not 150 as it should be
sanders
yep, it works for me.
Can Berk Güder
height is 150 and width is 196. so there's a 7px margin on the left and right.
Can Berk Güder
height 196? It should be 150!
sanders
will you please read my comment again?
Can Berk Güder
+1  A: 

I'd much rather resize so that the image is contained within your limit and then fill out the blank parts. So in the above example you would resize so that the height is OK, then fill up (7 pixels on each end I think) to the left and right with a background color.

+1  A: 

Resizing images from within a PHP-sourced webpage can be problematic. Larger images (approaching 2+MB on disk) can be so large that they need more than 32MB of memory to process.

For that reason, I tend to either do it from a CLI-based script, with up-to 128MB of memory available to it, or a standard command line, which also uses as much as it needs.

# where to put the original file/image. It gets resized back 
# it was originally found (current directory)
SAFE=/home/website/PHOTOS/originals
# no more than 640x640 when finished, and always proportional
MAXSIZE=640
# the larger image is in /home/website/PHOTOS/, moved to .../originals
# and the resized image back to the parent dir.
cd $SAFE/.. && mv "$1" "$SAFE/$1" && \
   convert "$SAFE/$1" -resize $MAXSIZE\x$MAXSIZE\> "$1"

'convert' is part of the ImageMagick command line tools.

Alister Bulman
+1  A: 

are these thumbnails? if they are, cropping is not a huge problem. we do it all the time. i don't even shy away from cropping arbitrary ratios into crippled quadratic thumbnails, completely messing up the image (yes, i'm that hardcore), if it just looks good. this is a designers answer to a technical question, but still. don't fear the crop!

Schnalle
+1  A: 

I think there is a little confusion.. If you only want to resize it, keeping the original ratio, the right operation is:

$ratio = $originalWidth / $originalHeight;
if(//you start from the width, and want to find the height){
 $newWidth = $x;
 $newHeight = $x / $ratio;
}else if(//you start from the height, and want to find the width){
 $newHeight = $x;
 $newWidth = $x * $ratio;
}

Else, if the prefixed newWidth and newHeight cant be changed, and the thumb ratio is different from the original ratio, the only way is to crop or add borders to the thumb.

If your'e on to take the cut way, this function can help you (i wrote years ago in 5 minutes, maybe need some improvement.. it works only with jpg, for example ;):

    function thumb_cut($nomeimage, $source_path, $destination_path, $new_width, $new_height){
      list($width, $height, $type, $attr) = getimagesize($source_path.$nomeimage);
      if($type == 2){
        if($width > $new_width){
          $new_width = $width;
          $new_height = $height;
        }
        $compression = 100;
        $destimg = imagecreatetruecolor($new_width,$new_height) or die("Problems creating the image");
        $srcimg = ImageCreateFromJPEG($source_path.$nomeimage) or die("problem opening the image");
        $w = ImageSX($srcimg);
        $h = ImageSY($srcimg);
        $ro = $new_width/$new_height;
        $ri = $w/$h;
        if($ro<$ri){
          $par = "h";
        }else{
          $par = "w";
        }
        if($par == "h"){
          $ih = $h;
          $conv = $new_width/$new_height;
          $iw = $conv*$ih;
          $cw = ($w/2)-($iw/2);
          $ch = ($h/2)-($ih/2);
        }else if($par == "w"){
          $iw = $w;
          $conv = $new_height/$new_width;
          $ih = $conv*$iw;
          $cw = ($w/2)-($iw/2);
          $ch = ($h/2)-($ih/2);
        }
        ImageCopyResampled($destimg,$srcimg,0,0,$cw,$ch,$new_width,$new_height,$iw,$ih) or die("problems with resize");
        ImageJPEG($destimg,$destination_path.$nomeimage,$compression) or die("problems with storing new image");
      }
    }
DaNieL
+7  A: 

This solution is basically the same as Can Berk Güder's, but after having spent some time writing and commenting, I felt like posting.

This function creates a thumbnail that is exactly as big as the size you give it. The image is resized to best fit the size of the thumbnail. If it does not fit exactly in both directions, it's centered in the thumnail. Extensive comments explain the goings-on.

function thumbnail_box($img, $box_w, $box_h) {
    //create the image, of the required size
    $new = imagecreatetruecolor($box_w, $box_h);
    if($new === false) {
        //creation failed -- probably not enough memory
        return null;
    }


    //Fill the image with a light grey color
    //(this will be visible in the padding around the image,
    //if the aspect ratios of the image and the thumbnail do not match)
    //Replace this with any color you want, or comment it out for black.
    //I used grey for testing =)
    $fill = imagecolorallocate($new, 200, 200, 205);
    imagefill($new, 0, 0, $fill);

    //compute resize ratio
    $hratio = $box_h / imagesy($img);
    $wratio = $box_w / imagesx($img);
    $ratio = min($hratio, $wratio);

    //if the source is smaller than the thumbnail size, 
    //don't resize -- add a margin instead
    //(that is, dont magnify images)
    if($ratio > 1.0)
        $ratio = 1.0;

    //compute sizes
    $sy = floor(imagesy($img) * $ratio);
    $sx = floor(imagesx($img) * $ratio);

    //compute margins
    //Using these margins centers the image in the thumbnail.
    //If you always want the image to the top left, 
    //set both of these to 0
    $m_y = floor(($box_h - $sy) / 2);
    $m_x = floor(($box_w - $sx) / 2);

    //Copy the image data, and resample
    //
    //If you want a fast and ugly thumbnail,
    //replace imagecopyresampled with imagecopyresized
    if(!imagecopyresampled($new, $img,
        $m_x, $m_y, //dest x, y (margins)
        0, 0, //src x, y (0,0 means top left)
        $sx, $sy,//dest w, h (resample to this size (computed above)
        imagesx($img), imagesy($img)) //src w, h (the full size of the original)
    ) {
        //copy failed
        imagedestroy($new);
        return null;
    }
    //copy successful
    return $new;
}

Example usage:

$i = imagecreatefromjpeg("img.jpg");
$thumb = thumbnail_box($i, 210, 150);
imagedestroy($i);

if(is_null($thumb)) {
    /* image creation or copying failed */
    header('HTTP/1.1 500 Internal Server Error');
    exit();
}
header('Content-Type: image/jpeg');
imagejpeg($thumb);
gnud
actually this script doen't resize the original image. Is that also possible?
sanders
This script makes a copy of the original image. When resampling an image, you would have to make a copy. My example use destroys the original image (imagedestroy($i)). If you want to overwrite the image file, then just write the $thumb to the file "img.jpg" in the example.
gnud
Ok thanks. What if i want to have the whole pic resized and not make just a crop?
sanders
This is not a crop. Cropping is removing a part of the image. This function always uses the whole image.
gnud
Thanks for the excellent tutorial.
Nathan Long
It's either missing a } after if($new === false) { //creation failed -- probably not enough memory return null;or you have to remove the {
Adriano Varoli Piazza
oh, thanks. Don't know how that got in there - I've been using this function myself :)
gnud
A: 

@Can Berk Güder:

I like your short script. One problem though.

It works but not in all cases. Lets say I have an orriginal Image which is:

260 * 250 Then the cropfactor is 1.238 * 1.66666. In such case it doesn't take the 210 for a width rater it takes the heigt and then fills up the width with black pixels.

sanders
+2  A: 

Just a tip for high-quality fast thumbnail generation from large images: (from the php.net site)

If you do the thumbnail generation in two stages:

  1. From original to an intermediate image with twice the final dimensions, using a fast resize
  2. From the intermediate image to the final thumbnail using a high-quality resample

then this can be much faster; the resize in step 1 is relatively poor quality for its size but has enough extra resolution that in step 2 the quality is decent, and the intermediate image is small enough that the high-quality resample (which works nicely on a 2:1 resize) proceeds very fast.

Jason S
Thanks for your addition. Do you have some sample code. How would this fit in the code of gnut?
sanders
see the php link in my post
Jason S
A: 

See if this link helps: Crop-To-Fit an Image Using ASP/PHP

The technique is to:

  1. resize the image so that one dimension matches while the other exceeds the desired dimensions
  2. take out the desired size image from the center of resized image.

If you need to resize-to-fit an image inside given dimensions, have a look at this article.

Lastly if you are puzzled about how to do the resize math, remember that if proportions of source and destination images are same, this relation holds:

SourceWidth / SourceHeight = DestinationWidth / DestinationHeight

If you know three parameters, you can calculate the fourth one easily.

Salman A
A: 

Hi

The code above is okay for normal images. But it won't work for corrupted images. Please have a look here for the solution.

adnan