简体   繁体   中英

PHP GD crop & scale image

I'm using jQuery's imgAreaSelect plugin in order to crop an image and save the thumbnail to use in cases where, for example, the ratio changes. Unfortunelly the results are far from what I would expect, and I can't get it right. The image gets resized as a whole instead of being cropped.

Here's the test example :

<?php

/***
*
* $_GET returned values
*
* x1 = 0
* x2 = 400
* y1 = 66
* y2 = 258
* w = 400
* h = 192
* folder = widethumb
* filename = IMG_4591.jpg
* scale = 48
*
* Original image properties
*
* width = 600px
* height = 900px
*
***/

define('DOCROOT', realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR);

extract($_GET);

$fn = $filename;
$filename = DOCROOT.$filename;

list($width, $height) = getimagesize($filename);

$src = imagecreatefromjpeg($filename);
$dst = imagecreatetruecolor($w, $h);
imagecopyresampled($dst, $src, 0, 0, (int) $x1, (int) $y1, (int) $w, (int) $h, $width, $height);

header('Content-Type: image/jpeg');
imagejpeg($dst);

What am I mising here?

Cheers!

From PHP Documentation:

bool imagecopyresampled ( resource $dst_image , resource $src_image , int $dst_x , int $dst_y , int $src_x , int $src_y , int $dst_w , int $dst_h , int $src_w , int $src_h )

imagecopyresampled() copies a rectangular portion of one image to another image, smoothly interpolating pixel values so that, in particular, reducing the size of an image still retains a great deal of clarity.

In other words, imagecopyresampled() will take an rectangular area from src_image of width src_w and height src_h at position (src_x,src_y) and place it in a rectangular area of dst_image of width dst_w and height dst_h at position (dst_x,dst_y).

So to get the result you are looking for, you need to avoid scaling. for that use:

imagecopy($dst, $src, 0, 0, $x1, $y1, $w, $h);

// this can also be done but is less efficient (over 10 times slower)
imagecopyresampled($dst, $src, 0, 0, (int) $x1, (int) $y1, $w, $h, $w, $h);

Here we are are taking the same sized rectangle from source as we are putting it into destination image.
I have just tested it and it works just fine.

Update: I have just tried again on my test server and it is working fine. I'm using following code:

$filename = "test.jpg";

extract($_GET);

$src = imagecreatefromjpeg($filename);
$dst = imagecreatetruecolor($w, $h);
imagecopy($dst, $src, 0, 0, $x1, $y1, $w, $h);

// this is over 10 times slower, as we are only cropping we should use imagecopy
//imagecopyresampled($dst, $src, 0, 0, $x1, $y1, $w, $h, $w, $h);

header('Content-Type: image/jpeg');
imagejpeg($dst);

And I'm calling it like this:

http://localserver/test/gd_crop.php?x1=906&y1=267&w=501&h=355

Performance Update
As we are not resizing we can simply use imagecopy . Performance of 3 functions as i have measured is given below.

imagecopyresampled    69ms
imagecopyresized      5.5ms
imagecopy             4.5ms

So there is an order of 10 speed difference between resampled and the two other functions.

I have finally come up with following line in place of imagecopyresampled function, try it, I have also updated the above code listing:

imagecopy($dst, $src, 0, 0, $x1, $y1, $w, $h);

Use the WideImage library instead.

This is my own cropping function:

function createThumbnail($file, $cropX, $cropY, $cropWidth, $cropHeight, $desiredWidth, $desiredHeight, $shrink = false)
{
    if(file_exists(MPS_ROOT_PATH . "$file") && $cropWidth && $cropHeight)
    {
        $source_path = MPS_ROOT_PATH . $file;

        list( $source_width, $source_height, $source_type ) = getimagesize( $source_path );
        switch ( $source_type )
        {
            case IMAGETYPE_GIF:
                $source_gdim = imagecreatefromgif( $source_path );
                break;

            case IMAGETYPE_JPEG:
                $source_gdim = imagecreatefromjpeg( $source_path );
                break;

            case IMAGETYPE_PNG:
                $source_gdim = imagecreatefrompng( $source_path );
                break;

            default:
                return false;
        }

        if(!$desiredWidth)
        {
            // Desired width not set, computing new width based on original 
            // image's aspect ratio...
            $desiredWidth = $cropWidth * ($desiredHeight / $cropHeight);
        }

        if(!$desiredHeight)
        {
            // Desired height not set, computing new height based on original
            // image's aspect ratio
            $desiredHeight = $cropHeight * ($desiredWidth / $cropWidth);
        }

        if(!$desiredWidth || !$desiredHeight)
        {
            // Desired height or width not set. 
            // Halting image processing and returning file
            return $file;
        }

        $source_aspect_ratio = $cropWidth / $cropHeight;
        $desired_aspect_ratio = $desiredWidth / $desiredHeight;

        if($shrink)
        {
            // Shrink to fit flag set. Inverting computations to make image fit
            // within the desired dimensions...
            if($source_aspect_ratio > $desired_aspect_ratio)
            {
                // Source image is wider than desired aspect ratio, 
                // setting thumbnail width to the desired width and the height 
                // will be computed based on the original image's aspect ratio
                $temp_width = $desiredWidth;
                $temp_height = (int) ($desiredWidth / $source_aspect_ratio);
            }
            else
            {
                // Source image is taller than desired aspect ratio, 
                // setting thumbnail height to the desired height and the width 
                // will be computed based on the original image's aspect ratio
                $temp_height = $desiredHeight;
                $temp_width = (int) ($desiredHeight * $source_aspect_ratio);
            }
        }
        // shrink to fit not set
        else
        {
            if($source_aspect_ratio > $desired_aspect_ratio)
            {
                // Source image is wider than desired aspect ratio, 
                // setting thumbnail height to the desired height to fill the 
                // desired aspect ratio and the width will be computed based on 
                // the original image's aspect ratio
                $temp_height = $desiredHeight;
                $temp_width = (int) ($desiredHeight * $source_aspect_ratio);
            }
            else
            {
                // Source image is taller than desired aspect ratio, 
                // setting thumbnail width to the desired width to fill the 
                // desired aspect ratio and the width will be computed based on 
                // the original image's aspect ratio");
                $temp_width = $desiredWidth;
                $temp_height = (int) ($desiredWidth / $source_aspect_ratio);
            }
        }

        $temp_gdim = imagecreatetruecolor($temp_width, $temp_height);

        // Copying a $cropWidth x $cropHeight image from the source 
        // file at ($cropX, $cropY) and resampling it to fit the temporary 
        // $temp_width x $temp_height thumbnail at (0, 0)
        imagecopyresampled(
            $temp_gdim,
            $source_gdim,
            0, 0,
            $cropX, $cropY,
            $temp_width, $temp_height,
            $cropWidth, $cropHeight
        );

        $x0 = ($desiredWidth - $temp_width) / 2;
        $y0 = ($desiredHeight - $temp_height) / 2;
        // Positioning the temporary $temp_width x $temp_height thumbnail in 
        // the center of the final $desiredWidth x $desiredHeight thumbnail...
        // Creating final thumbnail canvas at $desiredWidth x $desiredHeight
        $desired_gdim = imagecreatetruecolor($desiredWidth, $desiredHeight);

        $white = imagecolorallocate($desired_gdim, 255, 255, 255);
        imagefill($desired_gdim, 0, 0, $white);
        // Filling final thumbnail canvas with white

        // Copying a $temp_width x $temp_height image from the temporary 
        // thumbnail at (0, 0) and placing it in the final 
        // thumbnail at ($x0, $y0)
        imagecopy(
            $desired_gdim,
            $temp_gdim,
            $x0, $y0,
            0, 0,
            $temp_width, $temp_height
        );

        $pathInfo = pathinfo($file);
        $thumbFile = "images/thumbs/thumb_" . basename($pathInfo["filename"]) . ".jpg";

        if(imagejpeg($desired_gdim, MPS_ROOT_PATH . $thumbFile, 80))
        {
            return $thumbFile;
        }
        else
        {
            return 1;
        }
    }
    else
    {
        echo "Image File Does not exist or Invalid crop parameters!";
        return false;
    }
}

Here's a function that you can pass the destination dimension to and will scale and crop from the center, maintain aspect ratio, and will scale up. This is easy to implement with the picture element for responsive design. If you change your destination dimensions, just delete the outputted files and this will recreate the images in their absence.

    <?php
    function scaleCrop($src, $dest, $destW, $destH, $anchor){
        if(!file_exists($dest) && is_file($src) && is_readable($src)){
            $srcSize = getimagesize($src);
            $srcW = $srcSize[0];
            $srcH = $srcSize[1];
            $srcRatio = $srcW / $srcH;
            $destRatio = $destW / $destH;
            $img = (imagecreatefromjpeg($src));
            $imgNew = imagecreatetruecolor($destW, $destH);

            if ($srcRatio < $destRatio){
                $scale = $srcW / $destW;
            }
            elseif($srcRatio >= $destRatio){
                $scale = $srcH / $destH;
            }
            $srcX = ($srcW - ($destW * $scale)) / 2;
            if($anchor = 'middle'){
                $srcY = ($srcH - ($destH * $scale)) / 2;
            }
            elseif($anchor = 'top'){
                $srcY = 0;
            }
            elseif($anchor = 'bottom'){
                $srcY = $srcH - ($destH * $scale);
            }
            if($srcX < 0){$srcX = 0;};
            if($srcY < 0){$srcY = 0;};
            imagecopyresampled($imgNew, $img, 0, 0, $srcX, $srcY, $destW, $destH, $destW * $scale, $destH * $scale);
            imagejpeg($imgNew, $dest, 70);
            imagedestroy($img);
            imagedestroy($imgNew);
        }
        return $dest;
    }
    ?>

<img src="<?php echo scaleCrop('srcfolder/srcfile.jpg', 'destfolder/destfile.jpg', 320, 240, 'top'); ?>">

Why don't you look into using imagemagik; it's great for image manipulation and cropping is just a simple case of using cropImage($width, $height, $x, $y);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM