2

I'm trying to take a .GIF file, open it with PIL, and write a text on it frame by frame. However, the code only saves an image (1 frame;it doesn't move like a .GIF file.

The code:

import giphypop
from urllib import request
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw 

g = giphypop.Giphy()

img = g.translate("dog")
request.urlretrieve(img.media_url, "test.gif") 

opened_gif = Image.open("test.gif")
opened_gif.load()
opened_gif.seek(1)

try:
    while 1:
      slide = opened_gif.seek(opened_gif.tell()+1)
      draw = ImageDraw.Draw(slide)
      # font = ImageFont.truetype(<font-file>, <font-size>)
      font = ImageFont.truetype("sans-serif.ttf", 16)
      # draw.text((x, y),"Sample Text",(r,g,b))
      draw.text((0, 0),"Sample Text",(255,255,255),font=font)
except EOFError:
    pass # end of sequence

except AttributeError:
    print("Couldn't use this slide")

opened_gif.save('test_with_caption.gif')
whatwhatwhat
  • 1,991
  • 4
  • 31
  • 50
  • 2
    as I know PIL can't save animated GIF. You will have to write every frame in separated image and use differnt tool to join all frames into one GIF. You can try `ffmpeg` or `ImageMagic` to join images into animated GIF. Or you can check [moviepy](http://zulko.github.io/moviepy/) module to create animation. – furas Jan 04 '17 at 03:06
  • @furas ok got it. But why, then, does it not save 1 frame with the sample text? All it does is save 1 image with **no** text. – whatwhatwhat Jan 04 '17 at 03:08
  • i can't check it - you works on `slice` but you save `opened_gif` – furas Jan 04 '17 at 03:13
  • BTW: don't use `pass` in `except` because you can have some error and you don't know it. – furas Jan 04 '17 at 03:14
  • see how to work with frames: http://stackoverflow.com/a/35615319/1832058 – furas Jan 04 '17 at 03:30
  • @furas what should I use instead of `pass`? – whatwhatwhat Jan 04 '17 at 23:17

1 Answers1

4

Code from https://github.com/python-pillow/Pillow/issues/3128 that, nicely enough, solves this exact problem.

from PIL import Image, ImageDraw, ImageSequence
import io

im = Image.open('Tests/images/iss634.gif')

# A list of the frames to be outputted
frames = []
# Loop over each frame in the animated image
for frame in ImageSequence.Iterator(im):
    # Draw the text on the frame
    d = ImageDraw.Draw(frame)
    d.text((10,100), "Hello World")
    del d

    # However, 'frame' is still the animated image with many frames
    # It has simply been seeked to a later frame
    # For our list of frames, we only want the current frame

    # Saving the image without 'save_all' will turn it into a single frame image, and we can then re-open it
    # To be efficient, we will save it to a stream, rather than to file
    b = io.BytesIO()
    frame.save(b, format="GIF")
    frame = Image.open(b)

    # Then append the single frame image to a list of frames
    frames.append(frame)
# Save the frames as a new image
frames[0].save('out.gif', save_all=True, append_images=frames[1:])
radarhere
  • 929
  • 8
  • 23