Docker環境でPyTorch 〜画像解析〜 #04 セクシー女優学習データ作成編

## はじめに
[株式会社クリエイス](https://creaith.jp/)CTOの[志村](https://qiita.com/naoto1222)です。
前回の続きです。
この記事に最初に行き着いた方は前回の記事を見ていただき、環境を作るところから始めてください。
[Docker環境でPyTorch 〜画像解析〜 #01 環境構築編](https://qiita.com/creaith/items/92b00f5d03c8ecf3948c)
[Docker環境でPyTorch 〜画像解析〜 #02 モデル訓練&保存編](https://qiita.com/creaith/items/bf513e65809ab5c5ce46)
[Docker環境でPyTorch 〜画像解析〜 #03 転移学習編](https://qiita.com/creaith/items/589f6fcfb205b0554a27)

社員の[曽宮](https://qiita.com/ryosomiya)、[モトキ](https://qiita.com/pickles)が四苦八苦しながらまとめているこちらの記事もよろしかったらどうぞ
[Anaconda環境でPyTorch 〜株価予想〜 #04 予測(リベンジ)編](https://qiita.com/creaith/items/a7d0ba9b9cbb4be42490)

今回はセクシー女優で学習データを作っていくぞということで、下準備のために[上田パイセン](https://qiita.com/uedashuhei) に
学習データの収集をお願いしたら、wikiやらなんやらをスクレイピングしてきたらしく
セクシー女優名鑑が完成されていました(笑)

こちらは技術選定をしつつ色々ネットを漁って。。あれ？すげー損な役回りじゃね？とか思いつつも
せこせこと情報を比較してみたところ、お手軽にそれなりの精度ということでOpenCVとdlibを利用することに決めました。
OpenCVだけでも顔認識はできるんですが精度が低いので、顔認識はdlibに画像の加工はOpenCVという形で行います。

では始めていきます。

## この記事でやること
モデルを調教するための学習データ(セクシー女優顔抜き画像データ)を作っていきます。
[前回](https://qiita.com/creaith/items/589f6fcfb205b0554a27)行った転移学習につなげるために同じような構成になるように進めます。

## この記事でやらないこと
1. Dockerfileを使って公式Imageを元にカスタムImageを作りません
2. AWSのECS,　EKSには対応いたしません
3. コピペしやすい様にコードブロックの頭のユーザーマーク($, %, #)は記述いたしません
4. 学習データを集める作業はここでは割愛します
5. 検証画像を使ってテストは行いません

## 前提条件
1. 学習データはフォルダ名がラベルになる想定でおこないますが、これから使うOpenCVが日本語のパスに対応していないので英字必須です(やり方はあるらしいですが割愛します)
2. 学習データに利用するオリジナル画像は100枚程度となっていますが、精度を上げるためにはあればあるだけ良いです
3. 完全に趣味の世界なので、楽しんで行ってください
 
## 流れ
それでは、ざっくりとこんな感じで進めて行きます
1. セクシー女優の画像をとにかく沢山集めてきて女優毎にフォルダ分け(フォルダ名は英字必須)
2. OpenCVをインストール
3. dlibをインストール
4. テストコードを作る
5. 動かして確認する
 
```
# ディレクトリ構成図
~
└── git  
    ├── deep-learning
    │   └── pytorch (★current workspace)
　　　　　　　　│　      　├── data
    │       │   └──av_data　
    │　      　│      ├── origin
    │       │   　　  │   ├── JULIA
    │       │   　　  │   │    └── xxx.jpg　(サイズがバラバラのJULIA様の画像100枚程度)
    │       │   　　  │   ├── Rio
    │       │   　　  │   ⁝    └── xxx.jpg　(サイズがバラバラのRio様の画像100枚程度)
　　　　　　 │       │   　　  └── transfer 
    │       │   　　      ├── train　(教師画像 face_detect.py実行で配下は自動作成されます)
    │       │   　　      │    ├── JULIA
    │       │   　　      │    │    └── xxx.jpg　(JULIA様の顔をトリミングした64×64の画像100枚程度)
    │       │   　　      │    ├── Rio
    │       │   　　      │    ⁝    └── xxx.jpg　(Rio様の顔をトリミングした64×64の画像100枚程度)
    │       │   　　      └── val　(検証画像　今回は使いませんが以下のフォルダ構成になる予定)
    │       │   　　           ├── JULIA
    │       │   　　           │    └── xxx.jpg　(サイズがバラバラのJULIA様の画像70枚程度)
    │       │   　　           ├── Rio
    │       │   　　           ⁝　    └── xxx.jpg　(サイズがバラバラのRio様の画像70枚程度)   
    │       ├── src
    │       │   └── facedata_create
 　　　　　　│       │　　　　　　　　　　　　　　└── face_detect.py　(今回作るやつ)
    │       └── docker-compose.yml  
    └── ...
```



## 1. オリジナル画像を配置
手作業はしんどいのでスクレイピングしてoriginフォルダに配置してください

## 2. OpenCVをインストール

まずはdockerを起動して中に入ります

```
docker-compose up -d pytorch.examples
docker exec -i -t $(docker ps -af "name=pytorch.examples" -q) /bin/bash
```
そして本題

```
pip install opencv-python
# OpenCVをimportしたらlibgtkがないと怒られたのでこちらも一緒に入れる
apt-get install libgtk2.0-dev
```

## 3. dlibをインストール
まじで糞時間がかかります

```
# dlibを入れるのに必要。面倒だけどソースからインストール
curl -OL https://cmake.org/files/v3.12/cmake-3.12.2.tar.gz
tar xvf cmake-3.12.2.tar.gz
cd cmake-3.12.2
./configure
make
make install

pip install dlib
```

## 4. テストコードを作る

```face_detect.py

import cv2
import dlib
import glob
import os

data_dir = '/workspace/data/av_data'
origin_dir = data_dir+'/origin'
out_dir = data_dir+'/transfer/train'

def face_detect(img_list, save_dir):

    # dlibの顔検出する識別器呼び出し
    detector = dlib.get_frontal_face_detector()

    for img_path in img_list:

        # 保存先 & 保存名設定
        save_dir_path = os.path.join(out_dir, save_dir)
        save_filename = os.path.basename(img_path)
        save_img_path = os.path.join(save_dir_path, save_filename)

        # OpenCVでオリジナル画像読み込み
        img = cv2.imread(img_path, cv2.IMREAD_COLOR)

        # OpenCVで読み込むと色の順番はBGR(青、緑、赤)なのでRGBに変換する
        cv_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

        # dlibで顔の座標情報を取得
        dets = detector(cv_img, 1)

        # 顔が検出されたら変換処理
        if len(dets) > 0:
            for det in dets:

                # OpenCVで取得した座標から顔に合わせてトリミング
                dst_img = img[det.top():det.bottom(), det.left():det.right()]

                # 保存先フォルダがなければフォルダ生成
                if not os.path.exists(save_dir_path):
                    os.mkdir(save_dir_path)

                # OpenCVで顔画像をリサイズして書き出し
                save_img = cv2.resize(dst_img, (64,64))
                cv2.imwrite(save_img_path, save_img)
        else:
            print("don't detector "+img_path)

for f in os.listdir(origin_dir):
    in_dir = os.path.join(origin_dir, f)
    if os.path.isdir(in_dir):
        img_list = glob.glob(in_dir+'/*')
        face_detect(img_list, f)


```

## 5. 動かして確認する

```
cd /workspace/src/facedata_create
python face_detect.py
```

trainフォルダに学習データ用画像が出来上がりましたね。
中を確認すると顔の部分だけが綺麗にトリミングされているかと思います。
歯抜けになって作成されている場合は顔認識できずに失敗しているパターンですので無視して問題ありません。
なんか顔だけになると妙に悲しいですね(泣)

ご視聴ありがとうございました。

## 次回
最終回です！今回作った学習データと転移学習させたモデルを使って実践的な画像解析を行います。
うまく動いたらサービス化させようかな？需要ありますかね？