23

在这里以类似的方式提出了这个问题,但答案超出了我的想象(我对 python 和 Web 开发非常陌生),所以我希望有一种更简单的方法,或者可以用不同的方式来解释。

我正在尝试使用 matplotlib 生成图像并提供它,而无需先将文件写入服务器。我的代码可能有点傻,但它是这样的:

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>
"""

我认为我应该创建一个 cstringIO 对象,然后执行以下操作,而不是执行 pyplot.savefig('test.png'):

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

但我从那里迷路了。我见过的所有例子(例如http://lost-theory.org/python/dynamicimg.html)都涉及到类似的事情

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

而且我不知道如何将它与我已经输出的 HTML 集成。

4

6 回答 6

20

你应该

  • 首先写入一个 cStringIO 对象
  • 然后写HTTP头
  • 然后将 cStringIO 的内容写入标准输出

因此,如果发生错误savefig,您仍然可以返回其他内容,甚至是另一个标题。一些错误不会被更早地识别出来,例如,文本的一些问题、图像尺寸过大等。

你需要告诉savefig在哪里写输出。你可以做:

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())

如果要将图像嵌入 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()
于 2013-02-12T11:03:13.123 回答
12

上面的答案有点过时了——这就是我在 Python3+ 上获取图形数据的原始字节的方法。

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

如其他答案所述,您现在需要将“Content-Type”标头设置为“image/png”并写出字节。

根据您使用的网络服务器,代码可能会有所不同。我使用 Tornado 作为我的网络服务器,执行此操作的代码是:

self.set_header('Content-Type', 'image/png')
self.write(figdata.getvalue())
于 2015-06-01T12:56:54.553 回答
8

python3对我有用的是:

buf = io.BytesIO()
plt.savefig(buf, format='png')
image_base64 = base64.b64encode(buf.getvalue()).decode('utf-8').replace('\n', '')
buf.close()
于 2015-09-10T18:40:39.730 回答
5

我的第一个问题是:图像是否经常变化?你想保留旧的吗?如果它是实时的,那么您对优化的追求是合理的。否则,动态生成图像的好处并不是那么显着。

目前的代码需要 2 个请求:

  1. 获取您已有的 html 源代码
  2. 获取实际图像

可能最简单的方法(将网络请求保持在最低限度)是@Alex L 的评论,它允许您通过构建一个嵌入了图像的 HTML 来在单个请求中完成它。

您的代码将类似于:

# 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

这段代码可能不会开箱即用,但显示了这个想法。

请注意,如果您的图像没有真正经常更改或生成它需要很长时间,这通常是一个坏主意,因为它每次都会生成。

另一种方法是生成原始 html。加载它将触发对“test.png”的请求。您可以通过您已经提到的缓冲区流解决方案或从静态文件单独提供该服务。

就个人而言,我会坚持使用解耦的解决方案:通过另一个进程生成图像(确保始终有可用的图像)并使用非常轻量级的东西来生成和提供 HTML。

高温下,

于 2013-02-12T10:53:43.640 回答
2

除非我严重误解了您的问题,否则您需要做的就是 cd 到图像的位置并运行: python -m SimpleHTTPServer 8000 &

然后打开浏览器,http://localhost:8000/在 URL 栏中输入。

于 2013-02-12T02:46:34.003 回答
0

我知道我在这里聚会有点晚了,但我遇到了同样的问题,最后得到了下面的小脚本。

这个 python 3.6+ 代码:

  • 启动一个 Web 服务器并告诉您在哪里查看它
  • 扫描自身以查找以“plot_”开头的类方法,并为浏览器提供绘图列表
  • 对于单击的绘图,提示输入所需的参数(如果有),包括自动刷新周期(以秒为单位)
  • 执行情节并刷新

从代码中可以看出,临时诊断和监控(在我的例子中是机器学习进度)故意最小化。

您可能需要安装任何依赖项(plac + 绘图所需的任何其他库,例如我使用 pandas、matplotlib)

您可以通过双击(无参数)或命令行(带/不带参数)运行文件

代码:

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)
于 2018-07-03T00:50:18.603 回答