Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically serving a matplotlib image to the web using python

This question has been asked in a similar way here but the answer was way over my head (I'm super new to python and web development) so I'm hoping there's a simpler way or it could be explained differently.

I'm trying to generate an image using matplotlib and serve it without first writing a file to the server. My code is probably kind of silly, but it goes like this:

import cgi
import matplotlib.pyplot as pyplot
import cStringIO #I think I will need this but not sure how to use

...a bunch of matplotlib stuff happens....
pyplot.savefig('test.png')

print "Content-type: text/html\n"
print """<html><body>
...a bunch of text and html here...
<img src="test.png"></img>
...more text and html...
</body></html>
"""

I think that instead of doing pyplot.savefig('test.png'), I am supposed to create a cstringIO object and then do something like this:

mybuffer=cStringIO.StringIO()
pyplot.savefig(mybuffer, format="png")

But I am pretty lost from there. All the examples I've seen (e.g. http://lost-theory.org/python/dynamicimg.html) involve doing something like

print "Content-type: image/png\n"

and I don't get how to integrate that with the HTML I'm already outputting.

like image 708
Ben S. Avatar asked Feb 12 '13 02:02

Ben S.


People also ask

Can matplotlib be interactive?

You can make a plot in matplotlib, add interactive functionality with plugins that utilize both Python and JavaScript, and then render it with D3. mpld3 includes built-in plugins for zooming, panning, and adding tooltips (information that appears when you hover over a data point).


2 Answers

You should

  • first write to a cStringIO object
  • then write the HTTP header
  • then write the content of the cStringIO to stdout

Thus, if an error in savefig occured, you could still return something else, even another header. Some errors won't be recognized earlier, e.g., some problems with texts, too large image dimensions etc.

You need to tell savefig where to write the output. You can do:

format = "png"
sio = cStringIO.StringIO()
pyplot.savefig(sio, format=format)
print "Content-Type: image/%s\n" % format
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) # Needed this on windows, IIS
sys.stdout.write(sio.getvalue())

If you want to embed the image into HTML:

print "Content-Type: text/html\n"
print """<html><body>
...a bunch of text and html here...
<img src="data:image/png;base64,%s"/>
...more text and html...
</body></html>""" % sio.getvalue().encode("base64").strip()
like image 96
Thorsten Kranz Avatar answered Oct 23 '22 10:10

Thorsten Kranz


The above answers are a little outdated -- here's what works for me on Python3+ to get the raw bytes of the figure data.

import matplotlib.pyplot as plt
from io import BytesIO
fig = plt.figure()
plt.plot(range(10))
figdata = BytesIO()
fig.savefig(figdata, format='png')

As mentioned in other answers you now need to set a 'Content-Type' header to 'image/png' and write out the bytes.

Depending on what you are using as your webserver the code may vary. I use Tornado as my webserver and the code to do that is:

self.set_header('Content-Type', 'image/png')
self.write(figdata.getvalue())
like image 12
Mike N Avatar answered Oct 23 '22 11:10

Mike N