2011-08-26 18 views
34

Estoy tratando de crear un script PHP que toma una imagen:PHP GD Usar una imagen para enmascarar otra imagen, incluida la transparencia

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

y luego se aplica una imagen PNG:

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

como una máscara.

El resultado final tiene que mantener la transparencia:

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

Si es posible que yo quiero hacer esto en GD, ImageMagick no es realmente una opción en este momento.

¿Cómo podría hacerlo?

phalacee's post (in "PHP/GD, how to copy a circle from one image to another?") parece estar en la línea correcta pero específicamente necesito usar una imagen como máscara, no como forma.

Respuesta

48

Matt,

Si usted hace su png con el relleno óvalo blanco sobre fondo negro en lugar de relleno negro con el fondo transparente de la siguiente función hace.

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

?> 
+0

Gracias, esto es realmente útil – Matt

+0

Sé que esta publicación ha estado cerrada durante un tiempo, pero ¿cómo se veía el tiempo de ejecución cuando ustedes ejecutaron este script? Está tardando en promedio 20 segundos más o menos ... Mis imágenes de fuente/máscara son 250 x 170 píxeles ... ¿se trata de lo que ustedes están recibiendo? –

+0

Sin tener en cuenta, no estoy seguro de lo que estaba haciendo mal, pero funciona muy bien ahora: P Gracias chicos! –

10

He aquí una pequeña actualización a este script - He descubierto que si la imagen de origen tiene la transparencia en sí, la máscara (utilizando el script anterior) representa un píxel hacia atrás en lugar de píxel transparente de la imagen de origen. El script extendido a continuación toma en consideración la transparencia de la imagen fuente y la conserva.

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

Me gusta su secuencia de comandos, buena idea para eliminar la información de color adicional cuando el píxel es totalmente transparente. Debo señalar solo un pequeño error (IMO) aunque alguien quiera usar este método.

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

debe ser

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

también que no estoy 100% seguro de por qué estás mirando valores RGB aquí si el píxel es 100% transparente

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

y estoy no estoy seguro de que la fusión alfa del archivo de máscara funcione bien con su método, ya que solo se usa cuando los valores rgba son iguales a 0.

J La secuencia de comandos de ules también es bastante buena, aunque espera que la máscara sea una representación en escala de grises de una máscara (que es una práctica bastante común).

En la consulta de Matt buscaba un script que tomara solo la transparencia alfa de una imagen existente y la aplicara a otra imagen. Aquí hay un mod simple de la secuencia de comandos de Jules solo para tomar el alfa de la imagen de la máscara, y preservar el alfa de la imagen de origen.

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

?> 
1
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)); 
} 

Aquí es un poco de actualización de la primera función. Como ya tiene una imagen transparente, no necesita copiar los píxeles enmascarados. Esto ayudará a la ejecución un poco.

0

Una manera diferente para obtener un efecto similar es pegar el archivo PNG en una nueva imagen con un color de fondo único para eliminar temporalmente la transparencia, y establecer la transparencia color de la imagen png al color del círculo negro en su lugar. Luego, cuando lo coloca sobre la imagen jpeg, establece el nuevo color transparente en el color de la máscara.

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

Si remuestreo o semi-transparentes píxeles que están consiguiendo abajo, en lugar de utilizar un png como una máscara, puede desactivar la mezcla y dibujar una forma transparente en la imagen $mask lugar.

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

Nota: El código anterior no se ha probado, pero con suerte debe hacer lo que necesita.

Cuestiones relacionadas