Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to add text to GIF? #3128

Closed
Diniboy1123 opened this issue May 12, 2018 · 6 comments
Closed

How to add text to GIF? #3128

Diniboy1123 opened this issue May 12, 2018 · 6 comments
Labels

Comments

@Diniboy1123
Copy link

How Can I add a custom text on an animated gif file?

@radarhere radarhere changed the title [Q] How to add text to GIF? How to add text to GIF? May 13, 2018
@radarhere
Copy link
Member

radarhere commented May 13, 2018

I was surprised that I couldn't find a simpler way, but here is what I came up with -

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:])

See the following parts of the docs if you would like more information -

@Diniboy1123
Copy link
Author

Thank you! 😃

Works like a charm. But if I try to save the final result into a BytesIO, I always get an empty file:

my_bytes = BytesIO()
frames[0].save(my_bytes, format='gif', save_all=True, append_images=frames[1:])

And I need to place a white text on the image. If I use the fill=(255,255,255), it gives me an error:

ValueError: unrecognized raw mode

@radarhere
Copy link
Member

radarhere commented May 14, 2018

I would conclude that you need to fill with the appropriate value for the mode. So since the image I used is in P mode, a fill value of 251 will show you a difference.

However, you probably will want to convert the image to RGB - which I've found incidentally also changes the image away from being multiple frames. So the temporary save operation is no longer required.

Here is code for that, as well as saving the final result into BytesIO. Let me know if it does not work for you.

from PIL import Image, ImageDraw, ImageSequence
import io

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

frames = []
for frame in ImageSequence.Iterator(im):
	frame = frame.convert('RGB')
	
	d = ImageDraw.Draw(frame)
	d.text((10,100), "Hello World", fill=(255,255,255))
	del d
	
	frames.append(frame)
my_bytes = io.BytesIO()
frames[0].save(my_bytes, format="GIF", save_all=True, append_images=frames[1:])
print(my_bytes.getvalue())

@Diniboy1123
Copy link
Author

Diniboy1123 commented May 14, 2018

This one gave me an error unfortunately:

  File "C:\Users\Steve\AppData\Local\Programs\Python\Python36-32\lib\site-packages\PIL\Image.py", line 1935, in save
    save_handler(self, fp, filename)
  File "C:\Users\Steve\AppData\Local\Programs\Python\Python36-32\lib\site-packages\PIL\GifImagePlugin.py", line 456, in _save_all
    _save(im, fp, filename, save_all=True)
  File "C:\Users\Steve\AppData\Local\Programs\Python\Python36-32\lib\site-packages\PIL\GifImagePlugin.py", line 468, in _save
    if not save_all or not _write_multiple_frames(im, fp, palette):
  File "C:\Users\Steve\AppData\Local\Programs\Python\Python36-32\lib\site-packages\PIL\GifImagePlugin.py", line 451, in _write_multiple_frames
    _write_frame_data(fp, im_frame, offset, frame_data['encoderinfo'])
  File "C:\Users\Steve\AppData\Local\Programs\Python\Python36-32\lib\site-packages\PIL\GifImagePlugin.py", line 728, in _write_frame_data
    _write_local_header(fp, im_frame, offset, 0)
  File "C:\Users\Steve\AppData\Local\Programs\Python\Python36-32\lib\site-packages\PIL\GifImagePlugin.py", line 494, in _write_local_header
    transparency = int(transparency)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'tuple'

@Diniboy1123
Copy link
Author

Actually changing the mode to RGBA solved my problem. 😃 Works like a charm now. Thanks for all your amazing help!

And for the BytesIO thing, it was a noob thing,,, I forgot to do a seek(0)...

@radarhere
Copy link
Member

For the record, I've since realised a simpler way of converting a multiframe image into a single frame image - im = im.copy().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants