FastAPI
前回、FastAPIで画像データをJavaScriptに送り、HTMLで表示する方法を紹介しました。
今回はさらに画像データでもMatplotlibで作成したグラフのデータをFastAPIでJavaScriptに送り、HTML上に表示する方法を紹介します。
この場合に問題となるのは、グラフデータの保存です。
つまりFastAPI上でMatplotlibのグラフデータを作成し、サーバー上のストレージに保存できる場合は、前回の方法でJavaScriptに送れるのですが、ストレージがない場合(もしくは少ない場合や他と連携するのが面倒な場合)はそうはいきません。
今回はそんな場合に備えて、Matplotlibで作成したグラフデータをbase64形式のデータに変換して、JavaScriptに送る方法をを紹介します。
それでは始めていきましょう。
プログラム全体
今回は最初にプログラム全体をお見せします。
from fastapi import FastAPI
import random
import base64
import matplotlib.pyplot as plt
from fastapi.middleware.cors import CORSMiddleware
from io import BytesIO
import matplotlib
matplotlib.use('Agg')
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
def test13():
x = range(100)
y = [random.randrange(0,100) for _ in range(len(x))]
fig = plt.figure()
plt.cla()
plt.plot(x, y)
ofs = BytesIO()
fig.savefig(ofs, format="png")
png_data = ofs.getvalue()
plt.close()
base64_data = base64.b64encode(png_data).decode()
return {"img":base64_data}
ランダムで100点のデータを作成し、グラフとしています。
今回、重要なのは、
- MatplotlibのグラフデータをBytesIOのオブジェクトにバイナリデータのまま保存
- バイナリデータをbase64形式に変換(前回解説)
- Matplotlibによる画像を表示しない設定
の3点です。
グラフデータをBytesIOのオブジェクトに保存
Matplotlibでグラフを作成する部分は解説を省略します。
通常、作成したグラフを「fig.savefig(“ファイルパス”)」で保存するのですが、今回は画像ファイルとして保存をしません。
この場合はまずはBytesIOのオブジェクトにバイナリデータとして画像データを保存し、base64形式に変換するため、そのバイナリデータの値を取得するという流れになります。
ということでこんな感じ。
ofs = BytesIO()
fig.savefig(ofs, format="png")
png_data = ofs.getvalue()
「BytesIO()」でバイナリデータのオブジェクトを作成します。
作成したオブジェクト(ofs)に対し、「fig.savefig(ofs, format=”png”)」でpng形式のバイナリデータとしてデータを保存します。
そして「.getvalue()」でバイナリデータの値を取得します。
得られたバイナリデータの値をbase64形式に変換しますが、ここは前回解説していますので、解説は省略します。
base64_data = base64.b64encode(png_data).decode()
Matplotlibによる画像を表示しない設定
もう一つ重要なのが「Matplotlibによる画像を表示しない設定」です。
Matplotlibでグラフを表示するには「plt.show()」としますが、「plt.show()」しなくても勝手にグラフを表示しようとすることが多々あります。
ただサーバーではディスブレイがないので、画像を表示しようとしたって表示できません。
そのため今回matplotlibにディスプレイがない状態(バックエンド)で動かしますよ、だから画像を表示しなくていいですよと教えてあげる必要があります。
そのためのコマンドがこちら。
import matplotlib
matplotlib.use('Agg')
実はDjangoでグラフ作成する場合も同じコマンドが必要だったりしますので、FastAPIに限らずバックエンドでは必要だということを覚えておきましょう。
また環境によってはmatplolibのライブラリを読み込む順番によってエラーが出る場合があるようです。
今回、matplotlib関係のライブラリはこの順でインポートしています。
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('Agg')
しかし「matplotlib.use(‘Agg’)」とする前に「matplotlib.pyplot」をインポートするとエラーとなるということもあるようです。
ということでmatplotlib関連のエラーが出た場合は、こちらの順番も試してみてください。
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
FastAPI側がここまでできたら、JavaScrip(HTML)側でが、こちらは前回と同じなので解説は割愛します。
<!DOCTYPE html>
<html lang="ja">
<head>
<script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>
<meta charset="UTF-8">
</head>
<body>
<script>
async function callApi() {
const res = await fetch('http://127.0.0.1:8000')
.then(result => result.json())
.then((output) => {
document.write('<img src="data:image/png;base64,'+ output["img"]+'" alt="Bare">')
}).catch(err => console.error(err));
};
callApi();
</script>
</body>
</html>
これでFastAPIを「uvicorn main:app –reload」で起動し、HTMLにアクセスしてみるとこんな感じでmatplotlibのグラフが表示できました。
これでFastAPIでやりたいことができることが分かったので、一旦FastAPIの勉強はここでおしまいにします。
次回はPythonで自作モジュールをパッケージ化する方法を紹介します。
ではでは今回はこんな感じで。
コメント