2011-08-26 12 views
34

Próbuję utworzyć skrypt PHP, który pobiera obraz:PHP GD Użyj jednego obrazu w celu zamaskowania innego obrazu, w tym przejrzystości

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

a następnie stosuje obrazu PNG:

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

jako maska.

Wynik końcowy musi zachować przejrzystość:

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

Jeśli w ogóle możliwe, chcę zrobić to w GD, ImageMagick nie jest to opcja w tej chwili.

Co mam zrobić?

phalacee's post (in "PHP/GD, how to copy a circle from one image to another?") wydaje się być na właściwej linii, ale muszę użyć obrazu jako maski, a nie kształtu.

Odpowiedz

48

Matt

Jeśli dokonać png z owalnym wypełnieniem białym na czarnym tle zamiast czarnym wypełnieniem z przezroczystym tłem następująca funkcja robi.

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

Dziękuję, to jest naprawdę pomocne – Matt

+0

Wiem, że ten post był przez jakiś czas zamknięty, ale jak wyglądało środowisko uruchomieniowe, kiedy prowadziliście ten skrypt? Zajmuje to około 20 sekund ... Moje obrazy źródłowe/maski to 250 x 170 pikseli ... czy to, co dostajecie? –

+0

Zignorować, nie jestem pewien, co robiłem źle, ale teraz działa świetnie: P Dzięki chłopaki! –

10

Oto mały upgrade do tego skryptu - Uważam, że jeśli obraz źródłowy ma sama przejrzystość, maska ​​(przy użyciu skryptu powyżej) działki piksel tylną zamiast przezroczystego piksela obrazu źródłowego za. Poniższy rozszerzony skrypt bierze pod uwagę przejrzystość obrazu źródłowego i zachowuje go.

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

Podoba mi się twój skrypt, dobry pomysł, aby usunąć dodatkowe informacje o kolorze, gdy piksel jest całkowicie przezroczysty. Powinienem wskazać tylko niewielki błąd (IMO), chociaż ktoś chce skorzystać z tej metody.

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

powinny być

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

też nie jestem w 100% pewien, dlaczego jesteś sprawdzanie wartości RGB tutaj jeśli piksel jest w 100% przezroczysty

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

i jestem nie wiesz, czy mieszanie alpha z pliku maski działałoby dobrze z twoją metodą, ponieważ jest używane tylko wtedy, gdy wartości rgba są równe 0.

J Scenariusz ulesa jest całkiem niezły, choć spodziewa się, że maska ​​będzie reprezentacją maski w skali szarości (co jest dość powszechną praktyką).

W pytaniu Matta poszedł za skryptem, który pobiera tylko przezroczystość alfa z istniejącego obrazu i stosuje ją do innego obrazu. Oto prosty mod skryptu Julesa po to, aby pobrać alfa z obrazu maski i zachować alfa obrazu źródłowego.

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

Oto mały uaktualnienie do pierwszej funkcji. Ponieważ masz już przezroczysty obraz, nie musisz kopiować zamaskowanych pikseli. To trochę pomoże w wykonaniu.

0

Innym sposobem, aby uzyskać podobny efekt jest wklejenie pliku PNG na nowego obrazu z unikalnym kolorem tła, aby tymczasowo usunąć przejrzystości i ustawić przezroczyste zamiast tego kolor obrazu png zamiast czarnego koła. Następnie, po umieszczeniu go nad obrazem jpeg, ustawiasz nowy przezroczysty kolor na kolor maski.

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

Jeśli resampling lub półprzezroczyste piksele stają się w dół, zamiast używać PNG jako maska, możesz wyłączyć mieszanie i narysuj kształt na $mask przejrzystego obrazu zamiast.

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

Uwaga: Powyższy kod jest nieprzetestowany, ale powinien mieć nadzieję, że zrobi to, czego potrzebujesz.