84

The following is my first shot which never works:

import cStringIO
import pylab
from PIL import Image
pylab.figure()
pylab.plot([1,2])
pylab.title("test")
buffer = cStringIO.StringIO()
pylab.savefig(buffer, format='png')
im = Image.open(buffer.read())
buffer.close()

the error says,

Traceback (most recent call last):
  File "try.py", line 10, in <module>
    im = Image.open(buffer.read())
  File "/awesomepath/python2.7/site-packages/PIL/Image.py", line 1952, in open
    fp = __builtin__.open(fp, "rb")

any ideas? I don't want the solution to involve extra packages.

desertnaut
  • 57,590
  • 26
  • 140
  • 166
nye17
  • 12,857
  • 11
  • 58
  • 68

2 Answers2

142

Remember to call buf.seek(0) so Image.open(buf) starts reading from the beginning of the buf:

import io
from PIL import Image
import matplotlib.pyplot as plt

plt.figure()
plt.plot([1, 2])
plt.title("test")
buf = io.BytesIO()
plt.savefig(buf, format='png')
buf.seek(0)
im = Image.open(buf)
im.show()
buf.close()
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • Awesome! it works like a charm! even when I substitute `io.BytesIO` with my original `StringIO`. Can you remind you what the why you choose to use the former here? Thanks! – nye17 Dec 22 '11 at 03:24
  • 1
    For Python2.6 or better, use `io.BytesIO` instead of `cStringIO.StringIO` for forward-compatibility. In Python3, the `cStringIO`, `StringIO` modules are gone. Their functionality is all in the `io` module. – unutbu Dec 22 '11 at 10:21
  • How do you set a sensible file name for your .png? It seems to just call mine `fig.png` - thanks. – Little Bobby Tables Oct 02 '18 at 14:38
  • 1
    @josh: `plt.savefig(buf, format='png')` writes to an in-memory `BytesIO` object. No file is created. The more usual way to use `savefig` is to call `plt.savefig('/path/to/anyfilenameyouwant.png')`, which allows you to specify the filename. – unutbu Oct 02 '18 at 15:13
  • I am sending this in a response from a Falsk app so the file gets downloaded by Chrome, etc. Maybe there is a better way to do this, where I get to choose my file name (of the downloaded .png). Thanks though. – Little Bobby Tables Oct 02 '18 at 15:37
  • 1
    If you want the image as an array, you can get by without `PIL`/`pillow` and use `im = plt.imread(buf)`. You can also save the `im` array using `plt.imsave('/path/to/saved/file.png', im)`. – jmd_dk Oct 24 '18 at 12:28
8

I like having it encapsulated in a function:

def fig2img(fig):
    """Convert a Matplotlib figure to a PIL Image and return it"""
    import io
    buf = io.BytesIO()
    fig.savefig(buf)
    buf.seek(0)
    img = Image.open(buf)
    return img

Then I can call it easily this way:

import numpy as np
import matplotlib.pyplot as plt
from PIL import Image

x = np.arange(-3,3)
plt.plot(x)
fig = plt.gcf()

img = fig2img(fig)
img.show()
desertnaut
  • 57,590
  • 26
  • 140
  • 166
kotchwane
  • 2,082
  • 1
  • 19
  • 24