PHP GD调色板颜色

6
PHP GD中,如何将一张真彩色图片转换为调色板,同时不会失去任何颜色。使用imagetruecolortopallete无法工作。我有一个函数,可以遍历所有颜色并过滤它们(例如灰度),但是它不能保留所有颜色,比如这张兰博基尼的图片-

alt text

这是我的代码

$im = imagecreatefrompng("lamborghini.png");
$degrees = 0;

$img = imagecreatetruecolor(imagesx($im), imagesy($im));
imagecopy($img, $im, 0, 0, 0, 0, imagesx($im), imagesy($im));
imagetruecolortopalette($img, true, 256);
$t = imagecolorstotal($img);
for ($i = 0; $i < $t; $i++) {
  $rgb = imagecolorsforindex($img, $i);
  $hsv =rgbtohsv($rgb['red'], $rgb['green'], $rgb['blue']);
  $h = $degrees;
  $s = $hsv['s'];
  $v = $hsv['v'];
  while ($h > 360) {$h -= 360;};
        $nrgb = hsvtorgb($h, $s, $v);
  imagecolorset($img, $i, $nrgb['r'], $nrgb['g'], $nrgb['b']);
}
imagecopy($im, $img, 0, 0, 0, 0, imagesx($img), imagesy($img));

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

imagepng($im);
imagedestroy($im);

而且它看起来像这样

alt text

你可以看到它失去了颜色。是否有任何解决方案
此外,我认为这与我的代码无关,而是与imagetruecolortopalette如何输出有关。

GD库的调色板转换速度很快,但非常不准确。如果您想使用调色板,我建议通过popen()使用命令行pngquant(https://github.com/pornel/improved-pngquant)。尽管会丢失一些颜色,但损失不会太大。 - Kornel
3个回答

5
检查imagecolorstotal的返回值,无论您将要抖动的颜色数量设置得多高,始终会返回256种颜色。 PNG-8和GIF格式只支持最多256种颜色的调色板。因此,即使您可以在调色板中使用超过256种颜色,您也必须将其保存为真彩色才能使任何人都能使用它,从而使整个转换过程变成一场浪费时间的活动。换句话说,imagetruecolortopallete最多支持256种颜色,您无法超过这个上限。

以下是如何在真彩色中完成此操作,但这需要耗费大量资源。如果您希望更有效地完成此操作,请考虑使用ImageMagick。

$im = imagecreatefrompng("lamb.png");
$img = imagecreatetruecolor(imagesx($im), imagesy($im));
$degrees = 0;
if ($degrees > 360) {$degrees = $degrees % 360 ;}

foreach (range(0, imagesx($im) - 1) as $x ) {
    foreach (range(0, imagesy($im) - 1) as $y) {
        $rgb = imagecolorat($im, $x, $y);
        $r = ($rgb >> 16) & 0xFF;
        $g = ($rgb >> 8) & 0xFF;
        $b = $rgb & 0xFF;
        $hsv = rgbtohsv($r,$g,$b);
        $rgb = hsvtorgb($degrees, $hsv['s'], $hsv['v']);
        imagesetpixel($img, $x,$y,imagecolorallocate($img, $rgb['r'], $rgb['g'], $rgb['b']));
    }
}

imagepng($img, 'lamb2.png');

编辑:添加rgbtohsv和hsvtorgb函数。这些函数不是我编写的。

function rgbtohsv ($R, $G, $B) {
 // HSV Results:Number 0-1
$HSL = array();

$var_R = ($R / 255);
$var_G = ($G / 255);
$var_B = ($B / 255);

$var_Min = min($var_R, $var_G, $var_B);
$var_Max = max($var_R, $var_G, $var_B);
$del_Max = $var_Max - $var_Min;

$V = $var_Max;

if ($del_Max == 0)
{
$H = 0;
$S = 0;
}
else
{
$S = $del_Max / $var_Max;

$del_R = ( ( ( $var_Max  - $var_R ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
$del_G = ( ( ( $var_Max  - $var_G ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
$del_B = ( ( ( $var_Max  - $var_B ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;

if ($var_R == $var_Max) $H = $del_B - $del_G;
else if ($var_G == $var_Max) $H = ( 1 / 3 ) + $del_R - $del_B;
else if ($var_B == $var_Max) $H = ( 2 / 3 ) + $del_G - $del_R;

if ($H<0) $H++;
if ($H>1) $H--;
}

$HSL['h'] = $H;
$HSL['s'] = $S;
$HSL['v'] = $V;

return $HSL;
}

function hsvtorgb ($H, $S, $V) 
{
$RGB = array();

if($S == 0)
{
$R = $G = $B = $V * 255;
}
else
{
$var_H = $H * 6;
$var_i = floor( $var_H );
$var_1 = $V * ( 1 - $S );
$var_2 = $V * ( 1 - $S * ( $var_H - $var_i ) );
$var_3 = $V * ( 1 - $S * (1 - ( $var_H - $var_i ) ) );

if ($var_i == 0) { $var_R = $V ; $var_G = $var_3 ; $var_B = $var_1 ; }
else if ($var_i == 1) { $var_R = $var_2 ; $var_G = $V ; $var_B = $var_1 ; }
else if ($var_i == 2) { $var_R = $var_1 ; $var_G = $V ; $var_B = $var_3 ; }
else if ($var_i == 3) { $var_R = $var_1 ; $var_G = $var_2 ; $var_B = $V ; }
else if ($var_i == 4) { $var_R = $var_3 ; $var_G = $var_1 ; $var_B = $V ; }
else { $var_R = $V ; $var_G = $var_1 ; $var_B = $var_2 ; }

$R = $var_R * 255;
$G = $var_G * 255;
$B = $var_B * 255;
}

$RGB['r'] = $R;
$RGB['g'] = $G;
$RGB['b'] = $B;

return $RGB;
}

什么是 rgbtohsvhsvtorgb - EscoMaji
@EscoMaji,我已经将那些函数添加到了帖子中。 - Klinky

2
将真彩色图片转换为调色板模式时,除非您的调色板表足够大以容纳图像中的每种独特颜色,否则总会失去颜色。拉姆博基尼(Lamborghini)可能拥有超过255个黄色阴影,而且还需要调色板插槽来支持图像的其余部分。这就是为什么有真彩色图像的原因。没有调色板,但是每个像素可以有自己专用的RGB三元组,以更准确地表示原始场景。
话虽如此,我无法理解将真彩色图片转换为红色样本图像中的样品。您具体是如何进行此调色板转换的?对每个像素进行采样并进行某种转换吗?

如果你省略RGB->HSV->RGB的循环,会发生什么?看看imagetruecolortopalette生成了什么?我猜你在rgbtohsv和/或hsvtorgb函数中出现了错误,因为即使是一个“坏”的调色板生成器也不会从本质上没有红色的源图像中产生那么多的红色。 - Marc B
我通过可选使用调色板(以提高速度)和真彩色(以提高质量)来解决了这个问题。 - Mark Lalor

0

你启用了 dither 吗?并且你指定了多少种颜色? - Chris Laplante
这是我所拥有的 imagetruecolortopalette($img, true, 256);,所以是的。 - Mark Lalor
尝试将颜色数量从“256”增加到“512”。如果有任何改进,请继续增加颜色数量,直到质量可接受。 - Chris Laplante

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接