123

I have generated an image using PIL. How can I save it to a string in memory? The Image.save() method requires a file.

I'd like to have several such images stored in dictionary.

martineau
  • 119,623
  • 25
  • 170
  • 301
maxp
  • 5,454
  • 7
  • 28
  • 30

6 Answers6

233

You can use the BytesIO class to get a wrapper around strings that behaves like a file. The BytesIO object provides the same interface as a file, but saves the contents just in memory:

import io

with io.BytesIO() as output:
    image.save(output, format="GIF")
    contents = output.getvalue()

You have to explicitly specify the output format with the format parameter, otherwise PIL will raise an error when trying to automatically detect it.

If you loaded the image from a file it has a format property that contains the original file format, so in this case you can use format=image.format.

In old Python 2 versions before introduction of the io module you would have used the StringIO module instead.

sth
  • 222,467
  • 53
  • 283
  • 367
  • 1
    Yes! This is exactly what I was looking for. I googled every combination of python, string, reader, writer, buffer and didn't come up with anything. Thanks! – rik.the.vik Dec 07 '09 at 06:33
  • Thanks - saved me much head scratching this morning. – Colonel Sponsz Jan 15 '10 at 11:44
  • 2
    Doesn't work for me :( I got this KeyError in Imaging/PIL/Image.pyc line 1423 -> raise KeyError(ext) # unknown extension – tabdulradi Mar 31 '11 at 17:21
  • @Radian, I actually have the same problem, any ideas? – Ricardo Villamil Mar 07 '12 at 19:09
  • 6
    @Radian, The PIL documentation on the save() method says: `You can use a file object instead of a filename. In this case, you must always specify the format.` So if the first argument is a file object, you have to pass in the second argument, which is the format (e.g. `'PNG'`). – Su Zhang Dec 14 '12 at 02:16
  • See [this answer](http://stackoverflow.com/a/28102870/307084) for additional information. To ```Image.open(theStringIObuffer)``` with ```PIL```, you may need to first do ```theStringIObuffer.seek(0)```. – coreyt Jul 30 '15 at 02:51
  • 1
    In the current Python version, StringIO is in io package, you must write : `from io import StringIO` – Spoutnik16 Nov 09 '15 at 18:14
  • 4
    you can better use `image.save(output, format=image.format)` – reetesh11 Jan 07 '17 at 21:11
  • Shouldn't it be `BytesIO` in Python 3? Try doing `from io import BytesIO as StringIO` instead. – wallabra Jul 28 '18 at 22:18
  • @Gustavo6046: Since the io module is also available in Python2 I edited the answer to just use `io.BytesIO` for all versions. – sth Jul 30 '18 at 14:52
31

For Python3 it is required to use BytesIO:

from io import BytesIO
from PIL import Image, ImageDraw

image = Image.new("RGB", (300, 50))
draw = ImageDraw.Draw(image)
draw.text((0, 0), "This text is drawn on image")

byte_io = BytesIO()

image.save(byte_io, 'PNG')

Read more: http://fadeit.dk/blog/post/python3-flask-pil-in-memory-image

ozooner
  • 552
  • 6
  • 11
26

sth's solution didn't work for me
because in ...

Imaging/PIL/Image.pyc line 1423 -> raise KeyError(ext) # unknown extension

It was trying to detect the format from the extension in the filename , which doesn't exist in StringIO case

You can bypass the format detection by setting the format yourself in a parameter

import StringIO
output = StringIO.StringIO()
format = 'PNG' # or 'JPEG' or whatever you want
image.save(output, format)
contents = output.getvalue()
output.close()
tabdulradi
  • 7,456
  • 1
  • 22
  • 32
16

save() can take a file-like object as well as a path, so you can use an in-memory buffer like a StringIO:

buf = StringIO.StringIO()
im.save(buf, format='JPEG')
jpeg = buf.getvalue()
Jieter
  • 4,101
  • 1
  • 19
  • 31
bobince
  • 528,062
  • 107
  • 651
  • 834
14

With modern (as of mid-2017 Python 3.5 and Pillow 4.0):

StringIO no longer seems to work as it used to. The BytesIO class is the proper way to handle this. Pillow's save function expects a string as the first argument, and surprisingly doesn't see StringIO as such. The following is similar to older StringIO solutions, but with BytesIO in its place.

from io import BytesIO
from PIL import Image

image = Image.open("a_file.png")
faux_file = BytesIO()
image.save(faux_file, 'png')
mightypile
  • 7,589
  • 3
  • 37
  • 42
9

When you say "I'd like to have number of such images stored in dictionary", it's not clear if this is an in-memory structure or not.

You don't need to do any of this to meek an image in memory. Just keep the image object in your dictionary.

If you're going to write your dictionary to a file, you might want to look at im.tostring() method and the Image.fromstring() function

http://effbot.org/imagingbook/image.htm

im.tostring() => string

Returns a string containing pixel data, using the standard "raw" encoder.

Image.fromstring(mode, size, data) => image

Creates an image memory from pixel data in a string, using the standard "raw" decoder.

The "format" (.jpeg, .png, etc.) only matters on disk when you are exchanging the files. If you're not exchanging files, format doesn't matter.

Jonathan Root
  • 535
  • 2
  • 14
  • 31
S.Lott
  • 384,516
  • 81
  • 508
  • 779