Как обрезать участок вне полигона? - PullRequest
3 голосов
/ 06 марта 2012

Я бы хотел сделать прозрачным что-либо вне предопределенного многоугольника в PHP.Допустим, у вас есть форма вашей головы, тогда все остальное на изображении должно быть сгруппировано.Заранее большое спасибо!Чтобы прояснить ситуацию, вот код, над которым я работал:

$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);

Исходное изображение можно найти здесь и результат здесь .Все кроме черной области следует отбросить.

Ответы [ 2 ]

4 голосов
/ 19 сентября 2014

Я знаю, что уже немного поздно, но я искал во всей сети решение этой проблемы и не смог найти комплексное решение.Многие из постов, которые я нашел, касались редактирования изображения на пиксельной основе, что я не слишком любил делать.После объединения нескольких образцов с нескольких сайтов я пришел к следующему:

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;
}

Используя эту функцию, я могу превратить это изображение

Large giraffes

в это

Small giraffes

Возможно, он не полностью завершен, но я думаю, что это определенно хорошая отправная точка.


РЕДАКТИРОВАТЬ

  • 09/19 / 2014 - добавлена ​​прозрачностьдо границы
1 голос
/ 10 сентября 2017

Вы можете использовать ImageArtist , который представляет собой оболочку GD, созданную мной, чтобы сделать манипулирование изображениями безумно простым.

см. Код для обрезки многоугольника, вам просто нужно передать координаты в процентах или в виде значений пикселей

$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.

Это окончательный результат enter image description here

...