Flask入門　メモ

[Aidemy](https://aidemy.net/)　2020/10/4
#はじめに
　こんにちは、んがょぺです！バリバリの文系ですが、AIの可能性に興味を持ったのがきっかけで、AI特化型スクール「Aidemy」に通い、勉強しています。ここで得られた知識を皆さんと共有したいと思い、Qiitaでまとめています。以前のまとめ記事も多くの方に読んでいただけてとても嬉しいです。ありがとうございます！
　今回は、Flask入門　メモの投稿になります。どうぞよろしくお願いします。

＊本記事は「Aidemy」での学習内容を「自分の言葉で」まとめたものになります。表現の間違いや勘違いを含む可能性があります。ご了承ください。


#Flaskについて
##Flaskとは
・Flaskとは、Pythonのウェブアプリケーションフレームワークである。RubyでいうところのRailsと同様。
・Flaskは以下のように使用する。

```python
#Flaskパッケージのインポート
from flask import Flask
#Flaskクラスのインスタンス作成
app=Flask(__name__)
#URLにアクセスした時の関数を定義
@app.route('/')
def hello_world():
    return "Hello World"
#コードが直接実行された時のみ実行されるようにする。
if __name__=='__main__':
    app.run()
```

・__@app.route("URL")__は、その後ろで定義した関数を、URLにアクセスしたときに実行するという物である。
・＿name＿については後述する。

##＿name＿とは
・＿name＿は、ファイルごとに自動的に定義される変数で、__そのファイル名が格納される。__
・また、「直接ファイルが実行されたとき（コマンドでファイルを実行した時など）」は、__＿main＿が格納される。__（だから、＿name＿=='＿main＿'とすると、直接ファイルが実行された時、という条件になる）


#手書き文字判別アプリの実装
##コードの解説

```python
@app.route('/')
def hello_world():
    return render_template('index.html')
```

・__render_template('HTMLのファイル')__で、@app.route()のURLにアクセスしたときに、HTMLを反映させられる。
・この時のHTMLファイルは__templatesフォルダ__に入れなければならない。

##大まかな流れ

```python
#まずは分類するクラスをリスト表記
classes = ["0","1","2","3","4","5","6","7","8","9","10"]
#画像サイズの指定
image_size = 28
#アップロードされた画像を保存するフォルダの指定とアップロードを許可する拡張子の指定
UPLOAD_FOLDER = "uploads"
ALLOWED_EXTENSIONS = set(['png','jpg','jpeg'])

#Flaskクラスのインスタンス生成
app = Flask(__name__)

#アップロードされた画像の拡張子判定
def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

#モデルのロード
model = load_model('./model.h5')
```

・拡張子判定の「'.' in filename」と「filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS」は__条件式__である。これの正誤判定の結果をreturnで返す。

・「'.' in filename」はファイル名に「.」があるか、「filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS」はファイル名の「.」の後ろが「ALLOWED_EXTENSIONS」のどれかに該当するかを表している。この時の__「rsplit」__とは、文字列を後ろから区切るというものである。

##HTML側でアップロードされた画像ファイルを処理する
・HTML側で「・入力フォーム：__\<form>\</form>__ (method="POST"で画像をアップロード)」という説明をしたが、ここでは、入力フォームに入力されたデータを受け取って関数を実行する流れを見る。

```python
@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
```

・HTML側で\<form method="POST">とした後、Flaskで__「if request.method == 'POST'」__とすると、アップロードされたファイルに対して、そのあとに定義した関数などを適用できる。

・また、受け取った'POST'にファイルが格納されていなかったり、そもそもファイル名がついていなかったりした場合はモデルによる学習ができないため、そのような場合は元のページにリダイレクトさせるようにする。

```python
#ファイルがない場合の処理
  if 'file' not in request.files:
            flash('ファイルがありません')
            return redirect(request.url)
        file = request.files['file']
#ファイル名が空欄の時の処理
        if file.filename == '':
            flash('ファイルがありません')
            return redirect(request.url)
```

・そのあとは、先ほど定義した「allowed_file()」で拡張子が適切かを判断し、「file.save」で保存する。

##保存した画像を読み込み、モデルを適用する

```python
#受け取った画像を読み込み、np形式に変換
            img = image.load_img(filepath, grayscale=True, target_size=(image_size,image_size))
            img = image.img_to_array(img)
            data = np.array([img])
#変換したデータをモデルに渡して予測する
            result = model.predict(data)[0]
            predicted = result.argmax()
            pred_answer = "これは " + classes[predicted] + " です"
```

・__image.load_img(画像のURL,target_size=(縦,横))__によって、画像の読み込みとリサイズを同時に行う。grayscale=Trueは、読み込みをモノクロで行うという意味。
・__image.img_to_array(画像)__は、画像をNumPy配列にしている。さらに__np.array([img])__でimgをリストにしている。
・なぜこのように変換するのかというと、モデルによる予測（model.predict()）にはNumPyのリストしか渡せないからである。

```python
  return render_template("index.html",answer=pred_answer)
```

・__render_template("HTMLファイル",answer=予測結果)__とすることで、予測結果をHTMLファイルの{{ }}に渡すことができる。

#デプロイする
・デプロイとは、作成したアプリをHerokuを通じて公開することを言う。