In PHP GD how can you convert a truecolor image to a palette without losing any colors. With imagetruecolortopallete
it doesn't work. I have a function that runs through all the colors and filters them (eg. grayscale). and It doesn't retain all colors, such as this picture of a Lamborghini-
Picture
This is my code
$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);
And it looks like this
You can see it loses colors. Is there any solution?
Also I don't think it has to do with my code but how imagetruecolortopalette
outputs it
Check the return on imagecolorstotal
, you are always getting 256 colors as the return no matter how high you set the number of colors to be dithered to. PNG-8 & GIF formats only support palettes of up to 256 colors. So even if you can use more than 256 in a palette, you'd have to save it back as true color for anyone to be able to use it, thus making the whole conversion process a waste of time. In other words imagetruecolortopallete
has an upper limit of 256 colors, you cannot go higher.
Here's how you can do it in truecolor, though it's resource intensive. Maybe look into imagemagick if you want to do this more efficiently.
$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');
Edit: Adding rgbtohsv & hsvtorgb functions. I did not write these functions.
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;
}
converting truecolor to palette is always going to involve losing colors, unless your pallette table is big enough to hold every unique color in the image. The lamborghini may very well have more than (say) 255 shades of yellow alone, and then you still need palette slots for the rest of the image on top of that. That's why there's truecolor images. There's no palette, but each pixel can have its own dedicated RGB triplet to more accurately represent the original scene.
That being said, I can't see how converting a truecolor picture will change yellow to red as in your sample images. How exactly are you doing this palette conversion? Sampling each pixel and doing some sort of transformation on it?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With