【问题标题】:Replace a color with another color in an image with PHP用 PHP 将图像中的一种颜色替换为另一种颜色
【发布时间】:2025-01-10 00:25:01
【问题描述】:

是的,我知道 * 上有相关的问题,但它们不能完全按照我的需要工作。我正在尝试用另一种颜色替换图像的颜色。在下面的代码中,我将 (255,0,255) 替换为 (0,192,239)。 以下代码有效,但不能完美地替换粉红色(255,0,255)颜色上的新颜色,如您在输出图像中看到的,一些小点或粉红色边框仍然存在。

我怎样才能得到它的完美解决方案?

<?php
$filename = 'img/Mascots_Aviators_General-copy.png'; 
$im = imagecreatefrompng($filename);
$out = imagecreatetruecolor(imagesx($im), imagesy($im));
$transColor = imagecolorallocatealpha($out, 254, 254, 254, 127);
imagefill($out, 0, 0, $transColor);

for ($x = 0; $x < imagesx($im); $x++) {
    for ($y = 0; $y < imagesy($im); $y++) {
        $pixel = imagecolorat($im, $x, $y);

        $red = ($pixel >> 16) & 0xFF;
        $green = ($pixel >> 8) & 0xFF;
        $blue = $pixel & 0xFF;
        $alpha = ($pixel & 0x7F000000) >> 24;

        if ($red == 255 && $green == 0 && $blue == 255) {
            $red = 0;
            $green=192;
            $blue =239;
        }

        if ($alpha == 127) {
            imagesetpixel($out, $x, $y, $transColor);
        }
        else {
            imagesetpixel($out, $x, $y, imagecolorallocatealpha($out, $red, $green, $blue, $alpha));
        }
    }
} 
imagecolortransparent($out, $transColor);
imagesavealpha($out, TRUE); 
header('Content-type: image/png');
imagepng($out);

【问题讨论】:

  • 那是因为这些行不完全是(255,0,255) 。您需要实现颜色范围才能使其正常工作。
  • @Helix 是的,我已经尝试过保持范围 ($red = 245) && ($green >= 0 && $green = 245) 但输出仍然相同。我说的对吗?
  • 给我一秒钟,我会尝试发布答案。基础数学有问题。
  • 那么,新的解决方案有效吗?
  • @Helix 可能是,但不完全确定我将这段代码包含在我的库中,一旦完成,我也会为其他图像运行它,我会回复你

标签: php imagick


【解决方案1】:

编辑 2: 您可能需要优化某些内容并更改 hueAbsoluteError 以满足您的需求,但色调是启蒙和更清晰画质的方法(函数取自 https://gist.github.com/brandonheyer/5254516):

<?php
function RGBtoHSL( $r, $g, $b ) {
    $r /= 255;
    $g /= 255;
    $b /= 255;
    $max = max( $r, $g, $b );
    $min = min( $r, $g, $b );
    $l = ( $max + $min ) / 2;
    $d = $max - $min;
    if( $d == 0 ){
        $h = $s = 0;
    } else {
        $s = $d / ( 1 - abs( 2 * $l - 1 ) );
        switch( $max ){
            case $r:
                $h = 60 * fmod( ( ( $g - $b ) / $d ), 6 );
                if ($b > $g) {
                    $h += 360;
                }
                break;
            case $g:
                $h = 60 * ( ( $b - $r ) / $d + 2 );
                break;
            case $b:
                $h = 60 * ( ( $r - $g ) / $d + 4 );
                break;
        }
    }
    return array( round( $h, 2 ), round( $s, 2 ), round( $l, 2 ) );
}

function HSLtoRGB( $h, $s, $l ){
    $c = ( 1 - abs( 2 * $l - 1 ) ) * $s;
    $x = $c * ( 1 - abs( fmod( ( $h / 60 ), 2 ) - 1 ) );
    $m = $l - ( $c / 2 );
    if ( $h < 60 ) {
        $r = $c;
        $g = $x;
        $b = 0;
    } else if ( $h < 120 ) {
        $r = $x;
        $g = $c;
        $b = 0;
    } else if ( $h < 180 ) {
        $r = 0;
        $g = $c;
        $b = $x;
    } else if ( $h < 240 ) {
        $r = 0;
        $g = $x;
        $b = $c;
    } else if ( $h < 300 ) {
        $r = $x;
        $g = 0;
        $b = $c;
    } else {
        $r = $c;
        $g = 0;
        $b = $x;
    }
    $r = ( $r + $m ) * 255;
    $g = ( $g + $m ) * 255;
    $b = ( $b + $m  ) * 255;
    return array( floor( $r ), floor( $g ), floor( $b ) );
}

/* ---------------CHANGE THESE------------------- */
$colorToReplace = RGBtoHSL(255, 0, 255);
$hueAbsoluteError = 0.4;
$replacementColor = RGBtoHSL(0, 192, 239);
/* ---------------------------------------------- */

$filename = 'img/Mascots_Aviators_General-copy.png';
$im = imagecreatefrompng($filename);
$out = imagecreatetruecolor(imagesx($im), imagesy($im));
$transColor = imagecolorallocatealpha($out, 254, 254, 254, 127);
imagefill($out, 0, 0, $transColor);

for ($x = 0; $x < imagesx($im); $x++) {
    for ($y = 0; $y < imagesy($im); $y++) {
        $pixel = imagecolorat($im, $x, $y);

        $red = ($pixel >> 16) & 0xFF;
        $green = ($pixel >> 8) & 0xFF;
        $blue = $pixel & 0xFF;
        $alpha = ($pixel & 0x7F000000) >> 24;

        $colorHSL = RGBtoHSL($red, $green, $blue);

        if ((($colorHSL[0]  >= $colorToReplace[0] - $hueAbsoluteError) && ($colorToReplace[0] + $hueAbsoluteError) >= $colorHSL[0])){
            $color = HSLtoRGB($replacementColor[0], $replacementColor[1], $colorHSL[2]);
            $red = $color[0];
            $green= $color[1];
            $blue = $color[2];
        }

        if ($alpha == 127) {
            imagesetpixel($out, $x, $y, $transColor);
        }
        else {
            imagesetpixel($out, $x, $y, imagecolorallocatealpha($out, $red, $green, $blue, $alpha));
        }
    }
}
imagecolortransparent($out, $transColor);
imagesavealpha($out, TRUE);
header('Content-type: image/png');
imagepng($out);

编辑: 更好的解决方案 - 确定颜色是否需要更换(使用此方法)。确定替换颜色的色调(我不知道它是否正确,我的意思是明暗)。将其涂在替换颜色上,使其具有阴影或 AA 的感觉。


因此,正如我在评论中所说,您需要确定这种颜色是否真的是 ping(深色、浅色等)。最简单的解决方案是对特定颜色通道应用绝对误差方法。可能有(肯定有)更好的通用方法,但我希望这样做:

$color = [255, 0, 255];
$colorAbsoluteError = [150, 0, 150];
$replacementColor = [0, 192, 239];
$filename = 'img/Mascots_Aviators_General-copy.png';
$im = imagecreatefrompng($filename);
$out = imagecreatetruecolor(imagesx($im), imagesy($im));
$transColor = imagecolorallocatealpha($out, 254, 254, 254, 127);
imagefill($out, 0, 0, $transColor);

for ($x = 0; $x < imagesx($im); $x++) {
    for ($y = 0; $y < imagesy($im); $y++) {
        $pixel = imagecolorat($im, $x, $y);

        $red = ($pixel >> 16) & 0xFF;
        $green = ($pixel >> 8) & 0xFF;
        $blue = $pixel & 0xFF;
        $alpha = ($pixel & 0x7F000000) >> 24;

        if ((($red  >= $color[0] - $colorAbsoluteError[0]) && ($color[0] + $colorAbsoluteError[0]) >= $red) &&
            (($green  >= $color[1] - $colorAbsoluteError[1]) && ($color[1] + $colorAbsoluteError[1]) >= $green) &&
            (($blue  >= $color[2] - $colorAbsoluteError[2]) && ($color[2] + $colorAbsoluteError[2]) >= $blue)){
            $red = $replacementColor[0];
            $green= $replacementColor[1];
            $blue = $replacementColor[2];
        }

        if ($alpha == 127) {
            imagesetpixel($out, $x, $y, $transColor);
        }
        else {
            imagesetpixel($out, $x, $y, imagecolorallocatealpha($out, $red, $green, $blue, $alpha));
        }
    }
}
imagecolortransparent($out, $transColor);
imagesavealpha($out, TRUE);
header('Content-type: image/png');
imagepng($out);

【讨论】:

  • 我首先运行了上面的代码,当我更改文件(图像)时它会失去输出图像质量,它采用蓝色背景并完全破坏了输出图像。
  • 我将尝试使用 HSL 颜色空间的另一种解决方案。
最近更新 更多