I'm trying to convert some transparent PNGs to one animated GIF, but there is a trimming issue. Except for the first image, all other images' outermost space with only black colour are cropped and become transparent.
e.g. the red part of the PNG will be cut away in the generated GIF: example pic for trimming issue of animated GIF
Below is my code. Sorry if it is a bit messy cause I am still learning Python.
from PIL import Image
import glob
# https://stackoverflow.com/questions/46850318/transparent-background-in-gif-using-python-imageio
def gen_frame(path):
im = Image.open(path)
alpha = im.getchannel('A')
# Convert the image into P mode but only use 255 colors in the palette out of 256
im = im.convert('RGB').convert('P', palette=Image.Palette.ADAPTIVE, colors=255)
# Set all pixel values below 128 to 255 , and the rest to 0
mask = Image.eval(alpha, lambda a: 255 if a <=0 else 0)
# Paste the color of index 255 and use alpha as a mask
im.paste(255, mask)
# The transparency index is 255
im.info['transparency'] = 255
return im
def resize4Twitter(img):
TWITTER_MAX_WIDTH, TWITTER_MAX_HEIGHT = 1280, 1080
if img.width < TWITTER_MAX_WIDTH and img.height < TWITTER_MAX_HEIGHT:
return img
elif img.width/img.height > TWITTER_MAX_WIDTH/TWITTER_MAX_HEIGHT:
x, y = TWITTER_MAX_WIDTH, (img.height / img.width * TWITTER_MAX_WIDTH)
else:
x, y = (img.width / img.height * TWITTER_MAX_HEIGHT), TWITTER_MAX_HEIGHT
return img.resize((int(x),int(y)))
### User Input
imagePath, gifName, fpsStr, forTwitter = '', '', '', ''
fps = 0
imagePath = input("Enter PNG path:")
gifName = input("Enter GIF name:")
while fps == 0:
fpsStr = input("Enter FPS [1-50]:")
if fpsStr.isdigit():
if int(fpsStr) >= 1 and int(fpsStr) <= 50:
fps = int(fpsStr)
else:
print("Invalid. Please enter an integer from 1 to 50.")
else:
print("Invalid. Please enter an integer from 1 to 50.")
while forTwitter!= "Y" and forTwitter != "N":
forTwitter = input("Resize for Twitter? [Y/N]: ")
### filepaths
fp_in = imagePath + "\\" + gifName + "_*.png"
details = "_fps" + str(fps)
if forTwitter == "Y":
details = details + "_twitterSize"
fp_out = imagePath + "\\" + gifName + details + ".gif"
### Process Images
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#gif
# https://legacy.imagemagick.org/script/command-line-options.php?#dispose
imgpaths = sorted(glob.glob(fp_in))
imgs = []
for imgpath in imgpaths:
img = gen_frame(imgpath)
if forTwitter == "Y":
img = resize4Twitter(img)
imgs.append(img)
print("Image loaded:\t" + imgpath)
imgs = iter(imgs) # I tried .show() here, the PNGs are still normal
dur = 1000/fps
img = next(imgs) # extract first image from iterator
img.save(fp_out, save_all=True, append_images=imgs,
optimize=False, duration=dur, loop=0, disposal=2) # use diposal to clear prev. frame
print("Animated GIF produced at: " + fp_out)