Cette question a été posée de la même manière ici mais la réponse était évidente (je ne connais que très peu le développement python et Web). J'espère donc qu'il existe un moyen plus simple ou qu'il pourrait être expliqué différemment.
J'essaie de générer une image à l'aide de matplotlib et de la servir sans écrire au préalable un fichier sur le serveur. Mon code est probablement un peu idiot, mais il va comme ceci:
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>
"""
Je pense qu'au lieu de faire pyplot.savefig ('test.png'), je suis supposé créer un objet cstringIO puis faire quelque chose comme ça:
mybuffer=cStringIO.StringIO()
pyplot.savefig(mybuffer, format="png")
Mais je suis assez perdu à partir de là. Tous les exemples que j'ai vus (par exemple http://lost-theory.org/python/dynamicimg.html ) impliquent de faire quelque chose comme:
print "Content-type: image/png\n"
et je ne comprends pas comment intégrer cela avec le HTML que je produis déjà.
Vous devriez
Ainsi, si une erreur dans savefig
se produit, vous pouvez toujours retourner quelque chose d'autre, même un autre en-tête. Certaines erreurs ne seront pas reconnues plus tôt, par exemple des problèmes de texte, des dimensions d'image trop grandes, etc.
Vous devez indiquer savefig
où écrire le résultat. Tu peux faire:
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 vous souhaitez incorporer l'image au format 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()
Ma première question est la suivante: l'image change-t-elle souvent? Voulez-vous garder les plus âgés? Si c'est une opération en temps réel, votre quête d'optimisation est justifiée. Sinon, les avantages de la génération de l'image à la volée ne sont pas si importants.
Le code tel qu'il existe nécessiterait 2 demandes:
La manière la plus simple (en minimisant les requêtes sur le Web) est probablement le commentaire de @Alex L, ce qui vous permettrait de le faire en une seule requête, en créant un code HTML avec l'image incorporée.
Votre code serait quelque chose comme:
# 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
Ce code ne fonctionnera probablement pas immédiatement, mais montre l'idée.
Notez que ceci est une mauvaise idée en général si votre image ne change pas vraiment trop souvent ou si sa génération prend du temps, car sera sera généré à chaque fois.
Une autre façon serait de générer le code HTML d'origine. Le charger déclenchera une demande pour le "test.png". Vous pouvez le servir séparément, soit via la solution de diffusion en tampon que vous avez déjà mentionnée, soit à partir d'un fichier statique.
Personnellement, je m'en tenais à une solution découplée: générer l'image par un autre processus (en s'assurant qu'il y a toujours une image disponible) et utiliser un outil très léger pour générer et servir le code HTML.
HTH,
Les réponses ci-dessus sont un peu dépassées - voici ce qui fonctionne pour moi sur Python3 + pour obtenir les octets bruts des données de figure.
import matplotlib.pyplot as plt
from io import BytesIO
fig = plt.figure()
plt.plot(range(10))
figdata = BytesIO()
fig.savefig(figdata, format='png')
Comme mentionné dans d'autres réponses, vous devez maintenant définir un en-tête "Content-Type" pour "image/png" et rédiger les octets.
En fonction de ce que vous utilisez comme serveur Web, le code peut varier. J'utilise Tornado comme serveur Web et le code pour le faire est le suivant:
self.set_header('Content-Type', 'image/png')
self.write(figdata.getvalue())
Sauf si je comprends mal votre question, tout ce que vous avez à faire est de vous connecter à l’emplacement de l’image et de lancer: python -m SimpleHTTPServer 8000 &
Puis ouvrez votre navigateur et tapez http://localhost:8000/
dans la barre d’URL.
ce qui fonctionne pour moi avec python3 est:
buf = io.BytesIO()
plt.savefig(buf, format='png')
image_base64 = base64.b64encode(buf.getvalue()).decode('utf-8').replace('\n', '')
buf.close()
Je sais que je suis un peu en retard à la fête ici, mais j'ai eu le même problème et j'ai fini avec le petit script ci-dessous.
Ce code python 3.6+:
Comme vous pouvez le constater par le code, il est délibérément minimal pour le diagnostic et la surveillance temporaires (des progrès de l’apprentissage automatique dans mon cas).
Vous devrez peut-être installer des dépendances (plac + toutes les autres bibliothèques nécessaires pour le traçage, par exemple, j'utilise des pandas, matplotlib)
Vous pouvez exécuter le fichier par double clic (pas de paramètre) ou en ligne de commande (avec/sans paramètre)
Code:
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 __== '__main__':
import plac; plac.call(main)