やりたいこと
画像をjsonとしてPOSTして、flaskで受け取りたい。
ただ、bytes型のデータをjsonの値とすることはできないためひと工夫が必要。
ローカルでサーバーを立てて試してみた。
base64って?
前述の通り、バイナリデータをjsonの要素とすることができない。テキストデータならOK。
それならばバイナリデータをある規則で一旦テキストデータに変換して伝送して、受け取った先で元のバイナリデータに変換すればよい。
バイナリデータをテキストデータに変換する規則の一つがbase64である。
#こうした
結構丁寧に解説。
まずクライアント側。
データの変遷は以下の通り。
画像をPillowImageとして読み込む
⇒ bytesに変換する
⇒ base64でエンコードする(まだbytes)
⇒ bytesだったデータをstrに変換する
⇒ json.dumpsしてjsonにする
⇒ 無事jsonでPOSTできる
import requests
from PIL import Image
import json
import base64
from io import BytesIO
img = Image.open("iruka.jpeg")
# PillowImageをbytesに変換してさらにbase64に変換
buffered = BytesIO()
img.save(buffered, format="JPEG")
img_byte = buffered.getvalue() # bytes
img_base64 = base64.b64encode(img_byte) # base64でエンコードされたbytes ※strではない
# まだbytesなのでjson.dumpsするためにstrに変換(jsonの要素はbytes型に対応していないため)
img_str = img_base64.decode('utf-8') # str
files = {
"text":"hogehoge",
"img":img_str
}
r = requests.post("http://127.0.0.1:5000", json=json.dumps(files)) # jsonとしてサーバーにPOSTする
print(r.json())
>>>{'img_shape': [750, 500], 'text': 'hogehogefuga'}
次いで、サーバーサイド。
データの変遷は以下の通り。
jsonで受け取る
⇒ jsonからお目当てのデータ(base64でエンコードされたテキストデータ)を取り出す
⇒ base64でエンコードされたテキストデータをデコードしてbytesに変換する
⇒ Pillowで扱えるように_io.BytesIOに変換する
⇒ 無事に元のPillowImageを取得できる
from flask import Flask, jsonify, request
from PIL import Image
import json
import base64
from io import BytesIO
import matplotlib.pyplot as plt
app = Flask(__name__)
@app.route("/", methods=["GET", "POST"])
def index():
json_data = request.get_json() # POSTされたjsonを取得
dict_data = json.loads(json_data) # jsonを辞書に変換
img = dict_data["img"] # base64を取り出す # str
img = base64.b64decode(img) # base64に変換された画像データを元のバイナリデータに変換 # bytes
img = BytesIO(img) # _io.BytesIO pillowで扱えるように変換
img = Image.open(img)
img_shape = img.size # 取得した画像で適当に処理
text = dict_data["text"] + "fuga" #取得したテキストで適当に処理
# 処理結果をクライアントに返す
response = {
"text":text,
"img_shape":img_shape
}
return jsonify(response)
if __name__ == "__main__":
app.debug = True
app.run()
サーバーのレスポンスからも正しく画像を処理されたことが確認できた。
ちなみに
base64.b64encode()は入力がbytesで出力もbytes。
base64.b64decode()は入力はbytesでもstrでもよいが、出力はbytes。
参考
python - コマンド - POSTからbase64をデコードしてPILで使用する
How to convert PIL Image.image object to base64 string? [duplicate]