PIL Png With Gif Background

Viewed 650

I'm trying to make a fortnite image card generator. I want it to make the skin on the top layer and the bg aka the gif on the bottom. Here is the code so far.


import os
from PIL import Image

filename = 'CID_613_Athena_Commando_M_Columbus_7Y4QE.png'
skin = Image.open(filename, 'r')
filename1 = 'bg.gif'
bg = Image.open(filename1, 'r')
text_img = Image.new('RGBA', (512,512), (0, 0, 0, 0))
text_img.paste(bg, (0,0))
text_img.paste(skin, (0,0), mask=skin)
text_img.save("out.gif", format="gif")

The CID_613_Athena_Commando_M_Columbus_7Y4QE.png is: https://imgur.com/Seu1Tx6.png

Background: https://i.imgur.com/fCewt2H.gif

And here is the output: https://i.imgur.com/4yJMp3M.png

I want it to be like animated background. Unlike what it is right now

2 Answers

The simplest way to do that is without writing any Python and using ImageMagick in the Terminal. So, you would load your background GIF and split it into its constituent 72 frames with -coalesce then draw the spaceman thing on top and save as animated GIF:

magick fCewt2H.gif -coalesce -draw "image SrcOver 0,0 0,0 Seu1Tx6.png" animated.gif

enter image description here

Note that I have decreased the colour quality, number of frames and image size to come in under StackOverflow's 2MB limit for posts. You can get the full quality with the above command.


If you do not want to use ImageMagick for some reason, you can do it with Python, but you have to appreciate that an animated GIF is one file but that contains lots of frames. So you will need to:

  • load the foreground frame once (as you do already)
  • load the GIF background
  • iterate over the frames of the background pasting the foreground (you have this code) onto each of the 72 frames
  • save the resulting 72 frames as a GIF

You need to iterate all frames in the gif and paste the foreground image in all of them.

If the image is animated (bg.is_animated), you can get the number of frames in bg.n_frames.

For animated images (not just gifs) there Pillow includes a seek(frame_number) method that will forward the animation to frame you want to use.

The code will be similar to this:

if bg.is_animated:

  frames = []

  for num in range(bg.n_frames):
    bg.seek(num)
    text_img = Image.new('RGBA', (512,512), (0, 0, 0, 0))
    text_img.paste(bg, (0,0))
    text_img.paste(skin, (0,0), mask=skin)
    frames.append(text_img)

  frames[0].save('out.gif',
                 save_all=True,
                 append_images=frames[1:],
                 duration=100,
                 loop=0)  

Warning! Pillow does not optimize the gif output. Maybe you need to compress them further with another tool.

If you just need an animation, consider saving to WEBP file format just chenge the extension from .gif to .webp in the save() method. You will get a smaller file, it is supported by all browsers, and the image quality is better with gradient backgrounds (there will be no no banding effects due to GIF 8-bit color palette).

Related