Converting Transparent GIF to PNG by using pyPIL
January 21, 2009 7:45 pm PythonTo modify GIF89a files in PIL (Python Imaging Library) is a bit tricky. Unlike GIF87a, GIF89a supported animation as well as transparent background, however, PIL only support “read” mode of GIF89a. Thus, when there is a need to modify GIF89a file, all the information (e.g. alpha channel) will not be maintained as PIL will save the file to GIF97a format.
When a Mathtype object is created in OpenOffice, the replacement object (in GIF89a format) will be created so user who does not have Mathtype installed still able to view the formulas. On top of this, this replacemenet object also can be used as picture for web.
As I need to use this replacement object for web, I still need to modify the size of this replacement GIF file so it can be placed nicely on the browser. However, the only good library can be used in python is PIL. From http://nadiana.com/pil-tips-converting-png-gif, I managed to get few tips to maintain and the transparency for the GIF object.
Original Image with pink background:
![]() |
To resize the image:
import Image
img = Image.open('1.gif')
transparency = img.info['transparency']
img.resize ((127,47))
img.save('2.gif', transparency=transparency)
However, the quality of the image produced by the above code is really bad:
I tried to find a way to play around with others available modes found in PIL to enhance the quality of the images. I realise that in PIL, the standard mode for GIF image is “P” (Palette mode) and when the image is previewed through PIL, the transparent background will be changed to pink. To maintain the quality of the image when it’s resized, I need to convert the image to grey scale by using “L” mode (liminance). As all my formula images are in black and white, this convertion will be fine. I managed to get the good quality of the resized image, however, the pink background is also converted to grey when using “L” mode. So before I converted to “L” mode, I need to set the original image background to “white”. This is done by hacking the pallete of the image.
And since my formula only uses black and white color, resize will break the black pixel. So to work around this, I need to convert the image to “L” mode (liminance) so the black color will be converted to grey scale. However when a transparent image being converted to “L” mode, the background will turn to black. Thus, before we convert to “L”, we need to change the transparency of the original image to white. The information about the transparency of the image can be gathered from
transparency = img.info['transparency']
. The transparency information is 2 which means the third RGB tuple in the palette. Changing the transparency to white:
def transparent(im):
transparency = img.info['transparency']
x = transparency*3
p = im.getpalette() #NOTE: the original image is GIF with "Palette mode", with this we can hack the palette of the image
for x in range(x, x+3):
p[x] = 255
im.putpalette(p)
return im
Then we convert the image to “L” mode and resize the image:
im = im.convert('L')
im = im.resize((127,47), Image.ANTIALIAS)
Now, there are two ways to replace the transparency back to the image after it’s resized. I prefer the second option as it’s faster.
First way is to create an invert function to invert the value of each pixel of the image. Then use eval to apply the function to each pixel of the image.
Next, create a new “L” mode image with the same size and black in color for each pixel. Afterwards, create a multi-band image from multiple single-band images. This new multi-band image is RGBA with the values of R, G, B are the new black black image and Alpha color is the converted image.
def convert1(im):
im = transparent(im)
im = im.convert('L')
im = im.resize((127,47), Image.ANTIALIAS)
def invert(p):
return 255^p
im = Image.eval(im, invert)
new = Image.new('L', im.size, 0) #0 is black color
n = Image.merge('RGBA', (new, new, new, im))
return n
The second option is reverse the paletted directly. After the image being resized, convert back the image to “P” mode and reverse the palette value. This reverse function is built in palette function which perform faster than the above inverse function. At this stage, after the pallete being reversed, we need to convert the image back to the “L” mode again to get the gray scale image. The next process is the same as the above which involve creating a blank black image and merge the blank image and the converted image together in new multi-band image.
def convert2(im):
im = transparent(im)
im = im.convert('L')
im = im.resize((127,47), Image.ANTIALIAS)
im = im.convert('P')
p = im.getpalette()
p.reverse()
im.putpalette(p)
im = im.convert('L')
new = Image.new('L', im.size, 0)
n = Image.merge('RGBA', (new, new, new, im))
return n
The png image produced from both of the convert functions is:
The complete code together with the performance test is:
import Image, time
def transparent(im):
p = im.getpalette()
p[6] = 255
p[7] = 255
p[8] = 255
im.putpalette(p)
return im
def convert1(im):
im = transparent(im)
im = im.convert('L')
im = im.resize((127,47), Image.ANTIALIAS)
def invert(p):
return 255^p
new = Image.new('L', im.size, 0)
im = Image.eval(im, invert)
n = Image.merge('RGBA', (new, new, new, im))
return n
def convert2(im):
im = transparent(im)
im = im.convert('L')
im = im.resize((127,47), Image.ANTIALIAS)
im = im.convert('P')
p = im.getpalette()
p.reverse()
im.putpalette(p)
im = im.convert('L')
new = Image.new('L', im.size, 0)
n = Image.merge('RGBA', (new, new, new, im))
return n
im = Image.open('1.gif')
starttime = time.time()
im1 = convert1(im)
im1.save('convert1.png')
endtime = time.time()
print endtime-starttime
starttime = time.time()
im2 = convert2(im)
im2.save('convert2.png') #this is faster
endtime = time.time()
print endtime-starttime
Download link for the above code include the Original GIF Image is: imaging.zip
Need to take note that IE6 do not support transparency, IE6 will turn the transparent background to light grey color. I found javascript code to handle the transparent PNG in IE6. The code can be downloaded from: Transparent PNG problen in Window IE 6
Useful links:


Zashkaser :
Date: August 5, 2009 @ 12:24 pm
Amazing news, thank you!
Defiterka :
Date: August 18, 2009 @ 4:18 am
Отличная статья Спасибо солидное