Is there any other attribute that affects how colors are displayed on an indexed image besides the palette?

Viewed 215

I am currently working on a Python script that inverts the colors of a given image file. It supports JPG, PNG and TGA file formats.

To test the TGA format, I downloaded a set of sample files. The two files that concern my question are CCM8.TGA and UCM8.TGA. These are respectively compressed and uncompressed indexed color 8-bit gamma integer images.

I am unfamiliar with all the various elements that take part when constructing an image so please forgive me if I use incorrect terminology or I state or miss the obvious.

What I am trying to do is to invert the color of these 'P' images getting the RGB palette, inverting it and then putting it back. The problem is that some of the color indexes are different to what I expect and what I get when I use ImageMagick to check the file attributes or visualize the files with GIMP, so there must be other elements at play that I am unaware of.

This is how I retrieve and save the pixel mapping and color palette to text files for debugging.

from PIL import Image
import numpy as np

im = Image.open('CCM8.TGA')
d = list(im.getdata())
p = np.array(im.getpalette(),dtype=np.uint8).reshape((256,3))

with open("data.txt", "w") as file:
  file.write(str(d))

with open("palette.txt", "w") as file:
  file.write(str(p))

Looking at data.txt, I see that pixel (0,0) is indexed to entry 64 of the RGB palette, and pixel (0,8) is indexed to 128. When I look to the corresponding entries in palette.txt, I find:

64:  [123 129   0]
128: [  0 125   0]

But I know that pixel (0,0) is RGB 255,0,0 and pixel (0,8) is 0,255,0 as these are red and green respectively and not a shade of these colors.

I firstly confirmed this using the manual method of opening the image with GIMP and picking the colors. Later, I found out I could use identify from ImageMagick which gave me the values for those indexes:

identify -verbose CCM8.TGA
...
Colormap entries: 256
Colormap:
...
64: (255,0,0) #FF0000 red
...
128: (0,255,0) #00FF00 lime
...

Pixel (0,16) is blue, indexed to 192, and the values I get with PIL and ImageMagick are consistent: 0,0,255. Same as (0,24) which is black, indexed to 0, which is 0,0,0 in both cases. Finally, pixel (0,56) is white and I get 123,255,255 using PIL and 255,255,255 using identify.

There are no more colors on that image.

I have come to the conclusion that there are some other elements that I am missing which determine the 'final' values of the color palette but I really don't know enough about images and my search for an answer has been unfruitful so far.

I would appreciate if someone could point me in the right direction to understand why I am getting different values when I read the palette directly using PIL and what I see using ImageMagick's identify or GIMP.

Thank you.

1 Answers

I'm fairly sure this is a bug in PIL.


Firstly, at line 111 in /src/PIL/TgaImagePlugin.py is this line of code:

start, size, mapdepth = i16(s[3:]), i16(s[5:]), i16(s[7:])

which I think should be:

start, size, mapdepth = i16(s[3:]), i16(s[5:]), i8(s[7:])

because the Color Map Specification is 5 bytes not 6.


Secondly, the "Colormap Entry Size" (at byte offset 7) is 16 in your image, which means there are 2 bytes per colormap entry. When this is the case, the 16 bits must be interpreted as an alpha bit and 5-bits each for R, G, B, i.e. ARGB1555. That means the alpha bit must be masked off and then the RGB bits and each of R, G and B must be shifted left by 3 (multiplied by 8) to make the 5-bit value become 8-bit. That means all the colormap RGB entries must be multiples of 8, and that is clearly not happening because you found the entries:

64:  [123 129   0]
128: [  0 125   0]

I can't see any evidence of masking or shifting in ./src/PIL/ImagePalette.py at all.


Thirdly, if you load the image and display it without any processing, PIL gets it wrong:

from PIL import Image
Image.open('CCM8.TGA').show()

PIL displays:

enter image description here

whereas the image should look like this:

enter image description here


00000000: 1a01 0900 0000 0110 0000 0000 8000 8000  ................
00000010: 0800 5472 7565 7669 7369 6f6e 2852 2920  ..Truevision(R) 
00000020: 5361 6d70 6c65 2049 6d61 6765 0000 2104  Sample Image..!.
00000030: 4208 630c 8410 ffff ffff ffff ffff ffff  B.c.............
00000040: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00000050: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00000060: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00000070: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00000080: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00000090: ffff ffff ffff ffff ffff ffff ffff ffff  ................
000000a0: ffff ffff ffff ffff ffff ffff 007c ffff  .............|..
000000b0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
000000c0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
000000d0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
000000e0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
000000f0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00000100: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00000110: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00000120: ffff ffff ffff ffff ffff ffff e003 ffff  ................
Related