6

I'm currently trying to work with pictures and PHP, thanks to GD functions. Now I would like to modify the size of PNG pictures. Here is an example of a PNG I'd like to resize : enter image description here

The dotted line represent the border of the PNG, the background is transparent, and I only have a star lost on the middle of a large space. I'd like to crop this star, to get a simple square of the star (even if the new background becomes blank, It doesn't matter).

How could I do something like that efficiently ? I thought about doing a loop checking every pixel of the picture.. Trying to find where the image is, to finally crop with a little margin based on the minimum x / maximum X and minimum y / maximum y values, but If I start working with hundreds of pictures, It would be really long.

EDIT :

<?php

$file = "./crop.png";

$ext = pathinfo($file, PATHINFO_EXTENSION);

$image;

switch ($ext){
case 'png':
    $image = imagecreatefrompng($file);
    break;

case 'jpeg':
case 'jpg':
    $image = imagecreatefromjpeg($file);
    break;

case 'gif':
    $image = imagecreatefromgif($file);
    break;
}

$cropped = imagecropauto($image, IMG_CROP_DEFAULT);

    if ($cropped !== false) { // in case a new image resource was returned
        echo "=> Cropping needed\n";
        imagedestroy($image);    // we destroy the original image
        $image = $cropped;       // and assign the cropped image to $im
    }

    imagepng($image, "./cropped.png");
    imagedestroy($image);
saperlipopette
  • 1,603
  • 12
  • 26
  • Have you made an attempt yourself? Currently this is too broad imo. – Jonnix Aug 10 '17 at 09:32
  • Yes. Checking every pixel of the picture, cropping from (min X, min Y) to (max X, max Y), adding a little margin. It is working perfectly for one picture, but I'd like to apply this script to array of hundreds of pictures. Not sure a server will appreciate this type of looping script, I'm quite new with PHP and if there is a more efficient way to do this, I'm interested ! – saperlipopette Aug 10 '17 at 09:38
  • If you've written the code and you're asking about efficiency you probably want https://codereview.stackexchange.com – Jonnix Aug 10 '17 at 09:39
  • 2
    There is already a built in [tag:php-gd] function to do that for *PHP 5 >= 5.5.0, PHP 7*. Check my answer bellow. – Christos Lytras Aug 10 '17 at 10:08

2 Answers2

7

If you read and follow the documentation, you'll find a function called imagecropauto which does exactly what you want, it crops the alpha channel of the image.

Crop an PNG image with alpha channel

$im = imagecreatefrompng("./star-with-alpha.png");
$cropped = imagecropauto($im, IMG_CROP_DEFAULT);

if ($cropped !== false) { // in case a new image resource was returned
    imagedestroy($im);    // we destroy the original image
    $im = $cropped;       // and assign the cropped image to $im
}

imagepng($im, "./star-with-alpha-crop.png");
imagedestroy($im);

You can try it dirrectly to a php page using this code:

<body>

<img src="star-with-alpha.png">

<?php 

$im = imagecreatefrompng("./star-with-alpha.png");
$cropped = imagecropauto($im, IMG_CROP_DEFAULT);

if ($cropped !== false) { // in case a new image resource was returned
    imagedestroy($im);    // we destroy the original image
    $im = $cropped;       // and assign the cropped image to $im
}

imagepng($im, "./star-with-alpha-crop.png");
imagedestroy($im);

?>

<img src="star-with-alpha-crop.png">

</body>

The result

http://zikro.gr/dbg/php/crop-png/

Cropped image demo

Christos Lytras
  • 36,310
  • 4
  • 80
  • 113
  • Thank for your answer, it seems to be the function I was looking for ! But my file is not creating, I've put my code on the EDIT, I was using file_put_contents, it doesn't work, and even imagepng() doesn't work – saperlipopette Aug 10 '17 at 11:02
  • The mistake is coming from my switch my bad, your solution is working perfectly, thank you so much ! – saperlipopette Aug 10 '17 at 11:09
  • 1
    Hello, your solution is working great on my mac, but I just deployed my script on a server (debian). It is exactly the same php version (5.6.30) on my mac and on the server. When running the script on my computer, it crops well, but when I run it on the server, it crops nothing, and instead of a blank background, it becomes all black. Any idea why the same code is working on my mac but not on a distant server ? Thank in advance – saperlipopette Oct 05 '17 at 06:59
  • @saperlipopette create a PHP info file and check if PHP is compiled with [GD2](http://php.net/manual/en/image.installation.php). If not try to run `apt-get install php5-gd` and restart web server. – Christos Lytras Oct 05 '17 at 08:20
  • Here is a screen of the GD part : https://imghost.io/image/Gu2o1 Should I reinstall gd ? – saperlipopette Oct 05 '17 at 08:35
  • Enable all errors (`error_reporting(E_ALL)`) and run your code to check for errors. Check if all GD functions exists like `if (function_exists("imagecropauto"))` and if everything seems OK, then you should try to reinstall GD. – Christos Lytras Oct 05 '17 at 09:12
  • 1
    I used the error reporting, nothing found, and the function exists.. Even when I reinstall GD apt-get tells me this is the last version – saperlipopette Oct 05 '17 at 13:43
  • I think that this is a file permissions issue. Does your script user have read access to the file you're trying to open and also write access to the current directory so the GD `imagepng` can write and save the file? Try creating a script to read and write to a text file on the same directory you have the image and see the results. – Christos Lytras Oct 05 '17 at 14:22
  • I made a folder with 777 rights. The scripts simply crops a png and saves it to replace the old one. Still have the black background issue only on server, and not on local computer... – saperlipopette Oct 05 '17 at 20:56
  • @saperlipopette the issue is not with cropping the image by using the `imagecropauto`. Try to comment that line `$cropped = imagecropauto($im, IMG_CROP_DEFAULT);` and set `$cropped` variable to `false` and see if it saves the original image. For some reason your script can't read/write image data. – Christos Lytras Oct 05 '17 at 21:27
  • I tried what you said, and this is still the same issue. I have my new PNG (not even cropped) with a black background... – saperlipopette Oct 09 '17 at 11:45
  • 1
    The problem is not with the code but with the system/PHP/GD. I suggest you create a new question here or on superuser and get some help to make GD work with that Debian system. – Christos Lytras Oct 10 '17 at 00:29
  • I finally made it by using ImageMagick, which gives the same functions as gd – saperlipopette Oct 10 '17 at 12:35
  • 2
    Nice alternative. Yes I should have thought of that, you can have same functionality by using ImageMagick. – Christos Lytras Oct 10 '17 at 13:17
  • Thank you a lot for your help :) – saperlipopette Oct 11 '17 at 08:50
  • @ChristosLytras Is there any way to get the distance of each side cut by `imagecropauto` function? 4 example in this pic > https://i.stack.imgur.com/qxC9i.png, and the goal is to get the length of red line (maybe in px?). Thanks a lot <3. – Jim Nov 27 '17 at 05:58
  • Incase someone is still wondering how to use Imagick for debian/ubuntu - I have added my answer on how to do this using Imagick. – informer Jul 04 '19 at 05:44
1

[This is for ubuntu 12] The only problem with the imagecropauto is that it works only on Mac & Windows. And since most of the servers today use ubuntu/debain - this function is of no use. Instead use Imagick() for this. Here is a sample code I wrote which does exactly this:

    //Add background transmparent
    $background = 'none';
    $image = new Imagick($path);
    $image->trimImage(0);
   
    //add transparent border
    //border add start
    /** Set border format **/
    $borderWidth = 20;
    $borderColor = 'none';
    $borderPadding = 10;


    $imageWidth = $image->getImageWidth() + ( 2 * ( $borderWidth + 
    $borderPadding ) );
    $imageHeight = $image->getImageHeight() + ( 2 * ( $borderWidth + 
    $borderPadding ) );

Create Imagick object for final image with border

    $imageWithBorder = new Imagick();
    // Set image canvas
    $imageWithBorder->newImage( $imageWidth, $imageHeight, new ImagickPixel( 
    'none' ));
    // Create ImagickDraw object to draw border
    $border = new ImagickDraw();
    // Set fill color to transparent
    $border->setFillColor( 'none' );
    // Set border format
    $border->setStrokeColor( new ImagickPixel( $borderColor ) );
    $border->setStrokeWidth( $borderWidth );
    $border->setStrokeAntialias( false );

Draw border

    $border->rectangle(
        $borderWidth / 2 - 1,
        $borderWidth / 2 - 1,
        $imageWidth - ( ($borderWidth / 2) ),
        $imageHeight - ( ($borderWidth / 2) )
    );
    // Apply drawed border to final image
    $imageWithBorder->drawImage( $border );
    $imageWithBorder->setImageFormat('png');

Save Image

    // Put source image to final image
    $imageWithBorder->compositeImage(
            $image, Imagick::COMPOSITE_DEFAULT,
            $borderWidth + $borderPadding,
            $borderWidth + $borderPadding
    );
    
    $imageWithBorder->writeImage($path);

Recenter and fit to original image height and width

    $imageWithBorder->scaleImage(FINAL_WIDTH, FINAL_HEIGHT, true);
    $imageWithBorder->setImageBackgroundColor($background);
    $w = $imageWithBorder->getImageWidth();
    $h = $imageWithBorder->getImageHeight();
    $imageWithBorder->extentImage(FINAL_WIDTH, FINAL_HEIGHT, ($w  - 
    FINAL_WIDTH) / 2, ($h - FINAL_HEIGHT)/ 2);
    $imageWithBorder->writeImage($path);

Hope it helps. Cheers!

informer
  • 821
  • 6
  • 18