web-dev-qa-db-de.com

PHP Gd Verwenden Sie ein Bild, um ein anderes Bild einschließlich der Transparenz zu maskieren

Ich versuche, ein PHP -Skript zu erstellen, das ein Bild aufnimmt:

image1
http://i.stack.imgur.com/eNvlM.png

und wendet dann ein PNG-Bild an:

mask
http://i.stack.imgur.com/iJr2I.png

als maske.

Das Endergebnis muss transparent bleiben:

result
http://i.stack.imgur.com/u0l0I.png

Wenn überhaupt möglich, möchte ich dies in Gd tun, ImageMagick ist momentan keine Option.

Wie würde ich das angehen? 

phalacees Beitrag (in "PHP/Gd, wie kopiere ich einen Kreis von einem Bild auf ein anderes?") scheint auf der richtigen Linie zu liegen, aber ich muss ein Bild speziell als Maske verwenden, nicht als Form .

36
Matt

Matt, 

Wenn Sie Ihr Png mit der ovalen weißen Füllung auf schwarzem Hintergrund statt mit schwarzer Füllung mit transparentem Hintergrund machen, wird die folgende Funktion ausgeführt.

<?php
// Load source and mask
$source = imagecreatefrompng( '1.png' );
$mask = imagecreatefrompng( '2.png' );
// Apply mask to source
imagealphamask( $source, $mask );
// Output
header( "Content-type: image/png");
imagepng( $source );

function imagealphamask( &$picture, $mask ) {
    // Get sizes and set up new picture
    $xSize = imagesx( $picture );
    $ySize = imagesy( $picture );
    $newPicture = imagecreatetruecolor( $xSize, $ySize );
    imagesavealpha( $newPicture, true );
    imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) );

    // Resize mask if necessary
    if( $xSize != imagesx( $mask ) || $ySize != imagesy( $mask ) ) {
        $tempPic = imagecreatetruecolor( $xSize, $ySize );
        imagecopyresampled( $tempPic, $mask, 0, 0, 0, 0, $xSize, $ySize, imagesx( $mask ), imagesy( $mask ) );
        imagedestroy( $mask );
        $mask = $tempPic;
    }

    // Perform pixel-based alpha map application
    for( $x = 0; $x < $xSize; $x++ ) {
        for( $y = 0; $y < $ySize; $y++ ) {
            $alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) );
            $alpha = 127 - floor( $alpha[ 'red' ] / 2 );
            $color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) );
            imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $alpha ) );
        }
    }

    // Copy back to original picture
    imagedestroy( $picture );
    $picture = $newPicture;
}

?>
50
Jules_Text

Hier ist ein kleines Upgrade für dieses Skript. Ich habe festgestellt, dass, wenn das Quellbild selbst Transparenz aufweist, die Maske (mit dem obigen Skript) ein hinteres Pixel anstelle des transparenten Pixels des Quellbilds zeichnet. Das untenstehende erweiterte Skript berücksichtigt die Transparenz der Quellbilder und behält sie bei.

// Load source and mask
$source = imagecreatefrompng( '1.png' );
$mask = imagecreatefrompng( '2.png' );
// Apply mask to source
imagealphamask( $source, $mask );
// Output
header( "Content-type: image/png");
imagepng( $source );

function imagealphamask( &$picture, $mask ) {
// Get sizes and set up new picture
$xSize = imagesx( $picture );
$ySize = imagesy( $picture );
$newPicture = imagecreatetruecolor( $xSize, $ySize );
imagesavealpha( $newPicture, true );
imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) );

// Resize mask if necessary
if( $xSize != imagesx( $mask ) || $ySize != imagesy( $mask ) ) {
    $tempPic = imagecreatetruecolor( $xSize, $ySize );
    imagecopyresampled( $tempPic, $mask, 0, 0, 0, 0, $xSize, $ySize, imagesx( $mask ), imagesy( $mask ) );
    imagedestroy( $mask );
    $mask = $tempPic;
}

// Perform pixel-based alpha map application
for( $x = 0; $x < $xSize; $x++ ) {
    for( $y = 0; $y < $ySize; $y++ ) {
        $alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) );

            if(($alpha['red'] == 0) && ($alpha['green'] == 0) && ($alpha['blue'] == 0) && ($alpha['alpha'] == 0))
            {
                // It's a black part of the mask
                imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) ); // Stick a black, but totally transparent, pixel in.
            }
            else
            {

                // Check the alpha state of the corresponding pixel of the image we're dealing with.    
                $alphaSource = imagecolorsforindex( $source, imagecolorat( $source, $x, $y ) );

                if(($alphaSource['alpha'] == 127))
                {
                    imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) ); // Stick a black, but totally transparent, pixel in.
                } 
                else
                {
                    $color = imagecolorsforindex( $source, imagecolorat( $source, $x, $y ) );
                    imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $color['alpha'] ) ); // Stick the pixel from the source image in
                }


            }
    }
}

// Copy back to original picture
imagedestroy( $picture );
$picture = $newPicture;
}
10
user1436297

Ich mag Ihr Skript, eine gute Idee, um zusätzliche Farbinformationen zu entfernen, wenn das Pixel völlig transparent ist. Ich sollte nur einen kleinen Fehler (IMO) aufzeigen, wenn jemand diese Methode verwenden möchte.

$color = imagecolorsforindex( $source, imagecolorat( $source, $x, $y ) );

sollte sein

$color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) );

ich bin auch nicht zu 100% sicher, warum Sie hier die RGB-Werte überprüfen, wenn das Pixel zu 100% transparent ist

if(($alpha['red'] == 0) && ($alpha['green'] == 0) && ($alpha['blue'] == 0) && ($alpha['alpha'] == 0))
...

ich bin nicht sicher, ob Alpha-Blending aus der Maskendatei mit Ihrer Methode gut funktionieren würde, da sie nur verwendet wird, wenn die Werte für rgba alle gleich 0 sind.

Das Skript von Jules ist auch ziemlich gut, obwohl erwartet wird, dass die Maske eine Graustufendarstellung einer Maske ist (was ziemlich üblich ist). 

In Matts Abfrage ging es ihm um ein Skript, das nur die Alphatransparenz eines vorhandenen Bildes entnimmt und auf ein anderes Bild anwendet. Hier ist ein einfacher Mod des Jules-Skripts, nur um das Alpha aus dem Maskenbild herauszugreifen und das Alpha des Quellbildes beizubehalten.

<?php
// Load source and mask
$source = imagecreatefrompng( '1.png' );
$mask = imagecreatefrompng( '2.png' );
// Apply mask to source
imagealphamask( $source, $mask );
// Output
header( "Content-type: image/png");
imagepng( $source );

function imagealphamask( &$picture, $mask ) {
    // Get sizes and set up new picture
    $xSize = imagesx( $picture );
    $ySize = imagesy( $picture );
    $newPicture = imagecreatetruecolor( $xSize, $ySize );
    imagesavealpha( $newPicture, true );
    imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) );

    // Resize mask if necessary
    if( $xSize != imagesx( $mask ) || $ySize != imagesy( $mask ) ) {
        $tempPic = imagecreatetruecolor( $xSize, $ySize );
        imagecopyresampled( $tempPic, $mask, 0, 0, 0, 0, $xSize, $ySize, imagesx( $mask ), imagesy( $mask ) );
        imagedestroy( $mask );
        $mask = $tempPic;
    }

    // Perform pixel-based alpha map application
    for( $x = 0; $x < $xSize; $x++ ) {
        for( $y = 0; $y < $ySize; $y++ ) {
            $alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) );
            //small mod to extract alpha, if using a black(transparent) and white
            //mask file instead change the following line back to Jules's original:
            //$alpha = 127 - floor($alpha['red'] / 2);
            //or a white(transparent) and black mask file:
            //$alpha = floor($alpha['red'] / 2);
            $alpha = $alpha['alpha'];
            $color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) );
            //preserve alpha by comparing the two values
            if ($color['alpha'] > $alpha)
                $alpha = $color['alpha'];
            //kill data for fully transparent pixels
            if ($alpha == 127) {
                $color['red'] = 0;
                $color['blue'] = 0;
                $color['green'] = 0;
            }
            imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $alpha ) );
        }
    }

    // Copy back to original picture
    imagedestroy( $picture );
    $picture = $newPicture;
}

?>
9
niall.campbell

Es gibt eine Bibliothek namens WideImage, die Alphamasken unterstützt. http://wideimage.sourceforge.net/documentation/manipulating-images/

3
itsjavi
for ($y = 0; $y < $ySize; $y++) {
  $alpha = imagecolorsforindex($mask, imagecolorat($mask, $x, $y));
  $alpha = 127 - floor($alpha['red'] / 2);
  if (127 == $alpha) {
    continue;
  }
  $color = imagecolorsforindex($picture, imagecolorat($picture, $x, $y));
  imagesetpixel($newPicture, $x, $y, imagecolorallocatealpha(
    $newPicture, $color['red'], $color['green'], $color['blue'], $alpha));
}

Hier ist ein kleines Upgrade für die erste Funktion. Da Sie bereits über ein transparentes Bild verfügen, müssen Sie die maskierten Pixel nicht kopieren. Dies wird der Ausführung ein wenig helfen.

1
Gardner

Eine andere Möglichkeit, einen ähnlichen Effekt zu erzielen, besteht darin, die PNG-Datei mit einer eindeutigen Hintergrundfarbe in ein neues Bild einzufügen, um die Transparenz vorübergehend zu entfernen, und stattdessen die transparente Farbe des PNG-Bildes auf die Farbe des schwarzen Kreises setzen. Wenn Sie es dann über dem JPEG-Bild platzieren, setzen Sie die neue transparente Farbe auf die Farbe der Maske.

// Load the Black Circle PNG image
$png = imagecreatefrompng( 'mask.png' );
$width = imagesx( $png );
$height = imagesy( $png );

// Create a mask image
$mask = imagecreatetruecolor( $width, $height );
// We'll use Magenta as our new transparent colour - set it as the solid background colour.
$Magenta = imagecolorallocate( $mask, 255, 0, 255 );
imagefill( $mask, 0, 0, $Magenta );

// Copy the png image onto the mask. Destroy it to free up memory.
imagecopyresampled( $mask, $png, 0, 0, 0, 0, $width, $height, $width, $height );
imagedestroy( $png );

// Set the black portion of the mask to transparent.
$black = imagecolorallocate( $mask, 0, 0, 0 );
imagecolortransparent( $mask, $black );

// Load JPEG image.
$jpg = imagecreatefromjpeg( 'image.jpg' );
$j_width = imagesx( $jpg );
$j_height = imagesx( $jpg );

// Enable alpha blending and copy the png image
imagealphablending( $jpg, true );
imagecopyresampled( $jpg, $mask, 0, 0, 0, 0, $j_width, $j_height, $width, $height );
imagedestroy( $mask );

// Set the new transparent colour and output new image to browser as a png.
$Magenta = imagecolorallocate( $jpg, 255, 0, 255 );
imagecolortransparent( $jpg, $Magenta );
imagepng( $jpg );

Wenn Sie durch erneute Abtastung oder halbtransparente Pixel nach unten gehen, können Sie anstelle eines PNG als Maske die Überblendung deaktivieren und stattdessen eine transparente Form auf das $mask-Bild zeichnen.

// Load JPEG Image.
$jpg = imagecreatefromjpeg( 'image.jpg' );
$width = imagesx( $jpg );
$height = imagesx( $jpg );

// Create mask at same size with an opaque background.
$mask = imagecreatetruecolor( $width, $height );
$Magenta = imagecolorallocate( $mask, 255, 0, 255 );
imagefill( $mask, 0, 0, $Magenta );

// Disable alpha blending and draw a transparent shape onto the mask.
$transparent = imagecolorallocatealpha( $mask, 255, 255, 255, 127 );
imagealphablending( $mask, false );
imagefilledellipse( $mask, round( $width / 2 ), round( $height / 2 ), $width, $height, $transparent );

// Paste the mask onto the original image and set the new transparent colour.
imagealphablending( $jpg, true );
imagecopyresampled( $jpg, $mask, 0, 0, 0, 0, $width, $height, $width, $height );
imagedestroy( $mask );
$Magenta = imagecolorallocate( $jpg, 255, 0, 255 );
imagecolortransparent( $jpg, $Magenta );

// Output new image to browser as a png.
imagepng( $jpg );

Hinweis: Der obige Code wurde nicht getestet, sollte jedoch hoffentlich das tun, was Sie benötigen.

0
Shaun Cockerill