【コード解説】自作画像認識編 Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラム

【コード解説】自作画像認識編 Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラム

 

Table of Contents

【解説】自作画像認識編 Flask(Python)Web機械学習アプリ開発入門 – 画像アップロード判定プログラム

 

 

Google Colaboratoryの環境でKerasを使い、ディープラーニング(深層学習)でおなじみの「畳み込みニューラルネットワーク」(CNN:Convolutional Neural Network)を実装したオリジナルデータセットで自作画像認識AIを作れるプログラム

flask_keras_cnn_originaldataset_demo.ipynb| Google Colaboratory
(ファイル – ドライブにコピーを保存後にコピー環境で実行)

* 2021年6月14日時点で、Google Colaboratoryに公開中のサンプルコードでエラーが出ないように修正しておきました。

の学習済みモデルなどを活用して、Flask Web 自作画像認識AIアプリを実装出来るようにしたサンプルコードを作成し、コードの解説をしてみました。

* Keras:Pythonで書かれているTensorFlow上等で実行可能な高水準のニューラルネットワークライブラリ

 

機械学習プログラミングを始めたものの、オリジナルデータセットでのアプリ化の壁が超えられない・・・

機械学習を活用した自作画像認識Webアプリの基本的な仕組みを学びたい!

と考えている方の、何かしらのきっかけになることがありましたら幸いです。
今回のプログラムをきっかけに、Flaskを活用した他の機械学習Webアプリのサンプルコードも読み始められますように

尚、内容に関しては正確に記載するように努めておりますが、個人で学習した範囲の情報ですので、正確な情報等の詳細に関してはご自身でもよくお調べください。

【情報】

このページをPDF化した資料を無料でダウンロードできるように公開しました。

【追記:2023年2月14日】
「keras.preprocessing API」は「Tensorflow 2.9.1」で廃止されたようです。
Tensorflow 2.9.1より上のバージョンを使う場合には
「from keras.preprocessing.image import img_to_array」のところを「from tensorflow.keras.utils import img_to_array」に修正してください。

 




 

【コード全体の概略】Keras・自作データセット編:Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラムのサンプルコード・サンプルソース

 

 

サンプルコードダウンロード


初版:2021年2月9日
最終更新:2021年2月10日

 

学習済みモデル「keras_cnn_model_color_weight.h5」ダウンロード

 

画像認識用の丸バツ画像サンプルダウンロード

 

Mac・Windowsでの使い方解説はこちら
【サンプルコード】自作画像認識編 Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラム

 

 

ファイル構造:サンプルコード概略

 

ファイル構造:【サンプルコード】自作画像認識編 Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラム

 

Flask-Keras-Simple-Original-basic
-app.py(Pythonプログラム)
-keras_cnn_model_color_weight.h5
(Keras:CNNオリジナルデータセット画像をカラー設定で学習させた学習モデル構造と学習済みの重み)
*「keras_cnn_model_color_weight.h5」は別途ダウンロードして、「Flask-Keras-Simple-Original-basic」のフォルダ直下に入れてください。
-keras_cnn_model_gray_weight.h5
(Keras:CNNオリジナルデータセット画像を白黒のモノクロ・グレースケール設定で学習させた学習モデル構造と学習済みの重み)
-requirements.txt(必要なライブラリのインストール)
-README.md(説明など)
:templates
– base.html(HTMLの基本構造となるベースレイアウト)
– index.html(トップページ)
– result.html(予測結果のページ)
 

 

base.html:サンプルコード概略

 

base.html:サンプルコード【コード解説】自作画像認識編 Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラム

 

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>【オリジナルデータセット編】Flask+KerasでシンプルWeb自作画像認識AIアプリ</title>
        {% block head %} {% endblock %}
    </head>
    <body>
        <div style="text-align:center">
            <h2>【オリジナルデータセット編】</h2>
            <h3>Flask+KerasでシンプルWeb自作画像認識AIアプリ</h3>
        </div>

       {% block body %} {% endblock %}
    </body>
</html>

 

Web開発でお馴染みの

・HTML(HyperText Markup Language)

の基本構造(骨格)を記述しています。
ここで「base.html?」(共通テンプレート・ベースレイアウト)と思われる方もいるのではないかと思いますが、Flaskの特徴の1つである

・Jinja2

というPythonのためのテンプレート言語(テンプレートエンジン)を利用することで、

・(1度記述すれば、その後は)少量の記述だけで効率的にWebページを作成

できるようになります。要は使い回せるということです。

{} 内の記述で共通で使える部分を指定しています。
今回は、

・<head>
・<body>

を使い回します。
その他にも、Jinja2を活用することでHTMLの中にPythonを埋め込むことができるようになります。

 

 

index.html:サンプルコード概略

 

index.html:サンプルコード【コード解説】自作画像認識編 Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラム

 

{% extends 'base.html' %}

{% block body %} 

<form method="POST" action="/predict" enctype="multipart/form-data">
   <div style="text-align:center">
       <input type="file"  name="input_file" aria-label="ファイルの選択">
       <input type="submit" value="予測する">
   </div>
</form>

{% endblock %}

 

Web AIアプリのトップページ(ホームページ)の構造を記述しています。
Jinja2を使い「base.html」で記述したHTMLコードを再利用して

・<head>
・<body>

内の構造を反映させています。
その他に、

・ファイルのアップロード用フォーム
・予測を開始するためのボタン

などを設置しています。

 

 

result.html:サンプルコード概略

 

result.html:サンプルコード【コード解説】自作画像認識編 Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラム

 

{% extends 'base.html' %}

{% block body %}

 <div style="text-align:center">
    <h2>予測結果は{{ result_output }}です。</h2>
    <button><a href="/">戻る</a></button>
 </div>

{% endblock %}

 

Web AIアプリの予測結果ページの構造を記述しています。
「index.html」の時と同様に、Jinja2を使い「base.html」で記述したHTMLコードを再利用して

・<head>
・<body>

内の構造を反映させています。
機械学習Webアプリ開発で気になる

・予測結果を表示

させるための場所です。
ここまでHTML構造を見ただけでは、

どのように予測させているの?

と疑問に思うのではないかと思います。
次は、いよいよ今回の機械学習アプリの心臓部分を覗いてみましょう。

 

 

app.py:サンプルコード概略

 

 

from flask import Flask, render_template, request
from keras.models import load_model
from PIL import Image
from keras.preprocessing.image import img_to_array
#追記:「keras.preprocessing API」は「Tensorflow 2.9.1」で廃止。 Tensorflow 2.9.1より上のバージョンを使う場合には
#「from keras.preprocessing.image import img_to_array」のところを「from tensorflow.keras.utils import img_to_array」に修正してください
import numpy as np

app = Flask(__name__)

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

@app.route('/predict', methods=['POST'])
def predict():
    data = request.files['input_file'].stream
    im = Image.open(data)
    
    model = load_model('keras_cnn_model_gray_weight.h5') #モノクロ・グレースケール(白黒)の場合のモデル読み込み
    #model = load_model('keras_cnn_model_color_weight.h5') #カラーの場合のモデル読み込み

    folder = ['○:丸', '×:バツ']  
    image_size = 28
    color_setting = 1 #モノクロ・グレースケール(白黒)の場合は「1」にする
    #color_setting = 3 #カラーの場合の場合は「3」にする

    img = im.resize((image_size, image_size))
    img = img.convert(mode='L') #モノクロ・グレースケール(白黒)の場合は「L」
    #img = img.convert(mode='RGB') #カラーの場合は「RGB」

    img = img_to_array(img)
    img = img.reshape(image_size, image_size, color_setting).astype('float32')/255

    result = model.predict(np.array([img]))
    result = result[0]
    
    return render_template('result.html',result_output=folder[result.argmax()])

if __name__ == '__main__':
    app.run(debug=True)

 

ここには、機械学習アプリの核となるPythonプログラムを記述しています。
具体的には、

・Flask Webアプリを実行するために必要な
:Flask関連のインポート
:実装

・アップロード画像を予測するために必要な
:「機械学習モデル構造」「学習済みの重み」や、アップロード画像の処理と予測に必要な各種ライブラリなどのインポート
:実装

などを記述しています。

 

 

【コード解説】Keras・自作データセット編:Flask(Python)Web機械学習アプリ開発入門 – 画像アップロード判定プログラム

 

 

ある程度、Flaskで機械学習アプリを作成する方法を学ばれた方は、上記の概略を見るだけで、アプリの構造が大体わかるのではないかと思いますが、HTMLやPython、Flask初学者の方にとっては、意味不明なのではないかと思います。

以下に詳細を解説していきます。

 

 

base.html:サンプルコード解説

 

base.html:サンプルコード解説【コード解説】自作画像認識編 Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラム

 

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>【オリジナルデータセット編】Flask+KerasでシンプルWeb自作画像認識AIアプリ</title>
        {% block head %} {% endblock %}
    </head>
    <body>
        <div style="text-align:center">
            <h2>【オリジナルデータセット編】</h2>
            <h3>Flask+KerasでシンプルWeb自作画像認識AIアプリ</h3>
        </div>

       {% block body %} {% endblock %}
    </body>
</html>

 

<!DOCTYPE html>
#コード解説
:
文書型宣言(DOCTYPE宣言)。
HTMLバージョンによって書き方が変わりますが、今回はHTML5の文書型宣言の記述です。
この記述をすることで

・コードがHTMLであること
・HTMLのバージョン
・DTD(Document Type Definition:文書型定義)

などを宣言できるようです。古いバージョンのHTMLの記述方法に比べるとHTML5は、シンプルでいいすね。

 

<html lang=”ja”>
#コード解説
:
HTMLのlang属性。
html要素内で使われている言語は、日本語(ja:japanese)という意味です。

 

<head>〜</head>
#コード解説
:
<head>〜</head>内に文書のヘッダ部分を指定するための記述をします。
ここの記述の大部分は、ページが読み込まれてもWebブラウザには表示されませんが、

・Googleなどの検索結果の説明箇所
:meta description(メタディスクリプション)

・ブラウザのタブに表示されているテキスト(タイトル)
:title

などは、ここに記述されているようです。

 

<meta charset=”UTF-8″>
#コード解説
:
文字コード(文字エンコーディング)の指定。
meta要素のcharset属性を使うと文字コードを指定できます。今回は「UTF-8」を指定しています。
HTMLの世界では、HTMLファイルの文字コードと、HTMLの内容を表示させるブラウザの文字コードが異なると、文字化けが発生する原因になります。

 

<meta name=”viewport” content=”width=device-width, initial-scale=1″>
#コード解説
:
viewport(表示領域)の指定。
以下のように指定するとviewportの幅は実際の解像度ではなく端末やブラウザに合わせた値を返すようになるようです。
<meta name=”viewport” content=”width=device-width”>

initial-scaleで初期倍率を指定できます。今回は「1」を指定していますが、width=device-widthを記述すると、自動的に初期倍率は「1」となるようです。
当初は、サンプルコードをシンプルにするために、このコードを入れていませんでしたが、Edgeのブラウザに注意されたので入れておきました…

 

<title>【オリジナルデータセット編】Flask+KerasでシンプルWeb自作画像認識AIアプリ</title>
#コード解説
:
コンテンツ(ページ)のタイトルを指定。
<title>〜</title>内に好きなタイトルを入れます。
ここの記述で、

・Googleなどの検索結果のタイトル
・ブラウザのタブに表示されているテキスト(タイトル)

の表示が変わります。
基本的には、コンテンツの内容を上手く反映しているタイトルにすることで、検索エンジンを介して探す方に見つけてもらいやすい名前をつけます。

 

{% block head %} {% endblock %}
#コード解説
:
Jinja2でテンプレートの継承を設定。

・ {% block ブロックの名前 %} {% endblock %}

と記述することで、テンプレートとして他のHTMLページに継承したいブロックを指定できます。
今回は<head>〜</head>内に

・ {% block head %} {% endblock %}

と「head」と書くことで<head>〜</head>内の情報を継承することができるように設定しています。
他にも

<title>{% block title %}{% endblock %}</title>

とした場合、Pythonファイルの中で

return render_template(‘xxx.html’,title=’表示させたいタイトルを記述’)

などと記述することで、各ページの(タイトルの)中身を定義することもできるようです。

 

<body>〜</body>
#コード解説
:
<body>〜</body>の中に記述されたテキストや画像などがブラウザの画面上に表示されます。

 

<div style=”text-align:center”>
<h2>【オリジナルデータセット編】</h2>
<h3>Flask+KerasでシンプルWeb自作画像認識AIアプリ</h3>
</div>

 

<div style=”text-align:center”>〜</div>
#コード解説
:
中央揃えをしています。
今回は<h2>〜</h2>・<h3>〜</h3>内の

・【オリジナルデータセット編】
・Flask+KerasでシンプルWeb自作画像認識AIアプリ

などを中央に表示させています。

 

<h2>〜</h2>
<h3>〜</h3>
#コード解説
:
HTMLでマークアップする見出しタグです。

大見出し → 小見出し
h1・h2・h3・h4・h5・h6

などがあります。
<h2>〜</h2> と <h3>〜</h3>内に、表示させたいテキストを入力します。

 

{% block body %} {% endblock %}
#コード解説
:
Jinja2でテンプレートの継承を設定。
今回は<body>〜</body>内に

・ {% block body %} {% endblock %}

と「body」と書くことで<body>〜</body>内の情報を継承することができるように設定しています。

 

 

index.html:サンプルコード解説

 

index.html:サンプルコード解説【コード解説】自作画像認識編 Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラム

 

{% extends 'base.html' %}

{% block body %} 

<form method="POST" action="/predict" enctype="multipart/form-data">
   <div style="text-align:center">
       <input type="file"  name="input_file" aria-label="ファイルの選択">
       <input type="submit" value="予測する">
   </div>
</form>

{% endblock %}

 

{% extends ‘base.html’ %}
#コード解説
:
指定したHTMLを拡張。
今回は、ベースレイアウト「base.html」を使い回します。
具体的には、以下の構造

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>【【オリジナルデータセット編】Flask+KerasでシンプルWeb自作画像認識AIアプリ</title>
    </head>
    <body>
        <div style="text-align:center">
            <h2>【オリジナルデータセット編】</h2>
            <h3>Flask+KerasでシンプルWeb自作画像認識AIアプリ</h3>
        </div>
    </body>
</html>

を使い回しています。

 

{% block body %}〜{% endblock %}
#コード解説
:
ベースレイアウト「base.html」の<body>〜</body>内の内容に加えて、「〜」の部分に、新たに追加したいコードを記述します。

 

<form method=”POST” action=”/predict” enctype=”multipart/form-data”>
<div style=”text-align:center”>
<input type=”file” name=”input_file” aria-label=”ファイルの選択”>
<input type=”submit” value=”予測する”>
</div>
</form>
#コード解説
:
画像ファイルをアップロード後に、アップロードした画像ファイルをサーバーに送信(HTTPメソッド:POST送信)し、「〜/predict」のページを表示させるための処理です。

<form method=”POST” action=”/predict” enctype=”multipart/form-data”>
form method=”POST” action=”/predict”
:サーバーに送信。入力データを「〜/predict」のページに送信。

form enctype=”multipart/form-data
:データをマルチパートデータとして送信。ファイルを送信するフォームで必要なようです。

<div style=”text-align:center”>〜</div>
:中央揃え。
今回は<h2>〜</h2>内の

・ファイルの選択(画像アップロード用のボタン)
・予測する(予測実行用のボタン)

を中央に表示させています。

<input type=”file” name=”input_file” aria-label=”ファイルの選択”>
:<input>要素のtype属性に「file」を指定すると閲覧者が、自分のパソコン・スマホなどの端末のストレージからファイルを選択できるようになります。
「name=”input_file”」とすることで「app.py」のPythonプログラムで「input_file」という名前で画像ファイルを扱えるように指定しています。
「aria-label=”ファイルの選択”」についてですが、aria-label属性は、画面上には表示されませんがテキストリーダーなどでの読み上げの際に認識しやすいように設定しました。aria-label属性を付けなくても、見かけ上は「ファイルを選択」という表示があったのでよしとしていましたが、Edgeのブラウザで確認したところ、注意されましたので追記することにしました。

<input type=”submit” value=”予測する”>
:<input>要素のtype属性に「submit」を指定し、フォームの送信ボタンを作成します。「value=”予測する”」でボタン名を表示を指定しています。

 

 

result.html:サンプルコード解説

 

result.html:サンプルコード解説【コード解説】自作画像認識編 Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラム

 

{% extends 'base.html' %}

{% block body %}

 <div style="text-align:center">
    <h2>予測結果は{{ result_output }}です。</h2>
    <button><a href="/">戻る</a></button>
 </div>

{% endblock %}

 

{% extends ‘base.html’ %}
#コード解説
:
指定したHTMLを拡張。
「index.html」と同様に、ベースレイアウト「base.html」を使い回します。
具体的には、以下の構造

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>【【オリジナルデータセット編】Flask+KerasでシンプルWeb自作画像認識AIアプリ</title>
    </head>
    <body>
        <div style="text-align:center">
            <h2>【オリジナルデータセット編】</h2>
            <h3>Flask+KerasでシンプルWeb自作画像認識AIアプリ</h3>
        </div>
    </body>
</html>

を使い回しています。

 

{% block body %}〜{% endblock %}
#コード解説
:
ベースレイアウト「base.html」の<body>〜</body>内の内容に加えて、「〜」の部分に、新たに追加したいコードを記述します。

 

<div style=”text-align:center”>〜</div>
#コード解説
:
中央揃えをしています。
今回は<h2>〜</h2>内の

・予測結果は{{ result_output }}です。
・戻る(ボタン)

を中央に表示させています。

 

<h2>予測結果は{{ result_output }}です。</h2>
#コード解説
:
<h2>〜</h2>は、HTMLでマークアップする見出しタグです。

大見出し → 小見出し
h1・h2・h3・h4・h5・h6

などがあります。
<h2>〜</h2>内に、表示させたいテキストを入力します。
今回は

・予測結果は{{ result_output }}です。

と表示させています。
{{ result_output }}の所で「?」となるのではないかと思いますが、二重波括弧{{ }}内の「result_output」は変数名です。
{{ }} 内の記述が、Pythonのコードとして認識されます。

今回のプログラムでは、「index.html」でアップロードされた画像ファイル(変数名:input_file)を、「app.py」のPythonプログラムで予測した結果を「result_output」という変数名にしています。
そのため、{{ result_output }}と記述をすることで、予測結果を表示できます。

 

<button><a href=”/”>戻る</a></button>
#コード解説
:
指定したページに移動するボタンの作成。
「a href=”/”」とすることでトップページ(index.html)に移動するように設定しています。
<button><a href=”/”>〜</a></button>内には、ボタンの意味がわかりやすいようにテキストを入力します。今回は「戻る」ボタンとして機能するように設定しています。

 

 

app.py:サンプルコード解説

 

 

from flask import Flask, render_template, request
from keras.models import load_model
from PIL import Image
from keras.preprocessing.image import img_to_array
#追記:「keras.preprocessing API」は「Tensorflow 2.9.1」で廃止。 Tensorflow 2.9.1より上のバージョンを使う場合には
#「from keras.preprocessing.image import img_to_array」のところを「from tensorflow.keras.utils import img_to_array」に修正してください
import numpy as np

app = Flask(__name__)

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

@app.route('/predict', methods=['POST'])
def predict():
    data = request.files['input_file'].stream
    im = Image.open(data)
    
    model = load_model('keras_cnn_model_gray_weight.h5') #モノクロ・グレースケール(白黒)の場合のモデル読み込み
    #model = load_model('keras_cnn_model_color_weight.h5') #カラーの場合のモデル読み込み

    folder = ['○:丸', '×:バツ']  
    image_size = 28
    color_setting = 1 #モノクロ・グレースケール(白黒)の場合は「1」にする
    #color_setting = 3 #カラーの場合の場合は「3」にする

    img = im.resize((image_size, image_size))
    img = img.convert(mode='L') #モノクロ・グレースケール(白黒)の場合は「L」
    #img = img.convert(mode='RGB') #カラーの場合は「RGB」

    img = img_to_array(img)
    img = img.reshape(image_size, image_size, color_setting).astype('float32')/255

    result = model.predict(np.array([img]))
    result = result[0]
    
    return render_template('result.html',result_output=folder[result.argmax()])

if __name__ == '__main__':
    app.run(debug=True)

 

【FlaskのHello world!プログラムについて】

以下のコードで、ブラウザ上に「Hello world!」を表示できるようです。

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello world!'

if __name__ == '__main__':
    app.run(debug=True)

これが、FlaskでWebアプリを作成する上での基本的な構造です。
return ‘Hello world!’
の「Hello world!」のテキストを変えると、新たなテキストが表示できるようになります。

from flask import Flask
:flaskパッケージのFlaskクラスのインポート。

app = Flask(__name__)
:Flaskクラスの「app」という変数名のインスタンス作成。「app」という変数に、Flaskを定義して使えるようにする。
「__name__」は、自動的に定義される変数を指し、現在のファイルのモジュール名が入る。(例 app.pyの場合「app」)

@app.route(‘/’)
:「app」に「https://example.com」(URL「https://example.com」の例)のアドレスに処理を追加。
上記のHello world!プログラムでは「https://example.com」のページに、「Hello world!」を表示させる関数(index)を記述することで「Hello world!」をブラウザ上に表示しています。
「@」の行はデコレータと呼ばれるようです。
@app.route()の引数には、(‘/’)や(‘/predict’)などのURLを記述します。(‘/’)の場合は、アドレスのトップ(例 http://127.0.0.1:500/)が表示され、(‘/predict’)の場合には、http://127.0.0.1:500/predict が表示されます。

def index():
return ‘Hello world!’
:「Hello world」とブラウザに表示させる処理。
上記のHello world!プログラムでは「index」という関数名の関数が記述されています。

if __name__ == ‘__main__’:
app.run(debug=True)

if __name__ == ‘__main__’:
:Pythonプログラムを記述したファイル「app.py」が「python app.py というコード入力(コマンド入力)で実行されているかどうか?」を判定。
「import app.py」とコード入力(コマンド入力)した際に、プログラムが実行されることを防ぐ。

app.run(debug=True)
:Flaskの開発用サーバーで 「app」をデバッグモードで起動。

 

from flask import Flask, render_template, request
from keras.models import load_model
from PIL import Image
from keras.preprocessing.image import img_to_array
#追記:「keras.preprocessing API」は「Tensorflow 2.9.1」で廃止。 Tensorflow 2.9.1より上のバージョンを使う場合には
#「from keras.preprocessing.image import img_to_array」のところを「from tensorflow.keras.utils import img_to_array」に修正してください
import numpy as np

#コード解説
:
今回のプログラム(app.py)のPythonプログラムで必要なライブラリ・モジュールなどをインポートしています。

from flask import Flask, render_template, request
:Flaskを使うために必要なモジュールなどを読み込む。

render_template
:render_template()関数を利用することで、「index.html」「result.html」などのテンプレートにサーバのデータを組み込めるようになります。

request
:methods=[‘POST’]を使う際に必要です。

from keras.models import load_model
:Kerasで作成した機械学習の「モデル構造」「学習済みの重み」などを読み込むために必要です。
model = load_model(‘mnist_model_weight.h5’) #学習済みモデルの読み込み(重みを含む)
で使われています。

from PIL import Image
:PIL(Python Imaging Library)後継の画像処理ライブラリPillowを使います。
im = Image.open(data) #画像の読み込み
で使われています。

from keras.preprocessing.image import img_to_array
:読み込んだ画像をPIL形式(Pillow:Python Imaging Libraryから分岐した画像ライブラリのデータフォーマット)からndarray(NumPyの独自のデータ構造。多次元配列)に変換。
img = img_to_array(img)
で使われています。
追記:
「keras.preprocessing API」は「Tensorflow 2.9.1」で廃止。 Tensorflow 2.9.1より上のバージョンを使う場合には
「from keras.preprocessing.image import img_to_array」のところを「from tensorflow.keras.utils import img_to_array」に修正してください

import numpy as np
:科学技術計算や機械学習など、ベクトルや行列の演算などを行う分野でよく使われるPythonのライブラリ(または、拡張モジュール)の1つNumPyをインポートし、「np」という名前でプログラム内で使います。
np.array
で使われています。

 

@app.route(‘/’)
def index():
return render_template(‘index.html’)
#コード解説
:
:「app」に「https://example.com」(URL「https://example.com」の例)のアドレスに「index.html」のページを表示させる処理を追加。上記のコードでは、「index」と言う関数名が割り当てられています。
簡単に言うと、

・トップページ(ホームページ)に「index.html」に記述してあるHTMLを表示させる

のようなニュアンス。
Flaskでは、「render_template関数」を使い、HTMLファイルを表示させ、HTMLファイル内に値を入れることができます。「render_template」を利用する際の注意点として、Pythonプログラム内に「render_template」をimportすることと、「templates」と言う名前のフォルダを作成し、「templates」フォルダ内にHTMLファイルを入れておく必要があるようです。
「templates」フォルダを作成していないと指定したHTMLファイルを読み込むことが出来ないようです。

 

@app.route(‘/predict’, methods=[‘POST’])
def predict():

return render_template(‘result.html’,result_output=folder[result.argmax()])
#コード解説
:
「app」に「https://example.com/predict」(URL「https://example.com」の例)のアドレスに「result.html」のページを表示させる処理を追加。また、result.htmlに記述されている「result_output」と言う変数名には、app.pyのPythonプログラムで予測された「result」(変数名)の結果を反映させます。
「folder[result.argmax()]」という記述で「folder」内の予測確率が一番高い正解ラベルを出力できるようにします。後述しますが、事前に
folder = [‘○:丸’, ‘×:バツ’]
とすることで、今回は日本語のラベルが出力されるようにしています。

上記のコードでは、一連の処理を「predict」と言う名前の関数として定義しています。

 

def predict():
data = request.files[‘input_file’].stream
im = Image.open(data)

model = load_model(‘keras_cnn_model_gray_weight.h5’) #モノクロ・グレースケール(白黒)の場合のモデル読み込み
#model = load_model(‘keras_cnn_model_color_weight.h5’) #カラーの場合のモデル読み込み

folder = [‘○:丸’, ‘×:バツ’]
image_size = 28
color_setting = 1 #モノクロ・グレースケール(白黒)の場合は「1」にする
#color_setting = 3 #カラーの場合の場合は「3」にする

img = im.resize((image_size, image_size))
img = img.convert(mode=’L’) #モノクロ・グレースケール(白黒)の場合は「L」
#img = img.convert(mode=’RGB’) #カラーの場合は「RGB」

img = img_to_array(img)
img = img.reshape(image_size, image_size, color_setting).astype(‘float32’)/255

result = model.predict(np.array([img]))
result = result[0]
#コード解説
:
「index.html」でアップロードされた画像(変数名:input_file)を読み込み、Kerasで作成したオリジナルデータセットで作成した学習済みモデル(モデル構造+重み)の「keras_cnn_model_gray_weight.h5」を利用できるように画像を処理後に、画像認識結果を出力させています。
今回は、モノクロ・グレースケールとして学習させた学習済みモデルを使っています。
カラー画像としてオリジナルデータセットを学習させた場合には、コメントアウト部分(コードの先頭に#がついているコード)のコードを利用します。(コード先頭の#を消す)
その場合には、モノクロ・グレースケールの場合に使っていたコードの先頭に#をつけてコメントアウトさせてください。(または、削除してください)

 

data = request.files[‘input_file’].stream
:「index.html」でアップロードされた画像(変数名:input_file)を「data」と言う変数名でロードする。

im = Image.open(data)
:「im」と言う変数名で、「data」の画像ファイルを参照する。

model = load_model(‘keras_cnn_model_gray_weight.h5’)#モノクロ・グレースケール(白黒)の場合のモデル読み込み
:Kerasで作成したオリジナルデータセットをモノクロ・グレースケールで学習させた学習済みモデル(モデル構造+重み)の「keras_cnn_model_gray_weight.h5」を読み込む。

#model = load_model(‘keras_cnn_model_color_weight.h5’) #カラーの場合のモデル読み込み
:Kerasで作成したオリジナルデータセットをカラーで学習させた学習済みモデル(モデル構造+重み)の「keras_cnn_model_color_weight.h5」を読み込む。デフォルトでは、コメントアウトの#をコードの先頭につけて無効化しています。
「keras_cnn_model_color_weight.h5」の学習済みモデルは、別途ダウンロードして、「Flask-Keras-Simple-Original-basic」のフォルダ直下に入れてください。

folder = [‘○:丸’, ‘×:バツ’]
:オリジナルデータセットの各フォルダ名(ラベル)を「circle」(丸の画像を入れてあるフォルダ)、「cross」(バツの画像を入れてあるフォルダ)にしてあるものを上述のモデルの読み込みで、読み込んでいます。
今回は日本語の表示にしたかったので、folder = [‘circle’, ‘cross’] の順番で日本語に変更させています。ここの設定をしない場合は、データセットのフォルダ名が出力されます。

image_size = 28
:今回の一連のプログラムでは「28」を指定した場合、縦28横28ピクセルの画像に変換します。ここの数値は、オリジナルデータセットを学習させた際の画像の大きさと同じにします。
「keras_cnn_model_gray_weight.h5」「keras_cnn_model_color_weight.h5」の学習済みモデルでは、縦28横28ピクセルで学習させています。

color_setting = 1 #モノクロ・グレースケール(白黒)の場合は「1」にする
#color_setting = 3 #カラーの場合の場合は「3」にする
:今回の一連のプログラムでは、「1」はモノクロ・グレースケール画像。「3」はカラー画像として処理を行います。ここの数値は、オリジナルデータセットを学習させた際の、画像の形式に合わせます。デフォルトでは、「color_setting = 3」の方にコメントアウトの#を先頭につけて無効化しています。
「keras_cnn_model_gray_weight.h5」の学習済みモデルは、モノクロ・グレースケール画像として学習させたデータです。また、「keras_cnn_model_color_weight.h5」の学習済みモデルは、カラー画像として学習させたデータです。

img = im.resize((image_size, image_size))
:「im」と言う変数名の画像を「縦28×横28ピクセル」に変換し「img」と言う変数に入れる。今回の一連のプログラムでは「image_size = 28」と指定してありますので
img = im.resize((28, 28))
という意味になります。

img = img.convert(mode=’L’) #モノクロ・グレースケール(白黒)の場合は「L」
:「img」と言う変数名の画像をモノクロ・グレースケール化(白黒画像化:8bit グレイスケール 1チャンネル)。利用する学習済みモデルの画像処理形式に合わせて設定します。

#img = img.convert(mode=’RGB’) #カラーの場合は「RGB」
:「img」と言う変数名の画像をRGBモードに設定(RGBモード:8bit x 3チャンネル)。利用する学習済みモデルの画像処理形式に合わせて設定します。

img = img_to_array(img)
:「img」と言う変数名の画像をPIL形式(Pillow:Python Imaging Libraryから分岐した画像ライブラリのデータフォーマット)からndarray(NumPyの独自のデータ構造。多次元配列)に変換。

img = img.reshape(image_size, image_size, color_setting).astype(‘float32’)/255
:「img.reshape(image_size, image_size, color_setting)」の所で「img」と言う変数名の画像データのデータ構造の変換。
今回の一連のプログラムでは「image_size = 28」「color_setting = 1」と指定してありますので
img.reshape(28, 28, 1)
という意味になります。「縦28×横28ピクセル」の「モノクロ・グレースケール(白黒)」のデータ。
(縦28ピクセル, 横28ピクセル, モノクロ・グレースケール:チャンネル数)
カラー画像の場合は(28, 28, 3)。「縦28×横28ピクセル」の「カラー(RGBモード)」のデータ。
(縦28ピクセル, 横28ピクセル, カラー:チャンネル数)

:「.astype(‘float32’)/255」の所で「img」と言う変数名の画像データを正規化。黒から白までの変化を0〜255で表現しているので、255で割ることでデータを0〜1の範囲に正規化。float型にすることで実数値で表現。
正規化前「0〜255」 正規化実施「0.0〜1.0」(実数値)

result = model.predict(np.array([img]))
:「img」と言う変数名の正規化された画像データを元に、画像認識(予測)し、「result」と言う変数名に入れる。

result = result[0]:
:[0]を付けないと出力結果に「[]」がついてしまうので[0]を追記。

 

その後、上述のように

return render_template(‘result.html’,result_output=folder[result.argmax()])

で、予測結果をブラウザ上に表示することができます。

 

予測結果をブラウザ上に表示サンプルコード【コード解説】Keras・オリジナルデータセット編:Flask(Python)Web機械学習アプリ開発入門 - 画像アップロード

 

上記のコードでは、一連の処理を「predict」と言う名前の関数として定義しています。

 

【おわりに】Keras・自作データセット編:Flask(Python)Web機械学習アプリチュートリアルの解説

 

 

お疲れ様でした。
以上、簡単ではありますがFlaskを活用した機械学習Webアプリの解説です。

機械学習をするなら、やはりオリジナルデータセットで自分の関心のある物などを予測させられると面白いですね。
以下の

【コード解説】Keras・MNIST編:Flask(Python)Web機械学習アプリ開発入門 – 画像アップロード判定プログラム

の解説記事でも記載させていただきましたが、Webアプリ化をより楽しむためには、基礎的なWebアプリ開発の知識も必要そうです。そのため、
Pythonプログラミングの学習の他に

・HTML(HyperText Markup Language)
:Webサイトの骨格を作るハイパーテキストを記述するためのマークアップ言語

・CSS(Cascading Style Sheets)
:Webサイトのデザイン・見た目を整えるスタイルシート言語

・JavaScript
:Webサイトに動きをつけるオブジェクト指向スクリプト言語

などの基礎や、関連するフレームワーク(Bootstrapなど)を知っていると応用の幅が広がるのではないかと思います。
今回のコンテンツの情報が、これからオリジナルWeb AIアプリを作成したいと考えている方の学習の一助になることがありましたら幸いです。

 

 

印刷用PDF:【コード解説】Keras・自作データセット編:Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラム

 

外観イメージ - 印刷用PDF:【コード解説】Keras・自作データセット編:Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラム

 

学習の利便性を考え、印刷しやすいようにPDFも作成しました。
プリンタの印刷設定で、ブックレット等の冊子風に印刷すると読みやすいのではないかと思います。

 


初版:2021年2月10日

 

 

by 子供プログラマー | プログラミング入門ウェブ教室

 

 

HTML・CSS・JavaScriptなどが学べるWeb開発(ウェブ制作)入門者向けのチュートリアル
【Web開発入門】日本人のためのWeb Development for Beginners(microsoft / Web-Dev-For-Beginners:GitHub)