【顔分類その５】Pythonとkerasを利用して本田翼か佐倉綾音かを分類

## 目的

ショートカットが大好きなので、Pythonとkerasと事前に生成した顔分類モデルを利用して、画像ファイルの人物が本田翼か佐倉綾音かを分類してみました。
これが何番煎じかわからないほど似たようなものがあるなかで恐縮ですが、自分でやってみると面白いものです。
本田翼を0 佐倉綾音を1 としてラベル付けした顔画像をもとに生成した顔分類モデルを利用しています。

実装方法は https://blog.aidemy.net/entry/2017/12/17/214715 を参考にさせて頂きました。

## 参考

[【顔分類その１】PythonとGoogleカスタム検索APIを利用した画像ダウンロード](https://qiita.com/kerobot/items/f98b73cbd5f52c1fdac2)
[【顔分類その２】PythonとOpenCVを利用した画像ファイルからの顔の切り出し](https://qiita.com/kerobot/items/e3abe3f21808b4b584bd)
[【顔分類その３】PythonとOpenCVを利用した画像ファイルの水増し](https://qiita.com/kerobot/items/54bc1224424280150d1c)
[【顔分類その４】Pythonとkerasを利用した顔分類モデルの生成](https://qiita.com/kerobot/items/be4fd1a166073fbcff38)
[【顔分類その５】Pythonとkerasを利用して本田翼か佐倉綾音かを分類](https://qiita.com/kerobot/items/1ce75622d173d231d423)
[【顔分類その６】Djangoとkerasを利用した本田翼か佐倉綾音かを分類するWebアプリ](https://qiita.com/kerobot/items/65d4e1942a31c00317e2)

## 環境

* Windows 10 x64 1809
* Python 3.6.5 x64
* Power Shell 6 x64
* Visual Studio Code x64
* Git for Windows x64
* OpenCV 3.4.4

## 環境構築

* Windows 10 x64 に OpenCV 3.4.4 をインストールします。

> カスケードファイルのみ利用します。

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

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

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

```powershell
> python -m venv venv
> .\venv\Scripts\activate.ps1
(venv)> python -m pip install --upgrade pip
```

* pip でいろいろインストールします。

```powershell
(venv)> pip install pylint
(venv)> pip install opencv-python
(venv)> pip install matplotlib
(venv)> pip install tensorflow
(venv)> pip install keras
(venv)> pip install python-dotenv
```

* インストールした OpenCV のカスケードファイルのパスを設定した.envファイルを作成します。

```python
CASCADE_FILE_PATH = "C:\opencv344\sources\data\haarcascades\haarcascade_frontalface_default.xml"
```

## プログラム作成

* 本田翼か佐倉綾音かを分類するプログラムを Python で書きます。

* ファイル名は「img_decide.py」とします。

```python
import sys
import os
import cv2
import keras
import numpy as np
import matplotlib.pyplot as plt
import settings

def detect_face(model, cascade_filepath, image):
    # 画像をBGR形式からRGB形式へ変換
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    # plt.imshow(image)
    # plt.show()
    # print(image.shape)
    # グレースケール画像へ変換
    image_gs = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    # 顔認識の実行
    cascade = cv2.CascadeClassifier(cascade_filepath)
    face_list = cascade.detectMultiScale(image_gs, scaleFactor=1.1,
                                         minNeighbors=2, minSize=(64, 64))

    # 顔が１つ以上検出できた場合
    if len(face_list) > 0:
        print(f"認識した顔の数:{len(face_list)}")
        for (xpos, ypos, width, height) in face_list:
            # 認識した顔の切り抜き
            face_image = image[ypos:ypos+height, xpos:xpos+width]
            print(f"認識した顔のサイズ:{face_image.shape}")
            if face_image.shape[0] < 64 or face_image.shape[1] < 64:
                print("認識した顔のサイズが小さすぎます。")
                continue
            # 認識した顔のサイズ縮小
            face_image = cv2.resize(face_image, (64, 64))
            # plt.imshow(face_image)
            # plt.show()
            # print(face_image.shape)
            # 認識した顔のまわりを赤枠で囲む
            cv2.rectangle(image, (xpos, ypos), (xpos+width, ypos+height),
                          (255, 0, 0), thickness=2)
            # 認識した顔を1枚の画像を含む配列に変換
            face_image = np.expand_dims(face_image, axis=0)
            # 認識した顔から名前を特定
            name = detect_who(model, face_image)
            # 認識した顔に名前を描画
            cv2.putText(image, name, (xpos, ypos+height+20),
                        cv2.FONT_HERSHEY_DUPLEX, 1, (255, 0, 0), 2)
    # 顔が検出されなかった時
    else:
        print(f"顔を認識できません。")
    return image

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

RETURN_SUCCESS = 0
RETURN_FAILURE = -1
# Inpou Model Directory
INPUT_MODEL_PATH = "./model/model.h5"

def main():
    print("===================================================================")
    print("顔認識 Keras 利用版")
    print("学習モデルと指定した画像ファイルをもとに本田翼か佐倉綾音かを分類します。")
    print("===================================================================")

    # 引数のチェック
    argvs = sys.argv
    if len(argvs) != 2 or not os.path.exists(argvs[1]):
        print("画像ファイルを指定して下さい。")
        return RETURN_FAILURE
    image_file_path = argvs[1]

    # 画像ファイルの読み込み
    image = cv2.imread(image_file_path)
    if image is None:
        print(f"画像ファイルを読み込めません。{image_file_path}")
        return RETURN_FAILURE

    # モデルファイルの読み込み
    if not os.path.exists(INPUT_MODEL_PATH):
        print("モデルファイルが存在しません。")
        return RETURN_FAILURE
    model = keras.models.load_model(INPUT_MODEL_PATH)

    # 顔認識
    cascade_filepath = settings.CASCADE_FILE_PATH
    result_image = detect_face(model, cascade_filepath, image)
    plt.imshow(result_image)
    plt.show()

    return RETURN_SUCCESS

if __name__ == "__main__":
    main()
```

* 設定ファイルを読み込むプログラムを Python で書きます。

* ファイル名は「settings.py」とします。

```python
import os
from os.path import join, dirname
from dotenv import load_dotenv

dotenv_path = join(dirname(__file__), '.env')
load_dotenv(dotenv_path)

CASCADE_FILE_PATH = os.environ.get("CASCADE_FILE_PATH")
```

## プログラム実行

* modelフォルダに学習モデルファイル（model.h5）を配置します。

> 本田翼と佐倉綾音でモデルが生成されている前提です…。

* 画像ファイルを指定してプログラムを実行します。

```powershell
(venv)> python .\img_decide.py c:\temp\test.jpg
```

* OpenCVが正面顔を認識してくれれば、ほぼ分類できました。

認識した顔の数:1
認識した顔のサイズ:(230, 230, 3)
本田 翼　 の可能性:1.836%
佐倉 綾音 の可能性:98.164%
![Figure_3.png](https://qiita-image-store.s3.amazonaws.com/0/283306/2b880328-5c57-be71-3860-c2f3b0c2573d.png)

認識した顔の数:1
認識した顔のサイズ:(387, 387, 3)
本田 翼　 の可能性:99.782%
佐倉 綾音 の可能性:0.218%
![Figure_4.png](https://qiita-image-store.s3.amazonaws.com/0/283306/0381b761-6dd4-e013-eb83-72ea24db884a.png)

## 最後に

深層学習で画像の分類を行いたいという勢いから、いろいろな情報を参考にやってみました。
本田翼と佐倉綾音のどちらかに分類するのみなので、それ以外という判定をしてみたいです。
とりあえず動くものができたので、次はWebアプリとして実装してみます。