Crop a png image with PHP, remove empty transparency
Asked Answered
B

2

7

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);
Bought answered 10/8, 2017 at 9:29 Comment(4)
Have you made an attempt yourself? Currently this is too broad imo.Sunstroke
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 !Bought
If you've written the code and you're asking about efficiency you probably want codereview.stackexchange.comSunstroke
There is already a built in php-gd function to do that for PHP 5 >= 5.5.0, PHP 7. Check my answer bellow.Diao
D
9

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

Diao answered 10/8, 2017 at 10:3 Comment(17)
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 workBought
The mistake is coming from my switch my bad, your solution is working perfectly, thank you so much !Bought
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 advanceBought
@Bought create a PHP info file and check if PHP is compiled with GD2. If not try to run apt-get install php5-gd and restart web server.Diao
Here is a screen of the GD part : imghost.io/image/Gu2o1 Should I reinstall gd ?Bought
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.Diao
I used the error reporting, nothing found, and the function exists.. Even when I reinstall GD apt-get tells me this is the last versionBought
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.Diao
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...Bought
@Bought 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.Diao
I tried what you said, and this is still the same issue. I have my new PNG (not even cropped) with a black background...Bought
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.Diao
I finally made it by using ImageMagick, which gives the same functions as gdBought
Nice alternative. Yes I should have thought of that, you can have same functionality by using ImageMagick.Diao
Thank you a lot for your help :)Bought
@ChristosLytras Is there any way to get the distance of each side cut by imagecropauto function? 4 example in this pic > i.sstatic.net/qxC9i.png, and the goal is to get the length of red line (maybe in px?). Thanks a lot <3.Chartier
Incase someone is still wondering how to use Imagick for debian/ubuntu - I have added my answer on how to do this using Imagick.Gussie
G
2

[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!

Gussie answered 4/7, 2019 at 5:43 Comment(2)
This still gives me a black background.Guth
I won't down vote for the false information, but imagecropauto works perfectly fine on Linux.Madeira

© 2022 - 2024 — McMap. All rights reserved.