LoginSignup
0
0

More than 3 years have passed since last update.

pythonで果物分類してみた③モデル検討編

Last updated at Posted at 2021-02-05

7.Flaskアプリケーション化

・ Python+FlaskでのWebアプリケーション開発

Flaskにログイン
https://flask-login.readthedocs.io/en/latest/

Flaskとは

  • Flaskとは、Pythonのための軽量なウェブアプリケーションフレームワークです。
    フレームワークとは、アプリケーションソフトを開発する際に必要とされる機能をまとめて整えてくれている枠組み・ひな形を簡単に構築できるパッケージのことを指します。

  • Flaskを用いてサーバとの処理を行い、HTML&CSSでアプリの見た目を作ります。

Flaskモジュールのインストール

pipで拡張機能をインストールします。

pip install flask-login

Flaskウェブアプリケーションを実行してみる

hello.py

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return "Hello World!"

if __name__ == "__main__":
    app.run()

コード解説

  • flaskパッケージのFlaskクラスを使用することを宣言します。
from flask import Flask
  • appというFlaskクラスのインスタンスを作成します。

app. (Flaskクラスのメソッド名) とすることでFlaskクラスのメソッドを使えるようにします。

app = Flask(__name__)
  • @で始まる行はデコレータといって、その次の行で定義する関数やクラスに対して何らかの処理を行います。app.route()は、次の行で定義される関数を指定した URL に対応づけるという処理をしています。
@app.route('/')#http://127.0.0.1:5000/ 以降のパスを指定する
def hello_world():
    return "Hello World!"
  • name == 'main'がTrueである、すなわちこのコードが直接実行されたときのみapp.run()が実行され、Flaskアプリが起動します。
if __name__ == "__main__":
    app.run()
  • if __name__ == '__main__':の記述によって、直接そのPythonスクリプトを実行したとき(コマンドラインでpython ファイル名.pyを実行した時のように)のみ、if __name__ == '__main__':以下の処理を実行させることができます。そして、他のファイルからインポートされた場合には処理を実行しません。

ファイルフォルダtemplates/

HTML&CSSとは

  • HTMLとはHyper Text Markup Language(ハイパーテキストマークアップランゲージ)の略で、ホームページなどを作成する際に用いられます。文書構造を定義するものであり、骨組みや構造となるマークアップ言語です。ほぼすべてのウェブページにはHTMLが使われています。

  • CSSはCascading Style Sheets(カスケーディング・スタイル・シート)の略で、ホームページなどの見た目・スタイルを定義するための言語のことです。HTMLで画面に表示する文書構造などを定義し、CSSで表示方法や装飾などを定義します。

  • ローカルの環境で行ってください。

HTMLファイルを格納する場所

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>アプリ作成</title>
    <link rel="stylesheet"  href="./static/style.css" > 
    <script src="https://code.jquery.com/jquery-3.4.0.min.js"
    integrity="sha256-BJeo0qm959uMBGb65z40ejJYGSgR7REI4+CW1fNKwOg=" crossorigin="anonymous"></script>
</head>
<body>
  <div id="main" class="site-width">

       <!--<ul class="slider__container">

            <img src="https://www.dole.co.jp/banana/nutrients/assets/images/eiyo_img01.png" alt="" class="slider__item slider__item1 slider__item1">

            <img src="https://www.pakutaso.com/shared/img/thumb/PED_narandaapple2_TP_V.jpg" alt="" class="slider__item slider__item1">

            <img src="https://image.rakuten.co.jp/rainbow246/cabinet/02541763/03210664/imgrc0070019412.jpg" alt="" class="slider__item slider__item1">

          </ul> -->

          <form class="box" action="predict" method="post">
            <h1>Login</h1>
            <input type="text" name="name" placeholder="Username">
            <input type="password" name="pass" placeholder="Password">
            <input type="submit" >
          </form>
  </div>

</body>
<script src="./static/app.js"></script>

</html>
  • この部分はDOCTYPE宣言と呼ばれるもので、テキストファイルはHTMLだとブラウザに説明するための宣言

<!DOCTYPE html>
  • この部分は、このHTMLは英語を扱うとブラウザに説明するための宣言です。今回は日本語を扱うので先ほどのように"ja"と書き換えます。
<html lang="ja">
  • htmlでは下のようなタグと呼ばれる見出しをつけて、タグに囲まれたテキストやコードに様々な意味を持たせます。また、タグに囲まれたテキストやコードのことを要素といいます。
<head></head>
  • このまとまりはheadタグに囲まれているのでhead要素といいます。head要素の中にmetaタグやtitleタグ、CSSの読み込みを記入したりします。また、CSSの読み込みをする部分は自動で補完されないのでhead要素の最後に加えました。それでは順番に見ていきましょう。
<head>
    <meta charset="UTF-8">
    <title>アプリ作成</title>
    <link rel="stylesheet"  href="./static/style.css" > 
    <script src="https://code.jquery.com/jquery-3.4.0.min.js"
    integrity="sha256-BJeo0qm959uMBGb65z40ejJYGSgR7REI4+CW1fNKwOg=" crossorigin="anonymous"></script>
</head>

この部分は、このHTMLの文字コードはUTF-8だとブラウザに説明するための宣言です。この指定が違っていると文字化けの原因になります。


<meta charset="UTF-8">

title要素はページのタイトルを指定します。ここで指定したタイトルは、ブラウザのタブ上に現れます。

<title>アプリ作成</title>

CSSの読み込みをする部分です。href=""にCSSファイルの置いている場所とファイル名を渡します。

<link rel="stylesheet" href="./static/style.css">

以上でhead要素は終わりです。

メイン

div要素の「div」は「division」の略で、要素をグループ化するために使用されます。また、各要素をCSSで扱いやすいようにclass="xxx"としてラベリングしています


<div id="main" class="site-width">

       <!--<ul class="slider__container">

            <img src="https://www.dole.co.jp/banana/nutrients/assets/images/eiyo_img01.png" alt="" class="slider__item slider__item1 slider__item1">

            <img src="https://www.pakutaso.com/shared/img/thumb/PED_narandaapple2_TP_V.jpg" alt="" class="slider__item slider__item1">

            <img src="https://image.rakuten.co.jp/rainbow246/cabinet/02541763/03210664/imgrc0070019412.jpg" alt="" class="slider__item slider__item1">

          </ul> -->

          <form class="box" action="predict" method="post">
            <h1>Login</h1>
            <input type="text" name="name" placeholder="Username">
            <input type="password" name="pass" placeholder="Password">
            <input type="submit" >
          </form>
  </div>

見出しの要素です。h1>h2>h3>h4>h5>h6の順で重要度が高くなります。

        <h1>Login</h1>

この部分はファイルの選択と送信ボタンについての記述です。入力フォームを作成するときにはform要素を使います。ここでclass="box" action="predict" method="post"と指定していますが、これはboxリクエストで画像をアップロードするということを指します。すなわち、Flask入門で扱っているrequest.methodに"box"が渡され、if文がTrueになるためFlask側で処理が進められます。また、ファイルを送信するフォームではaction="predict" method="post">と指定しましょう。

<form class="box" action="predict" method="post">
            <h1>Login</h1>
            <input type="text" name="name" placeholder="Username">
            <input type="password" name="pass" placeholder="Password">
            <input type="submit" >
          </form>

ファイルのアップローダーを作成するときはinput要素type="text"と指定します。またname="name""name"はFlask入門で扱ったrequest.filesに渡されています。

<input type="text" name="name" placeholder="Username">
<input type="password" name="pass" placeholder="Password">

送信ボタンの部分です。typeには"submit"を指定します。valueには送信ボタンに書かれる文字を指定します。

<input type="submit" >

:補足

基本的には文章を入力する際に使います。パラグラフのpであり、1つの段落を意味します。
改行には br を使いましょう


<p>送信してください</p>

static/

・ HTMLファイル以外の、CSSファイル・JSファイル・画像ファイル等を格納する場所

・ モバイルWebアプリを作成するための、軽量なJavaScript UIライブラリです

app.js
//最初にどのスライドから表示するかを選択
var currentItemNum = 1;

var $slideContainer = $('.slider__container');

//クラスslider__itemの要素の個数を算出
var slideItemNum = $('.slider__item').length;

//スタイド1つあたりの横幅を算出
var slideItemWidth = $('.slider__item').innerWidth();

//スライド1つあたりの横幅とスライドの個数から
var slideContainerWidth = slideItemWidth * slideItemNum;

//スライドさせる速さ(animateメソッド用)
var DURATION = 1000;

//横に並べた複数のスライドを格納する用コンテナの横幅を設定
$slideContainer.attr('style', 'width:' + slideContainerWidth + 'px');

//スライドを自動的に変化させる関数
function autoSlide(){
    //現在のスライドの番号が全部のスライドの数より少ないとき
    if(currentItemNum < slideItemNum){
        //スライド用コンテナをanimateメソッドで変化させる
        $slideContainer.animate({left: '-='+slideItemWidth+'px'}, DURATION);//現在のCSSのleftのプロパティの値からスライド1つあたりの横幅分変化させる
        currentItemNum++;//現在のスライド番号を1増やす
    //現在のスライドの番号が全部のスライドの数と同じとき
    }else if(currentItemNum = slideItemNum){
        //現在のCSSのleftのプロパティの値を変更(徐々に変化させないのでanimateメソッドは使わない)
        $slideContainer.css('left', '+='+slideItemWidth*(slideItemNum-1)+'px');
        currentItemNum = 1;//現在のスライド番号を1に戻す
    }
}



//setInterval関数で適当な秒数ごとに実施
setInterval(autoSlide, 5000);


//logoutボタン
$('#logout').on('click', function() {
    window.location.href ="/logout"
});
style.css
body{
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  background: #34495e;
}

/*ログインフォームのエリア内のスタイル*/
.box{
  width: 300px;
  padding: 40px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: #191919;
  text-align: center;
}

.box{
  color: white;
  text-transform: uppercase;
  font-weight: 500;
}

/* ユーザーIDとパスワード入力ボックスのスタイル */
.box input[type="text"], .box input[type="password"]{
  border: 0;
  background: none;
  display: block;
  margin: 20px auto;
  text-align: center;
  border: 2px solid #3498db;
  padding: 14px 10px;
  width: 200px;
  outline: none;
  color: white;
  border-radius: 24px;
  transition: 0.25s;
}

/* 入力ボックスがフォーカスされたときのスタイル幅が大きくなる枠の色が変わる */
.box input[type="text"]:focus, .box input[type="password"]:focus{
  width: 280px;
  border-color: #2ecc71;
}

/* Loginボタンのスタイル */
.box input[type="submit"]{
  border: 0;
  background: none;
  display: block;
  margin: 20px auto;
  text-align: center;
  border: 2px solid #2ecc71;;
  padding: 14px 40px;
  outline: none;
  color: white;
  border-radius: 24px;
  transition: 0.25s;
  cursor: pointer;
}

/* Loginボタンをマウスオーバーしたときのスタイル */
.box input[type="submit"]:hover{
  background: #2ecc71;

}

/* Looutボタンのスタイル */
.box input[type="button"]{
  border: 0;
  background: none;
  display: block;
  margin: 20px auto;
  text-align: center;
  border: 2px solid #ff9100;;
  padding: 14px 40px;
  outline: none;
  color: white;
  border-radius: 24px;
  transition: 0.25s;
  cursor: pointer;
}

/* Looutボタンをマウスオーバーしたときのスタイル */
.box input[type="button"]:hover{
  background:  #ff9100;

}



body{
  background: #000000;
  color: #333;
}
.main{
  width: 1000px;
  margin: 30px auto;
}

.slider{
  /* スライドを表示させる画面の横幅(スライド1枚あたりの横幅) */
  width: 1000px;
  /* スライドを表示させる画面の縦幅(スライド1枚あたりの縦幅) */
  height: 500px;
  /* はみ出した要素は隠れるようにする */
  overflow: hidden;
}
.slider__container{
  /* スライドを表示する画面とスライド1枚をぴったり合わせるため設定 */
  padding: 0;
  margin: 0;

  /* 子要素でfloatを利用しているため設定 */
  overflow: hidden;

  list-style: none;

  /* 後ほどJSでleftプロパティを設定するため必要 */
  position: relative;

}
.slider__item{
  /* スライド1枚あたりの横幅 */
  width: 1000px;
  /* スライド1枚あたりの縦幅 */
  height: 500px;
  /* スライドを横一列に並べる */
  float: left;
}
main.py
import os
from flask import Flask, request, redirect, url_for, render_template, flash ,session
from werkzeug.utils import secure_filename
from tensorflow.keras.models import Sequential, load_model
from tensorflow.keras.preprocessing import image
import numpy as np

classes = ["リンゴ","バナナ","オレンジ"]
num_classes = len(classes)
image_size = 50

UPLOAD_FOLDER = "uploads"
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])

name ="test"
password = "0000"

app = Flask(__name__)
app.secret_key = "test_aidemy"

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

#model = load_model('./model.h5')
model = load_model('./traning.h5')
# ログイン画面
@app.route('/', methods=['GET', 'POST'])
def login_func():
    if "flag" in session and session["flag"]:
        return redirect("/predict")
    else:
        return render_template("index.html")

# ログアウト
@app.route('/logout', methods=['GET', 'POST'])
def logout_func():
    session.pop('flag', None)
    return redirect("/")

#学習済みモデルをロードする
@app.route('/predict', methods=['GET', 'POST'])
def upload_file():
        # ログインチェック
        if "name" in request.form and "pass" in request.form:
            if request.form["name"] == name and request.form["pass"] == password :
                session["flag"] = True
        # file チェック
        if ("flag" in session and session["flag"])==False :    
            return redirect("/")

        if 'file' not in request.files:
            return render_template("predict.html")  

        file = request.files['file']
        if file.filename == '':
            return render_template("predict.html") 
        if file and allowed_file(file.filename):

            filename = secure_filename(file.filename)
            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 = image.img_to_array(img)
            data = np.array([img])
            #変換したデータをモデルに渡して予測する
            result = model.predict(data)[0]

            predicted = result.argmax()
            #pred_answer = "これは" + classes[predicted] + "です"

            return render_template("predict.html",answer=predicted)

        return render_template("predict.html")



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

:補足

if __name__ == '__main__':の記述によって、直接そのPythonスクリプトを実行したとき(コマンドラインでpython ファイル名.pyを実行した時のように)のみ、if __name__ == '__main__':以下の処理を実行させることができます。

__name__というのはスクリプトファイルごとに自動的に定義される変数で、そのファイル名(モジュール名)が自動的に格納されます。ただし、ファイルを直接実行したときには__name____main__が自動的に格納されます。

test1.py

#関数show()を定義しておく
def show():
    return __name__

print(__name__)
#出力結果
__main__

test1.pyが直接実行されているため__name____main__が自動的に格納されるので、__main__と出力 されます。

Pythonスクリプトが実行された時には__name__という変数に__main__が格納されるため、__name__ == '__main__がTrueとなりif __name__ == '__main__':以下の処理が実行されます。

requirements.txtの作成

  • requirements.txt に必要なライブラリを記述することでHerokuの自分のアプリ内にそのライブラリがインストールされます。 runtime.txtには使用するPythonのバージョンを記述します。

(verなどを合わせてファイルの中身を書き直してください。)


absl-py==0.9.0
astor==0.8.1
bleach==3.1.5
bottle==0.12.18
click==7.1.2
certifi==2020.6.20
chardet==3.0.4
flask>=1.1.2
future==0.18.2
gast==0.3.3
grpcio==1.31.0
gunicorn==20.0.4
h5py==2.10.0
html5lib==1.1
itsdangerous==1.1.0
idna==2.10
Jinja2>=2.11.2
Markdown==3.2.2
MarkupSafe==1.1.1
numpy==1.18.0
oauthlib==3.1.0
pillow==7.2.0
protobuf==3.12.4
PyYAML>=5.3.1
python-dotenv==0.14.0
requests>=2.24.0
scipy==1.4.1
six==1.15.0
tensorboard==2.3.0
tensorflow==2.3.0
keras==2.4.3
termcolor==1.1.0
urllib3>=1.25.10
Werkzeug==1.0.1

公開設定

VScodeでwebアプリを動かしていました。ここではwebアプリをHerokuへデプロイし、全世界に公開する手順を学習します。

webアプリをデプロイする際には、サーバーを外部からも利用可能にするためにhost='0.0.0.0'と指定します。port = int(os.environ.get('PORT', 8080))では、Herokuでの使えるポート番号を取得してportに格納しています。設定されていなければ8080が格納されます。

mnist.pyの末尾の

if __name__ == "__main__":
    app.run()

の部分を

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

Webサーバを立てる

以下のコマンドをターミナルで実行するとWebサーバが起動します。


python main.py

するとターミナルに以下のようなメッセージが出るので、

Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)

ブラウザで http://0.0.0.0:8080/ を開きましょう。
スクリーンショット 2021-02-04 23.06.18.png

④へ続く ↓

0
0
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
0
0