3

I'd like to make transparent anything outside a predefined polygon in PHP. Let's say, you have the shape of your head then anything else of the image should be binned. Thanks a lot in advance! To make myself clear, here's some code I've been working on:

$dat = dirname(__FILE__)."/foto_".time().".png";
$img = imagecreatefrompng("foto.png");
$points = array(93,36,147,1,255,-5,294,37,332,114,327,189,315,249,303,291,290,327,260,360,205,404,165,407,131,376,86,325,62,236,61,155,66,96,77,57,87,45);
$schwarz = ImageColorAllocate ($img, 0, 0, 0);
imagefilledpolygon($img, $points, count($points)/2, $schwarz);
imagepng($img, $dat);

The original image can be found here and the result here. Everything except the black area should be discarded.

Jan
  • 42,290
  • 8
  • 54
  • 79
  • 1
    please try and make a start, and when stuck post code. –  Mar 06 '12 at 08:35
  • Given that PHP is not a graphical language, you need to give us a clue on how you are trying to display this "polygon". Are you trying you generate an image? If so, which extension are you using? Are you trying to do this with CSS? Please elaborate on exactly what you expect the end result to be and where you expect it to be visible. – DaveRandom Mar 06 '12 at 10:06
  • Ok, to clarify consider the following example: http://www.chipwreck.de/blog/software/cwcrop/cwcrop-demo/. Now I don't want the light area to be cropped but the darkened. Additionally this should not be a rectangle but a polygon. The result shall then be merged with another image (I already know how to do that). – Jan Mar 06 '12 at 11:02
  • So are you saying you want to *remove* the highlighted square from the image, or remove everything *except* the highlighted square? – DaveRandom Mar 06 '12 at 11:10
  • I want to remove everything except the highlighted area (does not have to be a square) – Jan Mar 06 '12 at 11:45

2 Answers2

5

I know it's a little late but I looked all over the web for a solution to this problem and couldn't find an all-in-one solution. Many of the posts I did find involved editing the image on a per-pixel basis which I wasn't too fond of doing. After piecing together multiple samples from multiple sites, here's what I came up with:

resizeCropPolygonImage()

function resizeCropPolygonImage($source, $dest = null, 
        $newWidth = null, $newHeight = null, $startX = 0, $startY = 0, 
        $points = array(), $numCoords = 2) {

    // Added in $numCoords in case we want to do 3D image processing in the future
    // (currently we do not process anything other than 2D coordinates)
    $points = array(100,115, 124,65, 192,65, 216,115, 192,165, 124,165);
    $numPoints = count($points) / $numCoords;

    // If there are not enough points to draw a polygon, then we can't perform any actions
    if ($numPoints < 3) {
        return;
    }

    // Get the original image's info
    list($width, $height, $file_type) = getimagesize($source);

    /******* Here I am using a custom function to resize the image  *********
     ******* keeping the aspect ratio.                              *********
     ******* You'll have to add in your own re-sizing logic         *********
    // Resize the source (using dummy vars because we don't want our
    // start x & y to be overwritten)
    scaleDimensions($width, $height, $newWidth, $newHeight, $dummyX = null, $dummyY = null);
    For simplicity sake, I'll just set the width and height to the new width and height
    *************************************************************************/
    $width = $newWidth;
    $height = $newHeight;

    switch ($file_type) {
    case 1:
        $srcImage = imagecreatefromgif($source);
        if (function_exists(ImageGIF)) {
            $imgType = "gif";
        } else {
            $imgType = "jpeg";
        }
        break;
    case 2:
        $srcImage = imagecreatefromjpeg($source);
        $imgType = "jpeg";
        break;
    case 3:
        $srcImage = imagecreatefrompng($source);
        $imgType = "png";
        break;
    default:
        return;
    }

    // Setup the merge image from the source image with scaling
    $mergeImage = ImageCreateTrueColor($width, $height);
    imagecopyresampled($mergeImage, $srcImage, 0, 0, 0, 0, $width, $height, imagesx($srcImage), imagesy($srcImage));

    /******** This is probably the part that you're most interested in *******/
    // Create the image we will use for the mask of the polygon shape and
    // fill it with an uncommon color
    $maskPolygon = imagecreatetruecolor($width, $height);
    $borderColor = imagecolorallocate($maskPolygon, 1, 254, 255);
    imagefill($maskPolygon, 0, 0, $borderColor);

    // Add the transparent polygon mask
    $transparency = imagecolortransparent($maskPolygon, imagecolorallocate($maskPolygon, 255, 1, 254));
    imagesavealpha($maskPolygon, true);
    imagefilledpolygon($maskPolygon, $points, $numPoints, $transparency);

    // Apply the mask
    imagesavealpha($mergeImage, true);
    imagecopymerge($mergeImage, $maskPolygon, 0, 0, 0, 0, $width, $height, 100);

    /******* Here I am using a custom function to get the outer     *********
     ******* perimeter of the polygon. I'll add this one in below   ********/
    // Crop down to just the polygon area
    $polygonPerimeter = getPolygonCropCorners($points, $numCoords);
    $polygonX = $polygonPerimeter[0]['min'];
    $polygonY = $polygonPerimeter[1]['min'];
    $polygonWidth = $polygonPerimeter[0]['max'] - $polygonPerimeter[0]['min'];
    $polygonHeight = $polygonPerimeter[1]['max'] - $polygonPerimeter[1]['min'];

    // Create the final image
    $destImage = ImageCreateTrueColor($polygonWidth, $polygonHeight);
    imagesavealpha($destImage, true);
    imagealphablending($destImage, true);
    imagecopy($destImage, $mergeImage, 
            0, 0, 
            $polygonX, $polygonY,
            $polygonWidth, $polygonHeight);

    // Make the the border transparent (we're assuming there's a 2px buffer on all sides)
    $borderRGB = imagecolorsforindex($destImage, $borderColor);
    $borderTransparency = imagecolorallocatealpha($destImage, $borderRGB['red'],
            $borderRGB['green'], $borderRGB['blue'], 127);
    imagesavealpha($destImage, true);
    imagealphablending($destImage, true);
    imagefill($destImage, 0, 0, $borderTransparency);

    if (!$dest) {
        // If no dest was given, then return to browser
        header('Content-Type: image/png');
        imagepng($destImage);
    } else {
        // Output image will always be png
        $dest .= '.png';

        // Save to destination
        imagepng($destImage, $dest);
    }

    // Destroy remaining images
    imagedestroy($maskPolygon);
    imagedestroy($srcImage);
    imagedestroy($destImage);

    // Only return a value if we were given a destination file
    if ($dest) {
        return $dest;
    }
}

getPolygonCropCorners()

function getPolygonCropCorners($points, $numCoords) {
    $perimeter = array();

    for ( $i = 0; $i < count($points); $i++ ) {
        $axisIndex = $i % $numCoords;

        if (count($perimeter) < $axisIndex) {
            $perimeter[] = array();
        }

        $min = isset($perimeter[$axisIndex]['min']) ? $perimeter[$axisIndex]['min'] : $points[$i];
        $max = isset($perimeter[$axisIndex]['max']) ? $perimeter[$axisIndex]['max'] : $points[$i];

        // Adding an extra pixel of buffer
        $perimeter[$axisIndex]['min'] = min($min, $points[$i] - 2);
        $perimeter[$axisIndex]['max'] = max($max, $points[$i] + 2);
    }

    return $perimeter;
}

Using this function, I can turn this image

Large giraffes

into this one

Small giraffes

It may not be entirely complete but I think it's definitely a good starting off point.


EDIT

  • 09/19/2014 - Added transparency to the border
Bryce Siedschlaw
  • 4,136
  • 1
  • 24
  • 36
  • if there is any white area and send point to your function there is some non-merged picture. Exxample : http://cdn.netmoda.com/items/1092799/cropped_0.jpg.png?i=4 – Muhammet Arslan Jul 10 '15 at 17:38
  • I'm sorry, but I'm not quite sure what you mean. Which part of the picture is not being merged? Do you have a before and after picture? – Bryce Siedschlaw Jul 13 '15 at 15:23
  • Hi, thanks for your comment. I solved my problem but there is a problem also. The edges of cropped picture are so sharpen. How can i make them softer ? – Muhammet Arslan Jul 15 '15 at 04:54
  • I don't think I can help you with that one. Unfortunately, it's outside the scope of my knowledge on the subject. Try searching for existing SO tickets and create a new one if you can't find any. – Bryce Siedschlaw Jul 15 '15 at 15:56
  • Thank you very much. Your class saves lots of hours. thanks again. keep up the good work. – amilaishere Sep 24 '15 at 12:14
1

You can use ImageArtist which is a GD wrapper authored by me to make image manipulation insanely easy.

see the code for cropping a polygon, you just need to pass the coordinates in percentage or as pixel values

$pentagon = new PolygonShape("./morning.jpeg");
$pentagon->scale(60);
$pentagon->push(new Node(50,0, Node::$PERCENTAGE_METRICS));
$pentagon->push(new Node(75,50, Node::$PERCENTAGE_METRICS));
$pentagon->push(new Node(62.5,100, Node::$PERCENTAGE_METRICS));
$pentagon->push(new Node(37.5,100, Node::$PERCENTAGE_METRICS));
$pentagon->push(new Node(25,50, Node::$PERCENTAGE_METRICS));
$pentagon->build(); //this will build the polygon
$triangle->save("./finalImage.png",IMAGETYPE_PNG);
//see the documentation for many other methods available.

This is the final resultenter image description here

Imal Hasaranga Perera
  • 9,683
  • 3
  • 51
  • 41