Flaskで画像分類Webアプリを作成する(Mobile Net)

# 概要

Webアプリケーションフレームワークである<b>Flask</b>を使い、ブラウザからサーバーに画像を渡し、<b>Mobile Net</b>でクラス分類を行うAPIを作成しました。
今回はファインチューニングをせずに、ImageNetの学習パラメータを使用します。
<a href='https://arxiv.org/abs/1704.04861' target='_blank'>Mobile Netの論文</a>

# ツリー構造
MobileNetApi
├── mobile_net_model.h5
├── save_model_mobile_net.ipynb
├── server.py
└── templates
 　　　　├── flask_api_index.html
    　　　　├── layout.html
    　　　　└── result.html

# Mobile Netのモデルを保存する
以下のコードを実行したら`mobile_net_model.h5`というファイルが作成されます。

```save_model.py
import numpy as np
from keras.models import load_model
from keras.applications.mobilenet import MobileNet

# Mobile Net
model = MobileNet(weights="imagenet")
    
# モデルの保存、モデルをロードした後予測しかしないため、include_optimizer=Falseとする
model.save('mobile_net_model.h5', include_optimizer=False)
```

# Severの作成

```server.py
from flask import Flask, render_template, request, redirect, url_for, send_from_directory
import numpy as np
from keras.models import load_model
from keras.applications.mobilenet import MobileNet, preprocess_input, decode_predictions
from keras.preprocessing.image import img_to_array, load_img


app = Flask(__name__)

def img_pred(image):
    # 保存したモデルをロード
    model = load_model('mobile_net_model.h5')

    # 読み込んだ画像を行列に変換
    img_array = img_to_array(image)
    
    # 3次元を4次元に変換、入力画像は1枚なのでsamples=1
    img_dims = np.expand_dims(img_array, axis=0)

    # Top2のクラスの予測
    preds = model.predict(preprocess_input(img_dims))
    results = decode_predictions(preds, top=2)[0]
    
    # resultsを整形
    result = [result[1] + str(result[2]) for result in results]
    return result

@app.route('/')
def index():
    return render_template('./flask_api_index.html')

@app.route('/result', methods=['POST'])
def result():
    # submitした画像が存在したら処理する
    if request.files['image']:
        # 画像の読み込み
        image_load = load_img(request.files['image'], target_size=(224,224))

        # クラスの予測をする関数の実行
        predict_Confidence = img_pred(image_load)

        # render_template('./result.html')
        return render_template('./result.html', title='予想クラス', predict_Confidence=predict_Confidence)

if __name__ == '__main__':
    app.debug = True
    app.run(host='localhost', port=5000)
```

# HTMLの作成
`layout.html`を元に`flask_api_index.html`と`result.html`を作成していきます。

```layout.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>API Sample</title>
  </head>
  <body>
    {% block content %}
    <!-- ここに内容 -->
    {% endblock %}
  </body>
</html>
```

`flask_api_index.html`は`localhost:5000`にアクセスした時に表示するHTMLです。

```flask_api_index.html
{% extends "layout.html" %}
{% block content %}

<form action="/result" method="post" enctype="multipart/form-data">
  <input type="file" name="image" accept="image/png, image/jpeg, image/jpg">
  <button type="submit">Submit</button>
</form>

{% endblock %}
```

`result.html`は、`localhost:5000`で画像を選択し、Submitを押した後に結果を表示するHTMLです。

```result.html
{% extends "layout.html" %}
{% block content %}

<h3>予想クラス</h3>
{{ predict_Confidence }}


{% endblock %}
```

# Serverを立てて実行
ここまでできたら、以下のコードを実行しlocalhost:5000にアクセスしてください。

```
$ python3 server.py
```
このように表示されて入れば大丈夫です。

<img width="331" alt="スクリーンショット 2019-03-29 17.10.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/285122/57412142-bd3b-cb66-08dc-918f0113606e.png">

ペットボトルなどのモノの画像を選択し、submitを押してみます。
`http://localhost:5000/result`に遷移し、予想クラスのTop2つが表示されていればOKです。

<img width="409" alt="スクリーンショット 2019-03-29 17.12.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/285122/399c9e49-a9c9-fea9-993a-ccf951b58978.png">

# その他記事
<a href='https://qiita.com/kagami-r0927/items/3d426997467f0a975143' target='_blank'>Flaskで機械学習APIを作ってみた
</a>

<a href='https://qiita.com/kagami-r0927/items/117247725a0fe678cdeb' target='_blank'>VGG16をFine Tuningして2018年上半期ブレイク女優・俳優・芸人に分類してみた
</a>

<a href='https://qiita.com/kagami-r0927/items/7e76a1bbbfad9dd8bdad' target='_blank'>HerokuでPythonアプリをデプロイしてみた
</a>