[番外編]機械学習プログラミング 日向坂46で顔認識してみた。@Python (4)

(番外編)日向坂46で顔認識
 最近流行のAI(機械学習)でプログラムを書いてみました。
 尚、この一連の記事ではOracleは全く登場しません。
 この記事は、前回までの続きです。

  1. (番外編)日向坂46で顔認識@Python(1) 概要~学習用画像のスクレイピング
  2. (番外編)日向坂46で顔認識@Python(2) 概要~顔部分抽出、顔画像増幅処理
  3. (番外編)日向坂46で顔認識@Python(3) 学習~モデル作成~顔認識処理

6. 認識実行画面

さて、メインとなる認識処理のプログラムは出来上がりました。
あとはこれを表示する画面です。

Pythonで画面を構築するフレームワークというと、django(ジャンゴ)かflask(フラスク)が有名ですが、今回はflaskから派生した新しいフレームワーク「Responder」(レスポンダー)を使用します。

画面仕様は至ってシンプルなものです。

python3 facerecg_run.py
を実行すると、コンソールに

Using TensorFlow backend.
INFO:     Started server process [XXXX]
INFO:     Uvicorn running on http://127.0.0.1:5042 (Press CTRL+C to quit)
INFO:     Waiting for application startup.
INFO:     Application startup complete.

というような表示が出てきます。
ここで表示されたURLにアクセスすると、下記のような画面が出てきます。

ファイルダイアログ

検証したい画像ファイルをダイアログから選び、「送信」を押すと、顔認識処理が動き、認識結果が表示されます。

 画像がアップロードされると、アップロード先のパスを、前回作成した顔認識処理に引数として渡して、認識処理を実行します。
 結果を受け取り、画面にHTML表形式で表示するという仕組みです。

全体構成

下記のようなディレクトリ・ファイル構成になります。

app  +--- facerecg_run.py
     +--- facerecg5_recog.py
     +--- facerecg_image_comn.py
     +--- static ---+-- uploaded
     |              +-- main.css
     |              +-- reset.css
     |
     +--- template -+-- index.html
     |              +-- layout.html
     |              +-- success.html
     |
     +--- model ----+-- images_obj.npy
                    +-- model_hitanazaka.h5

認識画面の制御を行うfacerecg_run.pyのソースコードは以下のようになります。

#========================================
# 日向坂46メンバーの顔認識
# 7.認識 画面表示
#========================================
from pathlib import Path
import io
import responder
import requests
import datetime
import numpy as np
import pandas as pd
import facerecg6_recog as fr
 
def parentdir(path='.', layer=0):
    return Path(path).resolve().parents[layer]
 
def imgfilename(nowdt):
    return 'i' + nowdt + '.jpg'
 
BASE_DIR = parentdir(__file__, 0)
IMAGE_DIR = './static/uploaded/'
upldtime = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
 
# staticファイルを使用するときは、APIを定義するときに「static_dir」を指定します。
api = responder.API(
    static_dir=str(BASE_DIR.joinpath('static')),
)
 
@api.route("/")
async def greet_world(req, resp):
    resp.content = api.template('index.html')
 
@api.route("/upload")
async def upload_file(req, resp):
    @api.background.task
    def process_data(data):
        file=data['file']
        f = open(IMAGE_DIR + imgfilename(upldtime), 'wb')
        f.write(file['content'])
        f.close()
 
    data = await req.media(format='files')
    # アップロード処理
    process_data(data)
    # 顔認識処理
    frr = fr.predict(IMAGE_DIR + imgfilename(upldtime))
 
    # Dataframeに変換
    df = pd.DataFrame(frr)
    df.columns = ['score', 'name']
    df = df.iloc[0:5,:]  # 上位5位までを表示。ヘッダも含むので0-5で指定
    df = df.astype({'score': 'float'}, copy=True)
    df['score'] = ((df['score']*10000//1)/100)  # 小数点以下2位まで保持
 
    # HTML出力用のパイプを定義
    buffer = io.StringIO()
    df.to_html(buf=buffer, index=False, justify='center', escape=False)
    # バッファに格納された文字列を取得し、変数に代入
    frtag = buffer.getvalue()
    topname = df.loc[0, 'name']
 
    resp.content = api.template('success.html', inf=upldtime, top=topname , result=frtag)
 
if __name__ == '__main__':
    api.run(debug=False)
 

ファイルのアップロード

 ファイルアップロード部分の処理は、下記のサイトを参考にさせて頂きました。

 Python+ResponderでWEBアプリケーションを構築する 4.File Upload
 (https://qiita.com/t-iguchi/items/5c4294efffbba9a6bb84)

ファイル選択画面の表示にPythonは不要です。
下記の設定で、staticディレクトリにあるindex.htmlファイルが開かれますが、この中にファイルダイアログの記述があります。

<form id="fileupload" action="/upload" method="post" enctype="multipart/form-data">
    <input name="file" type="file" required/>
    <button type="submit">送信</button>
</form>

最初にindex.htmlを表示するのは、facerecg_run.pyにある下記の記述です。
@api.route(“/") の記述で、トップディレクトリへアクセスがあった場合のルーティングを制御します。

@api.route("/")
async def greet_world(req, resp):
    resp.content = api.template('index.html')

submit時のアクションはformタグ中のactionの記述「/upload」と、python側にあるルーティング「@api.route(“/upload")」で制御されます。
この記述により、アップロード時には「@api.route(“/upload")」以下の処理が実行されます。

ファイルのアップロードとデータの読込みを行う process_data は下記の記述により、バックグラウンドタスクとして実行されます。

@api.background.task
def process_data(data):

そのため、ファイルのアップロード実行中に画面が遷移し、認識処理結果を待機することになります。

ファイルのアップロードは、

data = await req.media(format='files')

以下で行います。
process_data で受けとったファイルの書込みを行います。

認識処理の実行と結果の表示

認識処理は下記の記述で行われます。
fr.predict(IMAGE_DIR + imgfilename(upldtime))

結果はndarray型の配列で戻ってきますが、これはdataframe型に変換し、2次元の表データとして保持します。
dataframeには、to_html というメソッドが有り、HTML文字列を出力することが出来ます。
この出力結果を、io.StringIO()を使ってioモジュールの文字列バッファに保存します。

そしてこのHTMLを api.template を使用して、"success.html" に埋め込むことができます。
呼出時の引数"inf","top","result" が、下記"success.html"中の{{ }} で囲まれた箇所に対応しています。

{% extends "layout.html" %}
{% block content %}
  <h1>日向坂46誰に似てる?</h1>
  <p>アップロードした写真は、<span class="topname"> {{ top }} </span>ちゃんに 似ていますね。</p>
  <!-- a href="#" onclick="history.back(-1); return false;">戻る</a -->
  <p><img src="/static/uploaded/i{{ inf }}.jpg"></p>
  {{ result | safe }}
  <p><a href="./">戻る</a></p>
{% endblock %}

Posted by tfurukaw