LoginSignup
22
22

More than 5 years have passed since last update.

【顔分類その6】Djangoとkerasを利用した本田翼か佐倉綾音かを分類するWebアプリ

Last updated at Posted at 2019-02-06

目的

Pythonとkerasと事前に生成した顔分類モデルを利用して、画像ファイルの人物が本田翼か佐倉綾音かを分類できましたので、Djangoフレームワークを利用してWebアプリを作ってみました。
これまでに生成した顔分類モデルと、OpenCVのカスケードファイルを利用しています。

参考

【顔分類その1】PythonとGoogleカスタム検索APIを利用した画像ダウンロード
【顔分類その2】PythonとOpenCVを利用した画像ファイルからの顔の切り出し
【顔分類その3】PythonとOpenCVを利用した画像ファイルの水増し
【顔分類その4】Pythonとkerasを利用した顔分類モデルの生成
【顔分類その5】Pythonとkerasを利用して本田翼か佐倉綾音かを分類
【顔分類その6】Djangoとkerasを利用した本田翼か佐倉綾音かを分類するWebアプリ

環境

  • Windows 10 x64 1809
  • Python 3.6.5 x64
  • Power Shell 6 x64
  • Visual Studio Code x64
  • Git for Windows x64

環境構築

  • Windows 10 x64 に Python 3.6.x x64 をインストールします。

  • 環境変数の PATH に、インストールしたPythonのフォルダとPython\Scriptsフォルダを設定しておきます。

  • 下記手順で Python の仮想環境を構築し、有効化してpipをアップデートします。

> python -m venv venv
> .\venv\Scripts\activate.ps1
(venv)> python -m pip install --upgrade pip
  • pip でいろいろインストールします。
(venv)> pip install pylint
(venv)> pip install Django~=2.0.10
(venv)> pip install opencv-python
(venv)> pip install Pillow
(venv)> pip install tensorflow
(venv)> pip install keras

後ほど作成するプロジェクトのルートに、下記のような requirements.txt を作成しています。

Django~=2.0.10
pylint
opencv-python
Pillow
tensorflow
keras

プログラム作成

  • Django のプロジェクトを作成します。
(venv)> django-admin.exe startproject mysite .

プロジェクト名は mysite としました。
基本的な手順はDjango Girls のチュートリアルを参考にしています。

  • mysite/settings.py を修正します。
TIME_ZONE = 'Asia/Tokyo' # 修正
LANGUAGE_CODE = 'ja'     # 修正
# 追加
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
  • 念のためマイグレートしておきます。
(venv)> python manage.py migrate
  • ここまでの手順でWebサーバーを起動し、ブラウザから参照できることを確認します。
(venv)> python manage.py runserver

Ctrl+C でWebサーバーを停止します。

  • Django のアプリケーションを作成します。
(venv)> python manage.py startapp pred

アプリケーション名は pred としました。

  • mysite/settings.py を修正します。
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'pred', # 追加
]
  • プロジェクトのルートにモデルファイルとカスケードファイルを配置します。
img_pred_web
├─mysite
│  ├─settings.py
│  ├─urls.py
│  └─wsgi.py
├─pred
│  ├─migrations
│  ├─admin.py
│  ├─apps.py
│  ├─models.py
│  ├─tests.py
│  ├─urls.py
│  └─views.py
├─venv
├─db.sqlite3
├─haarcascade_frontalface_default.xml ← 配置
├─manage.py
├─model.h5 ← 配置
└─requirements.txt
  • mysite/settings.py を修正します。
# 追記
CASCADE_FILE_PATH = os.path.join(BASE_DIR, 'haarcascade_frontalface_default.xml')
MODEL_FILE_PATH = os.path.join(BASE_DIR, 'model.h5')
  • mysite/urls.py を修正します。
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('pred.urls')),
]

pred アプリケーションの urls を設定しています。

  • pred/urls.py を修正します。
from django.urls import path
from .views import PredView

urlpatterns = [
    path('', PredView.as_view(), name='index'),
]

後ほど作成するビューへのURLパターンを設定しています。

  • pred/forms.py を作成します。
from django import forms

class ImageForm(forms.Form):
    image = forms.ImageField(label="判定する画像を選択してください",
                             error_messages={'missing' : '画像ファイルが選択されていません。',
                                             'invalid' : '分類する画像ファイルを選択してください。',
                                             'invalid_image' : '画像ファイルではないようです。'})

画像ファイルをアップロードするためのフォームを作成しています。

  • pred/views.py を修正します。
from django.shortcuts import render
from django.views.generic import TemplateView
from .forms import ImageForm
from .main import detect

class PredView(TemplateView):
    # コンストラクタ
    def __init__(self):
        self.params = {'result_list': [],
                       'result_name': "",
                       'result_img': "",
                       'form': ImageForm()}

    # GETリクエスト(index.htmlを初期表示)
    def get(self, req):
        return render(req, 'pred/index.html', self.params)

    # POSTリクエスト(index.htmlに結果を表示)
    def post(self, req):
        # POSTされたフォームデータを取得
        form = ImageForm(req.POST, req.FILES)
        # フォームデータのエラーチェック
        if not form.is_valid():
            raise ValueError('invalid form')
        # フォームデータから画像ファイルを取得
        image = form.cleaned_data['image']
        # 画像ファイルを指定して顔分類
        result = detect(image)
        # 顔分類の結果を格納
        self.params['result_list'], self.params['result_name'], self.params['result_img'] = result
        # ページの描画指示
        return render(req, 'pred/index.html', self.params)

後ほど作成する main.py を利用して顔分類を行います。

  • pred/main.py を作成します。
import base64
import io
import cv2
import keras
import numpy as np
from PIL import Image
from keras.backend import tensorflow_backend as backend
from django.conf import settings

def detect(upload_image):
    result_name = upload_image.name
    result_list = []
    result_img = ''

    # 設定からカスケードファイルのパスを取得
    cascade_file_path = settings.CASCADE_FILE_PATH
    # 設定からモデルファイルのパスを取得
    model_file_path = settings.MODEL_FILE_PATH
    # kerasでモデルを読み込む
    model = keras.models.load_model(model_file_path)
    # アップロードされた画像ファイルをメモリ上でOpenCVのimageに格納
    image = np.asarray(Image.open(upload_image))
    # 画像をOpenCVのBGRからRGB変換
    image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    # 画像をRGBからグレースケール変換
    image_gs = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2GRAY)
    # カスケードファイルの読み込み
    cascade = cv2.CascadeClassifier(cascade_file_path)
    # OpenCVを利用して顔認識
    face_list = cascade.detectMultiScale(image_gs, scaleFactor=1.1,
                                         minNeighbors=5, minSize=(64, 64))

    # 顔が1つ以上検出できた場合
    if len(face_list) > 0:
        count = 1
        for (xpos, ypos, width, height) in face_list:
            # 認識した顔の切り抜き
            face_image = image_rgb[ypos:ypos+height, xpos:xpos+width]
            # 切り抜いた顔が小さすぎたらスキップ
            if face_image.shape[0] < 64 or face_image.shape[1] < 64:
                continue
            # 認識した顔のサイズ縮小
            face_image = cv2.resize(face_image, (64, 64))
            # 認識した顔のまわりを赤枠で囲む
            cv2.rectangle(image_rgb, (xpos, ypos),
                          (xpos+width, ypos+height), (0, 0, 255), thickness=2)
            # 認識した顔を1枚の画像を含む配列に変換
            face_image = np.expand_dims(face_image, axis=0)
            # 認識した顔から名前を特定
            name, result = detect_who(model, face_image)
            # 認識した顔に名前を描画
            cv2.putText(image_rgb, f"{count}. {name}", (xpos, ypos+height+20),
                        cv2.FONT_HERSHEY_DUPLEX, 1, (0, 0, 255), 2)
            # 結果をリストに格納
            result_list.append(result)
            count = count + 1

    # 画像をPNGに変換
    is_success, img_buffer = cv2.imencode(".png", image_rgb)
    if is_success:
        # 画像をインメモリのバイナリストリームに流し込む
        io_buffer = io.BytesIO(img_buffer)
        # インメモリのバイナリストリームからBASE64エンコードに変換
        result_img = base64.b64encode(io_buffer.getvalue()).decode().replace("'", "")

    # tensorflowのバックエンドセッションをクリア
    backend.clear_session()
    # 結果を返却
    return (result_list, result_name, result_img)

def detect_who(model, face_image):
    # 予測
    predicted = model.predict(face_image)
    # 結果
    name = ""
    result = f"本田 翼 の可能性:{predicted[0][0]*100:.3f}% / 佐倉 綾音 の可能性:{predicted[0][1]*100:.3f}%"
    name_number_label = np.argmax(predicted)
    if name_number_label == 0:
        name = "Honda Tsubasa"
    elif name_number_label == 1:
        name = "Sakura Ayane"
    return (name, result)

画像をBASE64エンコードする処理は https://qiita.com/miler0528/items/59caf9bd1ed4212edd3a を参考にさせて頂きました。

  • static/css/style.css を作成します。

スタイルシートの設定は割愛します。

  • templete/pred/base.html を作成します。
{% load static %}
<!doctype html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>kerasで深層学習</title>
        <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
        <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
        <link rel="stylesheet" href="{% static 'css/style.css' %}">
    </head>
    <body>
        <div class="container">
            {% block content %}
            {% endblock %}
        </div>
    </body>
</html>
  • templete/pred/index.html を作成します。
{% extends 'pred/base.html' %}
{% block content %}
<div style="padding:1em;">
    <div class="page-header">
        <h1>kerasで深層学習 <small>本田翼か佐倉綾音のどちらかに分類してみる</small></h1>
    </div>
    <form method="POST" action="{% url 'index' %}" enctype="multipart/form-data" class="form-horizontal">
        {% csrf_token %}
        <div class='form-group'>
            <div class='col-sm-4'>{{form.as_p}}</div>
            <div class='col-sm-8'><button type="submit" class="btn btn-primary">分類する</button></div>
        </div>
    </form>
    <p class="message">判定画像</p>
    <ul>
        {% if result_name != "" %}
            <li>{{ result_name }}</li>
        {% else %}
            <li>画像なし</li>
        {% endif %}
    </ul>
    <p class="message">判定結果</p>
    <ol>
        {% if result_list %}
            {% for result in result_list %}
                <li>{{ result }}</li>
            {% endfor %}
        {% else %}
            <li>結果なし</li>
        {% endif %}
    </ol>
    <div class="image">
        {% if result_img != "" %}
            <img src="data:image/png;base64,{{result_img}}"/>
        {% endif %}
    </div>
</div>
{% endblock %}
  • ここまででこんな感じになっているはずです。
img_pred_web
├─mysite (プロジェクト)
│  ├─__init__.py
│  ├─settings.py ← 修正
│  ├─urls.py ← 修正
│  └─wsgi.py
├─pred (アプリケーション)
│  ├─migrations
│  ├─static
│  │  └─css
│  │     └─style.css ← 作成
│  ├─templates
│  │  └─pred
│  │     ├─base.html ← 作成
│  │     └─index.html ← 作成
│  ├─__init__.py
│  ├─admin.py
│  ├─apps.py
│  ├─forms.py ← 作成
│  ├─main.py ← 作成
│  ├─models.py
│  ├─tests.py
│  ├─urls.py ← 修正
│  └─views.py ← 修正
├─venv
├─db.sqlite3
├─haarcascade_frontalface_default.xml ← 配置
├─manage.py
├─model.h5 ← 配置
└─requirements.txt ← 作成

プログラム実行

  • Webサーバーを起動し、ブラウザから参照できることを確認します。
(venv)> python manage.py runserver
  • ブラウザを起動し、http://127.0.0.1:8000/ にアクセスします。

  • このようなページになりました。
    WS000000.JPG

  • 画像ファイルを選択して分類してみます。

  • 分類できたようです。うれしい。
    WS000001.JPG

最後に

深層学習で画像の分類を行いたいという勢いから、いろいろな情報を参考にやってみました。
keras(tensorflow)を利用したWebアプリ開発のお作法が正しいのか不安なので、時間があるときに見直してみます。
顔分類はいったん区切りとして、まったく別のことをやってみようかと妄想中です。

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