1
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

女子プロゴルファーの顔診断AIを作ってみた③

Posted at

#1. はじめに
前回学習モデルの作成まで行いました。

今回は、学習モデルを用いて実際のWebアプリケーションを作成してデプロイまで行います。

完成イメージはこんな感じ
スクリーンショット 2020-08-04 21.01.56.png

#2. Flaskを用いて作成

main.py
# モジュールをインポートする
import cv2
import os
from flask import Flask, request, redirect, url_for, render_template, flash
from werkzeug.utils import secure_filename
from keras.models import Sequential, load_model
from keras.preprocessing import image
from PIL import Image
import tensorflow as tf
import numpy as np
from datetime import datetime
import face_recognition

# 選手名
classes = ['渋野日向子', '小祝さくら', '原英莉花']
num_classes = len(classes)
image_size = 64

# アップロードされた画像を保存するファイル
UPLOAD_FOLDER = "uploads/"
# アップロードを許可する拡張子を指定
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])

# Flaskクラスのインスタンスを設定
app = Flask(__name__)

# アップロードされたファイルの拡張子をチェックする関数を定義
def allowed_file(filename):
    # 1つ目の条件:変数filenameに'.'という文字が含まれているか。
    # 2つ目の条件:変数filenameの.より後ろの文字列がALLOWED_EXTENSIONSのどれに該当するかどうか
    # rsplitは区切る順序が文字列の最後から’1’回区切る。lowerは文字列を小文字に変換
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


# 顔を検出する(haarcascade)
def detect_face(img_path):
    image = face_recognition.load_image_file(img_path)
    faces = face_recognition.face_locations(image)
    if len(faces)>0:
        face_max = [(abs(faces[i][0]-faces[i][2])) * (abs(faces[i][1]-faces[i][3])) for i in range(len(faces))]
        top, right, bottom, left = faces[face_max.index(max(face_max))]#1人しか写っていなのでこれで問題ない
        faceImage = image[top:bottom, left:right]
        final = Image.fromarray(faceImage)

        final = np.asarray(final.resize((image_size,image_size)))
        final = Image.fromarray(final)

        basename = datetime.now().strftime("%Y%m%d-%H%M%S")
        filepath = UPLOAD_FOLDER + basename+".png"
        final.save(filepath)

        return final
    else:
        return "顔画像を入力してください"

#学習済みモデルをロードする
model = load_model('./golfer.h5', compile=False)

graph = tf.get_default_graph()

# app.route()で関数に指定したURLを対応づける。/ http://127.0.0.1:5000/以降のURLを指定
@app.route('/', methods=['GET', 'POST'])
def upload_file():
    global graph
    # as_default()で対象となるグラフを指定
    with graph.as_default():
        # HTTPメソッドがPOSTであれば
        if request.method == 'POST':
            # POSTリクエストにファイルデータが含まれているか
            if 'file' not in request.files:
                flash('ファイルがありません')
                # redirectは引数のURLに移動する関数
                return redirect(request.url)
            file = request.files['file']
            if file.filename == '':
                flash('ファイルがありません')
                return redirect(request.url)
            
            # ファイルがあって許可された形式であれば
            if file and allowed_file(file.filename):
                # ファイル名に危険な文字列がある場合、無効化する。
                filename = secure_filename(file.filename)
                # uploadsフォルダーに保存する
                file.save(os.path.join(UPLOAD_FOLDER, filename))
                # ファイルパスを作成する
                filepath = os.path.join(UPLOAD_FOLDER, filename)
                
                # #受け取った画像を読み込み、np形式に変換
                img = image.load_img(filepath, grayscale=False, target_size=(image_size,image_size))
                # 顔部分を検出する
                img = detect_face(filepath)

                if type(img)!=str:
                    img = image.img_to_array(img)
                    data = np.array([img])
                    #変換したデータをモデルに渡して予測する
                    result = model.predict(data)[0]
                    predicted = result.argmax()
                    pred_answer = "この女子プロは " + str(classes[predicted]) + " です"

                    return render_template("index.html",answer=pred_answer)
                else:
                    return render_template("index.html",answer=img)


        return render_template("index.html",answer="")

# FlaskでWebアプリ開発中にCSSが反映されない問題を解決する
@app.context_processor
def override_url_for():
    return dict(url_for=dated_url_for)

def dated_url_for(endpoint, **values):
    if endpoint == 'static':
        filename = values.get('filename', None)
        if filename:
            file_path = os.path.join(app.root_path,
                                 endpoint, filename)
            values['q'] = int(os.stat(file_path).st_mtime)
    return url_for(endpoint, **values)


if __name__ == "__main__":
    port = int(os.environ.get('PORT', 8080))
    app.run(host ='0.0.0.0',port = port)

#3. index.htmlとstylesheet

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>女子プロゴルファー判定</title>
    <!-- <link rel="stylesheet" href="./static/stylesheet.css"> -->
    <link rel= "stylesheet" type= "text/css" 
            href= "{{ url_for('static',filename='stylesheet.css') }}">
</head>
<body>
    <header>   
        <a class="header-logo" href="#">女子プロゴルファー判定</a>
    </header>

    <div class="main">    
        <h2>画像の顔を識別します</h2>
        <p>画像を送信してください</p>
        <form method="POST" enctype="multipart/form-data">
            <input class="file_choose" type="file" name="file">
            <input class="btn" value="submit!" type="submit">
        </form>
        <div class="answer">{{answer}}</div>
    </div>

    <footer>
    </footer>
</body>
</html>
stylesheet.css
header {
    background-color: rgb(100, 81, 255);
    height: 100px;
    margin: -8px;
    display: flex;
    flex-direction: row-reverse;
    justify-content: space-between;
}

.header-logo {
    color: #fff;
    font-size: 30px;
    margin: auto;
}

.header_img {
    height: 25px;
    margin: 15px 25px;
}

.main {
    height: 370px;
}

h2 {
    color: #444444;
    margin: 90px 0px;
    text-align: center;
}

p {
    color: #444444;
    margin: 70px 0px 30px 0px;
    text-align: center;
}

.answer {
    margin: 70px 0px 30px 0px;
    text-align: center;
    font-size: 30px;
    color: blue;
    font-weight: bold;
}

form {
    text-align: center;
}

h2 {
    color: #444444;
    text-align: center;
}

footer {
    background-color: #F7F7F7;
    height: 110px;
    margin: -8px;
    position: relative;
}

.footer_img {
    height: 25px;
    margin: 15px 25px;
}

small {
    margin: 15px 25px;
    position: absolute;
    left: 0;
    bottom: 0;
}

#4. ファイルの構成
ファイルの構成は以下のようにする
スクリーンショット 2020-08-04 21.38.29.png

templatesフォルダ内にindex.html
staticフォルダ内にstylesheet.css

残りのファイルはこちらの記事を参考にしてください
【完全版】Flaskで作ったAPIをHerokuにデプロイする手順(備忘録)

#5. herokuへデプロイする

####5-1. Herokuへ会員登録する
Herokuこちらより登録する

####5-2. Herokuへログイン

$ heroku login

####5-3. 設定
Create New App ⇨ App nameを入力し、国をUnited Statesで登録。
アプリのページに遷移しSetteingsに移動、Add buildpackをクリックしてPythonを追加。

####5-4. 以下をターミナルより実行

git init

※ 最初の1回だけ。2回やるとうまくデプロイできなくなります。

heroku git:remote -a (アプリ名)
git add .
git commit -m “(何を変更したかメッセージを書く)”
git push heroku master

以上でデプロイは完了です

#6. webアプリの確認
下記コードにて確認する

heroku open

以上で3回に渡った画像アプリの投稿を終了する。

1
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?