Search code examples
transparencygifpython-imaging-librarypalette

Maintaining transparency with palette mode GIFs in Pillow


I am trying to take a GIF with a palette containing one transparency index and use Pillow to create cropped sub-images. However, when using the crop() method the results no longer are transparent.

original = Image.open("filename.gif")
print(original.mode) # prints "P", as it should
transparent = original.info["transparency"]
print(transparent) # prints the correct index of the transparent color
cropped = original.crop((0, 0, 10, 10))
print(cropped.info) # transparency no longer present
cropped.info["transparency"] = 255
print(cropped.info) # key is entered, but not transparent in a drawn image

How can I maintain or restore the transparent index through manipulations in Pillow? As shown above, even if I "brute-force" add the transparency index back into the "info" dictionary, that is obviously not where Python is looking for the index to be specified. The documentation also mentions that certain methods like crop() are lazy and do not transfer all image information, so is there a way to re-add such information to an Image object? The documentation suggests I can do so by saving a new GIF file, but I won't need the sub-images after the program is finished running and displaying them.

edited to add the below additional information:

the original image The original image, made in GIMP (circle is red, marked as the transparent color using IrfanView)

code output The output of my code, with the circle reverting to a visible red

My entire program is here:

from tkinter import *
from tkinter import ttk
from PIL import Image
from PIL import ImageTk

class Main:
    def __init__(self):
        self.root = Tk()
        self.background = Canvas(self.root)
        self.background.grid(column=0,row=0)
        self.Draw()

    def Draw(self):
        original = Image.open("Transparency_test.gif")
        print(original.mode) # prints "P", as it should
        transparent = original.info["transparency"]
        print(transparent) # prints the correct index of the transparent color
        cropped = original.crop((0, 0, 50, 50))
        print(cropped.info) # transparency no longer present

        test_uncropped = ImageTk.PhotoImage(image=original)
        test_cropped = ImageTk.PhotoImage(image=cropped)

        self.background.create_image((0,0), image=test_uncropped, anchor=NW)
        self.background.create_image((100,0), image=test_cropped, anchor=NW)

        self.root.mainloop()

instance = Main()

Solution

  • I'm not sure this is the most efficient solution, but I got it working by making a resized copy of the image, and then pasting pixels from the original image over it. I think the result is what you expect.

        cropped = original.crop((0, 0, 50, 50))
        cropped.load()
        print(cropped.info) # transparency no longer present
    
        copied = original.resize((50,50))
        copied.paste(original, (0, 0))
        print(copied.info) # transparency present
    
        test_uncropped = ImageTk.PhotoImage(image=original)
        test_cropped = ImageTk.PhotoImage(image=cropped)
        test_copied = ImageTk.PhotoImage(image=copied)
    
        self.background.create_image((0,0), image=test_uncropped, anchor=NW)
        self.background.create_image((100,0), image=test_cropped, anchor=NW)
        self.background.create_image((200,0), image=test_copied, anchor=NW)