Python+Flaskで画像データのやり取りをするAPIを作成する

Python+Flaskで画像データのやり取りをするAPIを作成する

画像処理を行うWebAPIを作成したいという方を対象に、Python+Flaskを利用して、画像つきのPOSTリクエストの受け取り→画像処理→画像を返す方法を紹介します。

最近は画像処理の分野における機械学習の発展が凄まじく、学習モデルを利用することで高度な画像処理を施すことが可能になってきました。学習モデルを利用した面白いWebアプリも多いですよね。

1枚の画像を送信して、画像処理を施した画像を返却するAPIの作成を行っていきます。

環境

この記事を書いたときに動作確認を行った環境です。

  • OS: Linux
  • Python: 3.8.6
  • Flask: 1.1.2

クライアント側プログラムの作成

まずは、画像を送信するクライアント側のプログラムを作成します。

パターン1:リサイズしてから送信

データの送信に時間がかかるので、画像はリサイズして小さく送ったほうが良いです。下記の例では、Pillowを利用した画像のリサイズを行っています。

import requests
from PIL import Image
from io import BytesIO
# 画像の読み込み
filename = 'example.jpg'
img = Image.open(filename).convert('RGB')
# 画像のリサイズ
SCALE = 2.0
img = img.resize((int(img.size[0] / scale), int(img.size[1] / scale)), Image.ANTIALIAS)
# 画像をbyteに変換
img_bytes = BytesIO()
img.save(img_bytes, format='JPEG', quality=95)
img_bytes = img_bytes.getvalue()
# localhostにリクエスト
URL = 'http://localhost:8080/sample'
headers = {'Content-Type': 'application/octet-stream'}
response = requests.post( url, data=img_bytes, headers=headers
)
print('status:', response.status_code)
print('content:', response.content)

ポイントは下記2点です

  • リクエストのヘッダーに'Content-Type': 'application/octet-stream'
  • 画像はio.BytesIO()でbyte型にする

パターン2:リサイズが必要ない場合

最初からbyteで画像データを読み取ります。こちらは簡単ですね。

import requests
from io import BytesIO
# 画像の読み込み
filename = 'example.jpg'
with open(filename,'rb') as f: img_bytes = f.read()
# localhostにリクエスト
URL = 'http://localhost:8080/sample'
headers = {'Content-Type': 'application/octet-stream'}
response = requests.post( url, data=img_bytes, headers=headers
)
print('status:', response.status_code)
print('content:', response.content)

API側プログラムの作成

リクエストを受け取るAPIの作成を行っていきます。上記のクライアントプログラムから来たデータを想定しています。

画像処理にPillowを利用している例です。

from flask import Flask, request, make_response, send_file
from PIL import Image
from io import BytesIO
@app.route('/sample', methods=['POST'])
def segment(): # Pillowに変換 img = Image.open(BytesIO(request.data)).convert('RGB') # 画像処理。ret_imgはPillowイメージ ret_img = something(img) img_io = BytesIO() ret_img.save(img_io, 'JPEG', quality=95) img_io.seek(0) response = make_response(send_file(img_io, mimetype='image/jpeg')) return response

最大のポイントは、img_io.seek(0)です。ここは絶対に忘れないでください。

上記の例ではJPEGで返却していますが、'PNG'指定も可能です。状況に応じて、save()とsenf_file()の中身を変更してください。

まとめ

今回はPython+Flaskで画像処理APIの作成を行ってみました。

今回はシンプルなAPIにしましたが、本来は通信の高速化のために画像サイズの縮小や圧縮が重要になってくると思います。

そのあたりの話はまた別の記事にまとめてみようと思います。