Pythonで画像処理のWebAPIを作る ~Falcon編~

はじめに

今回はPythonでWebAPIを作ります。最近はPythonで深層学習を使った画像処理が流行っているので、今回の私の記事を見てどんどんWebAPIで技術を公開してほしいです。もちろん、Apitoreに相談していただいてもOKです(ステマ)。フレームワークはfalconを使いました。軽量かつシンプルな構成でWebAPIサーバーを立てられます。

ソースコード

GitHub

下準備

以下のライブラリをインストールします。なお、今回はkeras + tensorflowで画像処理をすることを前提としていますが、そちらのインストールについては割愛します。

$ pip install cython falcon gunicorn falcon-multipart

Pythonは2.x系を使いましたが、3.x系でもイケると思います。falcon-multipartはfalcon公式のものではなくサードパーティ製になります。今回は「クライアントからPOST + multipartで画像ファイルを送信する」という想定です。falconはmultipartをデフォルトでサポートしていないようなので、falcon-multipartを使わせてもらいました。
※誰か「binaryをrequest bodyに埋め込んでPOSTし、サーバー側で受け取る」というPython実装をご存じの方が居たら教えてください。

実装

今回はkerasを使った画像認識をWebAPIにします。画像認識の部分は非常に有用なブログがありました。こちらの「VGG16のFine-tuningによる17種類の花の分類」のソースコードを使わせていただいて、これをWebAPIにしてみます。

シンプルな実装を以下に示します。名前はdebug_falcon.pyとします。

# coding:utf-8
import falcon
import json
import io
import os
import sys
from keras.applications.vgg16 import VGG16
from keras.models import Sequential, Model
from keras.layers import Input, Activation, Dropout, Flatten, Dense
import numpy as np
from datetime import date
from PIL import Image
from falcon_multipart.middleware import MultipartMiddleware

class DebugResource:
    def on_post(self, req, res):
        """Handles POST requests"""
        data = req.get_param('file').file.read()
        pilimg = Image.open(io.BytesIO(data))
        x = np.asarray(pilimg)

        # 画像を読み込んで4次元テンソルへ変換
        x = np.expand_dims(x, axis=0)

        # 学習時にImageDataGeneratorのrescaleで正規化したので同じ処理が必要!
        # これを忘れると結果がおかしくなるので注意
        x = x / 255.0

        # クラスを予測
        # 入力は1枚の画像なので[0]のみ
        pred = model.predict(x)[0]

        # 予測確率が高いトップ5を出力
        top = 5
        top_indices = pred.argsort()[-top:][::-1]
        result = classes[top_indices[0]]

        # 予測確率が高いトップ1を出力
        res.status = falcon.HTTP_200
        res.body = json.dumps({'result':result})

# 画像認識のイニシャライズ
result_dir = 'results'

classes = ['Tulip', 'Snowdrop', 'LilyValley', 'Bluebell', 'Crocus',
           'Iris', 'Tigerlily', 'Daffodil', 'Fritillary', 'Sunflower',
           'Daisy', 'ColtsFoot', 'Dandelion', 'Cowslip', 'Buttercup',
           'Windflower', 'Pansy']
nb_classes = len(classes)

img_height, img_width = 150, 150
channels = 3

# VGG16
input_tensor = Input(shape=(img_height, img_width, channels))
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)

# FC
fc = Sequential()
fc.add(Flatten(input_shape=vgg16.output_shape[1:]))
fc.add(Dense(256, activation='relu'))
fc.add(Dropout(0.5))
fc.add(Dense(nb_classes, activation='softmax'))

# VGG16とFCを接続
model = Model(input=vgg16.input, output=fc(vgg16.output))

# 学習済みの重みをロード
model.load_weights(os.path.join(result_dir, 'finetuning.h5'))

model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

# WebAPIの起動
api = falcon.API(middleware=[MultipartMiddleware()])
api.add_route('/debug', DebugResource())

起動

以下のコマンドを実行します。起動ポートは何番でも構いません。以下の例では5000番で起動しています。

$ gunicorn -b 127.0.0.1:5000 debug_falcon:api

APIを使ってみる

以下のコマンドを実行します。

$ curl -X POST http://127.0.0.1:5000/debug -H "Content-Type: multipart/form-data" -F "file=@hoge.jpg"

おわりに

今回はPythonでWebAPIを作ってみました。今回の記事で画像処理系のWebAPIがどんどん増えれば良いなと思います。

WebAPIにするメリットは「公開することで誰かが使ってフィードバックくれるかもしれない」というのもありますが、学習モデルのロードが最初の1回で済むので、2回目以降のリクエストに対しては高速に処理できるというものがあります。特に深層学習系だと学習モデルの容量が大きくなりがちなので、毎回リクエストのたびに学習モデルをロードしていたらとても遅くて話になりません。お役に立てば幸いです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です