views:

110

answers:

3

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

alt text

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

alt text

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

A: 

How about this function: http://www.php.net/manual/en/function.imagetruecolortopalette.php. It might take some messing with the number of colors to get it right, though.

SimpleCoder
That what I was already trying, see my edit
Mark
Do you have `dither` enabled? And how many colors did you specify?
SimpleCoder
This is what I have `imagetruecolortopalette($img, true, 256);` so yes
Mark
Try increasing the number of colors from `256` to, say, `512`. If there is any improvement, keep increasing the amount of colors until the quality is acceptable.
SimpleCoder
Tried that with one million and it didn't work :P
Mark
+1  A: 

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?

Marc B
Let me edit to explain
Mark
WHat happens if you eliminate the rgb->hsv->rgb roundtrip? See what the imagetruecolortopalette produces? I'm guessing youv'e got an error in the rgbtohsv and/or hsvtorgb function, as even a 'bad' palette generator wouldn't produce that many reds from a source image that essentially has none.
Marc B
No I resolved the problem by optionally using palette (for speed) and truecolor for quality.
Mark
+2  A: 

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');
Klinky