Как я могу заменить один цвет другим в прозрачном изображении png 24 alpha с помощью GD

Я пытался:

$index = imagecolorresolve ( $im,  0,0,0 ); // get black
imagecolorset($im, $index, 255, 0, 255); // SET NEW COLOR

Кажется, это работает с png 8, но не с 24, и если я делаю это с 8, получается все странно из-за сглаживания.

Вот полный тестовый код, который я использую. (это всего лишь тестовый код, так что будьте осторожны).

function LoadPNG($imgname, $color = false)
{        
    $im = @imagecreatefrompng($imgname);
    imagealphablending($im, false); 

    if($color) {
      $index = imagecolorresolve ( $im,  0,0,0 ); // get black
      imagecolorset($im, $index, 255, 0, 255); // SET NEW COLOR
    }

    imageAlphaBlending($im, true);
    imageSaveAlpha($im, true);

    return $im;
}

header('Content-Type: image/png');

$img = LoadPNG("head.png", "red");

imagepng($img);
imagedestroy($img);

person merlincam    schedule 04.02.2011    source источник
comment
Это не отвечает на ваш вопрос, но я использую pngcolorizealpha в сочетании с некоторым imagefilledpolygon()   -  person Mikhail    schedule 04.02.2011
comment
В конечном итоге это будут сложные детали, представляющие части автомобиля. Поэтому я не могу рисовать их как многоугольники. (Это можно сделать, но я не могу.)   -  person merlincam    schedule 04.02.2011


Ответы (5)


Вы можете попробовать следующее:

  • прокрутить все точки
  • получить цвет этой точки
  • если он соответствует вашему цвету A, установите для этого пикселя желаемый цвет B

Код:

for ($x=imagesx($im); $x--; ) {
    for ($y=imagesy($im); $y--; ) {
        $c = imagecolorat($im, $x, $y);
        if ($c[0] == 0 && $c[1] == 0 && $c[2] == 0) {
            // here we use the new color, but the original alpha channel
            $colorB = imagecolorallocatealpha($im, 255, 0, 255, $c[3]);
            imagesetpixel($im, $x, $y, $colorB);
        }
    }
}

Надеюсь это поможет!

person aorcsik    schedule 04.02.2011

Основываясь на решении inti, я сделал скрипт, который работает:

$imgname = "yourimage.png";
$im = imagecreatefrompng($imgname);
imagealphablending($im, false);
for ($x = imagesx($im); $x--;) {
    for ($y = imagesy($im); $y--;) {
        $rgb = imagecolorat($im, $x, $y);
        $c = imagecolorsforindex($im, $rgb);
        if ($c['red'] < 40 && $c['green'] < 40 && $c['blue'] < 40) { // dark colors
            // here we use the new color, but the original alpha channel
            $colorB = imagecolorallocatealpha($im, 255, 0, 255, $c['alpha']);
            imagesetpixel($im, $x, $y, $colorB);
        }
    }
}
imageAlphaBlending($im, true);
imageSaveAlpha($im, true);
header('Content-Type: image/png');
imagepng($im);
imagedestroy($im);

Я хотел бы оптимизировать его, потому что он довольно медленный

person Benjamin Crouzier    schedule 05.07.2011
comment
В зависимости от внутреннего представления значений пикселей простая замена циклов for может повысить производительность. Кэширование возвращаемых значений imagecolorallocate() тоже стоит попробовать, но GD должен (надеюсь) уже кэшировать это. - person ComFreek; 15.12.2014

эта функция заменит 1 или все цвета на 1 новый цвет, сохраняя уровни прозрачности (в противном случае границы, вероятно, будут выглядеть ужасно, если для рисования границ использовалась ЧАСТИЧНАЯ прозрачность).

ПОЛНЫЙ ОТВЕТ НА ПОДОБНОЕ СООБЩЕНИЕ

<?php 

function colorizeKeepAplhaChannnel( $inputFilePathIn, $targetRedIn, $targetGreenIn, $targetBlueIn, $outputFilePathIn ) {
    $im_src = imagecreatefrompng( $inputFilePathIn );
    $im_dst = imagecreatefrompng( $inputFilePathIn );
    $width = imagesx($im_src);
    $height = imagesy($im_src);

    // Note this: FILL IMAGE WITH TRANSPARENT BG
    imagefill($im_dst, 0, 0, IMG_COLOR_TRANSPARENT);
    imagesavealpha($im_dst,true);
    imagealphablending($im_dst, true);

    $flagOK = 1;
    for( $x=0; $x<$width; $x++ ) {
        for( $y=0; $y<$height; $y++ ) {
            $rgb = imagecolorat( $im_src, $x, $y );
            $colorOldRGB = imagecolorsforindex($im_src, $rgb);
            $alpha = $colorOldRGB["alpha"];
            $colorNew = imagecolorallocatealpha($im_src, $targetRedIn, $targetGreenIn, $targetBlueIn, $alpha);

            $flagFoundColor = true;
            // uncomment next 3 lines to substitute only 1 color (in this case, BLACK/greys)
/*
            $colorOld = imagecolorallocatealpha($im_src, $colorOldRGB["red"], $colorOldRGB["green"], $colorOldRGB["blue"], 0); // original color WITHOUT alpha channel
            $color2Change = imagecolorallocatealpha($im_src, 0, 0, 0, 0); // opaque BLACK - change to desired color
            $flagFoundColor = ($color2Change == $colorOld);
*/

            if ( false === $colorNew ) {
                //echo( "FALSE COLOR:$colorNew alpha:$alpha<br/>" );
                $flagOK = 0; 
            } else if ($flagFoundColor) {
                imagesetpixel( $im_dst, $x, $y, $colorNew );
                //echo "x:$x y:$y col=$colorNew alpha:$alpha<br/>";
            } 
        }
    }
    $flagOK2 = imagepng($im_dst, $outputFilePathIn);

    if ($flagOK && $flagOK2) {
        echo ("<strong>Congratulations, your conversion was successful </strong><br/>new file $outputFilePathIn<br/>");
    } else if ($flagOK2 && !$flagOK) {
        echo ("<strong>ERROR, your conversion was UNsuccessful</strong><br/>Please verify if your PNG is truecolor<br/>input file $inputFilePathIn<br/>");
    } else if (!$flagOK2 && $flagOK) {
        $dirNameOutput = dirname($outputFilePathIn)."/";
        echo ("<strong>ERROR, your conversion was successful, but could not save file</strong><br/>Please verify that you have PERMISSION to save to directory $dirName <br/>input file $inputFilePathIn<br/>");
    } else {
        $dirNameOutput = dirname($outputFilePathIn)."/";
        echo ("<strong>ERROR, your conversion was UNsuccessful AND could not save file</strong><br/>Please verify if your PNG is truecolor<br/>Please verify that you have PERMISSION to save to directory $dirName <br/>input file $inputFilePathIn<br/>");
    }

    echo ("TargetName:$outputFilePathIn wid:$width height:$height CONVERTED:|$flagOK| SAVED:|$flagOK2|<br/>");
    imagedestroy($im_dst);
    imagedestroy($im_src);
}

$targetRed = 255;
$targetGreen = 255;
$targetBlue = 0;

//$inputFileName = 'frameSquareBlack_88x110.png';
$inputFileName = 'testMe.png';
$dirName = "../img/profilePics/";
$nameTemp = basename($inputFileName, ".png");
$outputFileName = $nameTemp."_$targetRed"."_$targetGreen"."_$targetBlue.png";
$inputFilePath = $dirName.$inputFileName;
$outputFilePath = $dirName.$outputFileName;

//echo "inputFileName:$inputFilePath<br>outputName:$outputFilePath<br>";
colorizeKeepAplhaChannnel( $inputFilePath, $targetRed, $targetGreen, $targetBlue, $outputFilePath);
?>
<br/><br/>
Original <br/>
<img src="<?php echo $inputFilePath; ?>">
<br /><br />Colorized<br/>
<img src="<?php echo $outputFilePath; ?>">
<br />
person tony gil    schedule 15.12.2014

Хороший способ сделать это — использовать paintOpaqueImage(), который также позволяет использовать цветовой допуск.

$targetColor = "#0074AD";
$fill = "#0074AA";
$tolerance = 30000;

$im = new Imagick( "yourimage.png");
if ($im->paintOpaqueImage ( $targetColor , $fill , $tolerance) ){

    $im->writeImage("yourimage.png"); 

}

Вы можете увидеть документ о допустимости в http://www.imagemagick.org/script/command-line-options.php#fuzz

person Francisco Wiegand Cruz    schedule 29.12.2014

Эта функция работает как шарм:

public function ImageToBlackAndWhite($im) {
    for ($x = imagesx($im); $x--;) {
        for ($y = imagesy($im); $y--;) {
            $rgb = imagecolorat($im, $x, $y);
            $r = ($rgb >> 16) & 0xFF;
            $g = ($rgb >> 8 ) & 0xFF;
            $b = $rgb & 0xFF;
            $gray = ($r + $g + $b) / 3;
            if ($gray < 0xFF) {

                imagesetpixel($im, $x, $y, 0xFFFFFF);
            }else
                imagesetpixel($im, $x, $y, 0x000000);
        }
    }
    imagefilter($im, IMG_FILTER_NEGATE);

}
person Amed    schedule 12.01.2013