pyplot - python plot multiple figures
Servir dinĂ¡micamente una imagen matplotlib a la web usando python (6)
Esta pregunta se ha formulado de manera similar here pero la respuesta estaba muy por encima de mi cabeza (soy muy nuevo en Python y en el desarrollo web), así que espero que haya una forma más simple o que pueda explicarse de manera diferente.
Estoy intentando generar una imagen utilizando matplotlib y servirla sin escribir primero un archivo en el servidor. Mi código es probablemente un poco tonto, pero es así:
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>
"""
Creo que en lugar de hacer pyplot.savefig (''test.png''), se supone que debo crear un objeto cstringIO y luego hacer algo como esto:
mybuffer=cStringIO.StringIO()
pyplot.savefig(mybuffer, format="png")
Pero estoy bastante perdido de allí. Todos los ejemplos que he visto (por ejemplo, http://lost-theory.org/python/dynamicimg.html ) implican hacer algo como
print "Content-type: image/png/n"
y no entiendo cómo integrar eso con el HTML que ya estoy publicando.
A menos que entienda mal su pregunta, todo lo que debe hacer es guardar la ubicación de la imagen y ejecutar: python -m SimpleHTTPServer 8000 &
Luego abra su navegador y escriba http://localhost:8000/
en la barra de URL.
Debieras
- Primero escribe en un objeto cStringIO
- luego escribe el encabezado HTTP
- luego escribe el contenido del cStringIO a la salida estándar
Por lo tanto, si ocurriera un error en savefig
, aún podría devolver algo más, incluso otro encabezado. Algunos errores no se reconocerán antes, por ejemplo, algunos problemas con textos, dimensiones de imagen demasiado grandes, etc.
Necesitas decirle a savefig
dónde escribir la salida. Tu puedes hacer:
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())
Si quieres incrustar la imagen en 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()
Las respuestas anteriores están un poco desactualizadas: esto es lo que me funciona en Python3 + para obtener los bytes en bruto de los datos de la figura.
import matplotlib.pyplot as plt
from io import BytesIO
fig = plt.figure()
plt.plot(range(10))
figdata = BytesIO()
fig.savefig(figdata, format=''png'')
Como se mencionó en otras respuestas, ahora necesita configurar un encabezado de ''Tipo de contenido'' en ''imagen / png'' y escribir los bytes.
Dependiendo de lo que esté utilizando como su servidor web, el código puede variar. Uso Tornado como mi servidor web y el código para hacerlo es:
self.set_header(''Content-Type'', ''image/png'')
self.write(figdata.getvalue())
Lo que me funciona con python3 es:
buf = io.BytesIO()
plt.savefig(buf, format=''png'')
image_base64 = base64.b64encode(buf.getvalue()).decode(''utf-8'').replace(''/n'', '''')
buf.close()
Mi primera pregunta es: ¿Cambia la imagen a menudo? ¿Quieres quedarte con los mayores? Si es una cosa en tiempo real, entonces su búsqueda de optimización está justificada. De lo contrario, los beneficios de generar la imagen sobre la marcha no son tan importantes.
El código actual requiere 2 solicitudes:
- para obtener la fuente html que ya tiene y
- para obtener la imagen real
Probablemente la forma más sencilla (mantener las solicitudes web al mínimo) es el comentario de @Alex L, que le permitiría hacerlo en una sola solicitud, mediante la creación de un HTML con la imagen incrustada.
Tu código sería algo como:
# Build your matplotlib image in a iostring here
# ......
#
# Initialise the base64 string
#
imgStr = "data:image/png;base64,"
imgStr += base64.b64encode(mybuffer)
print "Content-type: text/html/n"
print """<html><body>
# ...a bunch of text and html here...
<img src="%s"></img>
#...more text and html...
</body></html>
""" % imgStr
Este código probablemente no funcionará de forma inmediata, pero muestra la idea.
Tenga en cuenta que esta es una mala idea en general si su imagen realmente no cambia con demasiada frecuencia o la generación demora mucho tiempo, porque se generará siempre.
Otra forma sería generar el html original. Al cargarlo se activará una solicitud para el "test.png". Puede hacerlo por separado, ya sea a través de la solución de transmisión de búfer que ya mencionó, o desde un archivo estático.
Personalmente, me quedaría con una solución desacoplada: generar la imagen mediante otro proceso (asegurándose de que siempre haya una imagen disponible) y usar algo muy liviano para generar y servir el HTML.
HTH,
Sé que llego un poco tarde a la fiesta aquí, pero tuve el mismo problema y terminé con el pequeño script a continuación.
Este código de Python 3.6+:
- Inicia un servidor web y te dice dónde verlo.
- Escanea por sí misma los métodos de clase que comienzan con ''plot_'' y le proporciona al navegador una lista de gráficos
- Para un gráfico en el que se hace clic, se solicitan los parámetros requeridos (si los hay), incluido un período de actualización automática (en segundos)
- Ejecuta la trama y actualiza.
Como puede ver en el código, es deliberadamente mínimo para diagnósticos temporales y monitoreo (del progreso del aprendizaje automático en mi caso).
Puede que necesite instalar cualquier dependencia (plac + cualquier otra biblioteca necesaria para el trazado, por ejemplo, yo uso pandas, matplotlib)
Puede ejecutar el archivo haciendo doble clic (sin parámetros) o línea de comandos (con / sin parámetros)
Código:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import io
from http.server import HTTPServer,BaseHTTPRequestHandler
import urllib
import inspect
class PlotRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
args = urllib.parse.parse_qs(self.path[2:])
args = {i:args[i][0] for i in args}
html = ''''
if ''mode'' not in args:
plots = ''''
for member in dir(self):
if member[:5] == ''plot_'':
plots += f''<a href="http://{self.server.server_name}:{self.server.server_port}/?mode=paramcheck&graph={member}">{member[5:].replace("_"," ").title()}</a><br/>/n''
html = f''''''<html><body><h1>Available Plots</h1>{plots}</body></html>''''''
elif args[''mode''] == ''paramcheck'':
plotargs = inspect.getargspec(getattr(self,args[''graph''])).args
if len(plotargs) == 1 and plotargs[0].lower()==''self'':
args[''mode''] = ''plotpage''
else:
for arg in plotargs:
if arg.lower() != ''self'':
html += f"<input name=''{arg}'' placeholder=''{arg}'' value='''' /><br />/n"
html = f"<html><body><h1>Parameters:</h1><form method=''GET''>{html}<input name=''refresh_every'' value=''60'' />(Refresh in sec)<br /><input type=''hidden'' name=''mode'' value=''plotpage''/><input type=''hidden'' name=''graph'' value=''{args[''graph'']}''/><input type=''submit'' value=''Plot!''/></form></body></html>"
if ''mode'' in args and args[''mode''] == ''plotpage'':
html = f''''''<html><head><meta http-equiv="refresh" content="{args[''refresh_every'']};URL=/'http://{self.server.server_name}:{self.server.server_port}{self.path}/'" /></head>
<body><img src="http://{self.server.server_name}:{self.server.server_port}{self.path.replace(''plotpage'',''plot'')}" /></body>''''''
elif ''mode'' in args and args[''mode''] == ''plot'':
try:
plt = getattr(self,args[''graph''])(*tuple((args[arg] for arg in inspect.getargspec(getattr(self,args[''graph''])).args if arg in args)))
self.send_response(200)
self.send_header(''Content-type'', ''image/png'')
self.end_headers()
plt.savefig(self.wfile, format=''png'')
except Exception as e:
html = f"<html><body><h1>Error:</h1>{e}</body></html>"
if html != '''':
self.send_response(200)
self.send_header(''Content-type'', ''text/html'')
self.end_headers()
self.wfile.write(bytes(html,''utf-8''))
def plot_convergence(self, file_path, sheet_name=None):
if sheet_name == None:
data = pd.read_csv(file_path)
else:
data = pd.read_excel(file_path, sheet_name)
fig, ax1 = plt.subplots()
ax1.set_xlabel(''Iteration'')
ax1.set_ylabel(''LOSS'', color=''tab:red'')
ax1.set_ylim([0,1000])
ax1.plot(data.iteration, data.loss, color=''tab:red'')
ax2 = ax1.twinx()
ax2.set_ylabel(''Precision, Recall, f Score'')
ax2.set_ylim([0,1])
ax2.plot(data.iteration, data.precision, color=''tab:blue'')
ax2.plot(data.iteration, data.recall, color=''tab:green'')
ax2.plot(data.iteration, data[''f-score''], color=''tab:orange'')
fig.tight_layout()
plt.legend(loc=6)
return plt
def main(server_port:"Port to serve on."=9999,server_address:"Local server name."=''''):
httpd = HTTPServer((server_address, server_port), PlotRequestHandler)
print(f''Serving on http://{httpd.server_name}:{httpd.server_port} ...'')
httpd.serve_forever()
if __name__ == ''__main__'':
import plac; plac.call(main)