web-dev-qa-db-ja.com

pythonを使用してウェブにmatplotlib画像を動的に提供する

この質問は同様の方法で尋ねられました ここ が答えは私の頭上にありました(私は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>
"""

私は、pyplot.savefig( 'test.png')を実行する代わりに、cstringIOオブジェクトを作成して、次のようなことをすることになっていると思います:

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

しかし、私はそこからかなり迷っています。私が見たすべての例(例 http://lost-theory.org/python/dynamicimg.html )は次のようなことを含みます

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

それを、すでに出力しているHTMLと統合する方法がわかりません。

21
Ben S.

あなたがすべき

  • 最初にcStringIOオブジェクトに書き込みます
  • 次に、HTTPヘッダーを記述します
  • 次に、cStringIOの内容をstdoutに書き込みます

したがって、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()
20
Thorsten Kranz

上記の答えは少し時代遅れです-ここで、Python3 +で私がFigureデータの生のバイトを取得するのに役立つ方法を説明します。

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」に設定し、バイトを書き出す必要があります。

ウェブサーバーとして何を使用しているかによって、コードは異なります。私はWebサーバーとしてトルネードを使用し、そのためのコードは次のとおりです。

self.set_header('Content-Type', 'image/png')
self.write(figdata.getvalue())
8
Mike N

私にとってpython3でうまくいくのは:

buf = io.BytesIO()
plt.savefig(buf, format='png')
image_base64 = base64.b64encode(buf.getvalue()).decode('utf-8').replace('\n', '')
buf.close()
6
trulio

最初の質問は次のとおりです。画像は頻繁に変更されますか?古いものを保持しますか?それがリアルタイムのものである場合、最適化の探求は正当化されます。そうでなければ、その場で画像を生成することの利点はそれほど重要ではありません。

現状のコードには2つのリクエストが必要です。

  1. あなたがすでに持っているhtmlソースを取得する
  2. 実際の画像を取得する

おそらく最も単純な方法(Web要求を最小限に抑える)は、@ Alex Lのコメントです。これにより、画像が埋め込まれたHTMLを作成することにより、1つの要求でそれを行うことができます。

あなたのコードは次のようになります:

# 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

このコードは、そのままでは機能しない可能性がありますが、アイデアを示しています。

willは毎回生成されるため、画像があまり頻繁に変更されない場合、または生成に長い時間がかかる場合、これは一般に悪い考えです。

別の方法は、元のhtmlを生成することです。それをロードすると、「test.png」のリクエストがトリガーされます。すでに述べたバッファストリーミングソリューションを介して、または静的ファイルから、個別に提供できます。

個人的には、分離されたソリューションを使用します。別のプロセスで画像を生成し(常に使用可能な画像があることを確認し)、非常に軽量なものを使用してHTMLを生成および提供します。

HTH、

5
Laur Ivan

私があなたの質問をひどく誤解していない限り、あなたがする必要があるのは、イメージの場所にcdして実行するだけです:python -m SimpleHTTPServer 8000&

次に、ブラウザーを開き、URLバーにhttp://localhost:8000/と入力します。

2
Alptigin Jalayr

私はここでのパーティーに少し遅れていることを知っていますが、これと同じ問題があり、以下の小さなスクリプトで終わってしまいました。

この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)
0
QA Collective