[Image-SIG] PIL 1.0 palette

Denis Benoit benoitde@bigfoot.com
Thu, 30 Dec 1999 21:28:22 -0500 (EST)


On Thu, 30 Dec 1999, Jeff Blaine wrote:

> Hi folks,
> 
> I've gotten up to speed so far to do everything I want (make pie charts)
> with PIL.  Yay!
> 
> However, my charts are very drab from using the default palette for mode
> "P" (I am writing GIFs).
> 
> I have read through the PIL 1.0 handbook and cannot figure out how to
> change my palette properly.
> 
> If I do:
> 
>   im = Image.new("P", (ImageWidth, ImageHeight))
>   im.palette = ImagePalette.ImagePalette("RGB")
> 
> I get:
> 
>     File "/jblaine/mylocal/lib/python15/Image.py", line 375, in load
>       if self.im and self.palette and self.palette.rawmode:
>   AttributeError: rawmode
> 
> and aside from that, I can't figure out how to just get a "simple"
> "rainbow" palette (wow, I'm pretty imaging-lingo savvy, eh?).
> 
> Maybe I can kill 2 birds with one stone here and also mention that I
> want "Black" to be transparent in my output images."
> 
> Anyone have any pointers or code I can look at?  PIL comes with no
> demos that perform ImageDraw.* things that I can find.
> 
> Thanks for your time
> 
> 
> _______________________________________________
> Image-SIG maillist  -  Image-SIG@python.org
> http://www.python.org/mailman/listinfo/image-sig

Hi,

I recently faced the same problem, generating pie charts in "GIF" format, on
a "transparent" backround.  Here is my findings, I must say I'm NOT a python
expert or a PIL expert, I've been using python for less than a week and PIL
for about the same time!

First on the "palette" problem:

Somewhere in the docs, or is it on the site, it says that the proper way to
change palette, is to use the "putpalette" method of the Image object.  So
your code should read something like that:

	im = Image.new("P", (ImageWidth, ImageHeight))
	palette = ImagePalette.ImagePalette("RGB")
	im.putpalette(palette.palette)

But you'll get the same "drab" palette you're reporting.  It seems the palette
is just a list of integers.  I have found that each group of three integers in
the palette will generate a "colour".  For example, the colour of index 0 is
coded by the elements 0, 1 and 2 of the palette.  If you put the following:

	palette.palette[0] = 0
	palette.palette[1] = 0
	palette.palette[2] = 0

Then the colour #0 of your palette will be black (Red = 0, Green = 0, Blue =
0).  Now if you follow with:

	palette.palette[3] = 255
	palette.palette[4] = 255
	palette.palette[5] = 255

	palette.palette[6] = 255
	palette.palette[7] = 0
	palette.palette[8] = 0

The colour #1 of your palette would be white (Red = 255, Green = 255, Blue =
255), and the colour #2 of your palette would be red (Red = 255, Green = 0,
Blue = 0).

Second the "transparency" problem:

This one is a little tougher.  The "GIF" plugin does recognize the
"transparency" feature on a "GIF" but it does not save it on output.  I'm no
expert on "GIF", I simply reversed engineered what it does on input to put
"transparency" support on output.

If you do modify the GifImagePlugin.py, save a copy first so you can restore
it if you mess things up.

I haven't figured out how to add transparency support on the "save()" method
call of the Image object, so I only add a "transparency" entry into the
"info" dictionary of the image, like this:

	im.info["transparency"] = 125

So the colour #125 in my palette is used for the transparency colour.

I then modified the method of GifImagePlugin.py:

def _save(im, fp, filename):

	<...>
    try:
        interlace = im.encoderinfo["interlace"]
    except:
        # default is on (since we're writing uncompressed images)
        interlace = 1
        flags = flags | 64


    ########################################################################
    # 1999.12.26 Denis Benoit:
    #
    # Write transparency:
    # "! <249>" Gif Extension intro
    # "<4>" extension length
    # "<1>" Flags: transparency palette info present
    # "<0> <0>" duration of the image (multi-image)
    # "chr(<transparency>)" Transparency index in the palette
    # "<0>" End of additional headers
    ########################################################################
    try:
        transparency = im.info["transparency"]
        fp.write("!" + chr(249) + chr(4) + chr(1) +
                 chr(0) + chr(0) + chr(transparency) + chr(0))
    except:
        # No transparency info
        pass

    # local image header
    fp.write("," +
             o16(0) + o16(0) +          # bounding box
             o16(im.size[0]) +          # size
             o16(im.size[1]) +
             chr(flags) +               # flags
             chr(8))                    # bits

The "new" code is just between the "interlace" support and the "header" code.
It should be around line 248 of the file.  It worked for me!

Hope this info helps you.

Denis Benoit