【顔認識入門】haar cascades＋vgg16でカメラ動画で「まゆゆ」の顔識別♪

昨夜とほぼ同様だが、以前収集したAKBメンバーの顔を学習して、顔識別したら名前を表示するようにした。
最終的には、顔識別したら音声で「おはよう、うわんさん」みたいなことをやろうと思っていますが、今回は表示までで記事にします。

###やったこと
・AKBメンバーの顔学習
・組み込んで名前表示する
・結果
###・AKBメンバーの顔学習
学習は以前総選挙メンバー学習のときに利用したアプリとデータを使用しました。
データ読込は以下のコードを使っています。
・[face_recognition/getDataSet.py](https://github.com/MuAuan/face_recognition/blob/master/getDataSet.py)

```
./train_images
     |--0
     |   1.jpg ...
     |--1
     .
     .
```
今回は、
name_list=['まゆゆ','さっしー','きたりえ','‎じゅりな','おぎゆか','あかりんだーすー']
の人たちの顔写真を利用いたしました。
顔写真の抽出は以下で実施しています。ここでも以下を使っています。
`cascade_path = "/Users/user/haarcascade/haarcascade_frontalface_alt2.xml"`
・[DataManage/face_detect.py](https://github.com/MuAuan/DataManage/blob/master/face_detect.py)
学習はvgg16のFinetuningです。
・[face_recognition/akb_VGG16.py](https://github.com/MuAuan/face_recognition/blob/master/akb_VGG16.py)
100epochの時点で以下のようなval_lossとval_accです。
学習データが少ないのでこれ以上学習しても精度が上がるわけもないので、これで動画になったときの精度を見たいと思います。
いずれにしても静的識別というレベルでは、6人の顔認識はほぼ出来ていると思います。

```
Train on 360 samples, validate on 60 samples
Epoch 1/1
360/360 [==============================] - 4s 12ms/step - loss: 2.7682e-04 - acc: 1.0000 - val_loss: 0.4305 - val_acc: 0.9167
```
###・組み込んで名前表示する
今回作成したアプリはいかに置きました。
・[face_recognition/recognize_vgg16_camera.py](https://github.com/MuAuan/face_recognition/blob/master/recognize_vgg16_camera.py)
まず、識別した後の名前は前回はvgg16の表示を利用しましたが、それが使えないので、以下のように変更しました。
すなわち、学習のフォルダに合わせて名前リストを作成し、それを識別結果に合わせて表示することとしました。

```py
def yomikomi(model,img):
    name_list=['まゆゆ','さっしー','きたりえ','‎じゅりな','おぎゆか','あかりんだーすー']
    x = image.img_to_array(img)
    x = np.expand_dims(x, axis=0)
    preds = model.predict(x)
    index=np.argmax(preds)
    print(index,name_list[index])
    return name_list[index]
```
modelは学習で利用したmodelを使います。

```py
def model_definition():
    batch_size = 2
    num_classes = 6
    img_rows, img_cols=224,224
    input_tensor = Input((img_rows, img_cols, 3))
    # 学習済みのVGG16をロード
    # 構造とともに学習済みの重みも読み込まれる
    vgg16 = VGG16(weights='imagenet', include_top=False, input_tensor=input_tensor)
    
    # FC層を構築
    top_model = Sequential()
    top_model.add(Flatten(input_shape=vgg16.output_shape[1:])) #vgg16,vgg19,InceptionV3, ResNet50
    top_model.add(Dense(256, activation='relu'))
    top_model.add(Dropout(0.5))
    top_model.add(Dense(num_classes, activation='softmax'))

    # VGG16とFCを接続
    model = Model(input=vgg16.input, output=top_model(vgg16.output))
    model.summary()
    # 新たに学習した重みを読み込む
    model.load_weights('params_model_VGG16L3_i_100.hdf5')
    return model
```
日本語の表示を動画中にするのが少し大変です。
[この技術は以前QRコードの日本語を画面に表示させる](https://qiita.com/MuAuan/items/7265da5281aa69623a03)ときに利用しました。
【参考】
・[Python + OpenCV でWindowsの日本語フォントを描画する](https://qiita.com/goodboy_max/items/d5e7f1c0ecd3d446c4d0)
ということで、以下のように変更します。
※変更を理解するのに必要な部分だけ記載します

```py
def main():
    #カスケード分類器の特徴量を取得する
    cascade = cv2.CascadeClassifier(cascade_path)
    color = (255, 255, 255) #白
    ...
    model=model_definition()
    ## Use HGS創英角ゴシックポップ体標準 to write Japanese.
    fontpath ='C:\Windows\Fonts\HGRPP1.TTC' # Windows10 だと C:\Windows\Fonts\ 以下にフォントがあります。
    font = ImageFont.truetype(fontpath, 16) # フォントサイズが32
    font0 = cv2.FONT_HERSHEY_SIMPLEX #fps表示に利用
    while True:
        b,g,r,a = 0,255,0,0 #B(青)・G(緑)・R(赤)・A(透明度)
        ...
        #グレースケール変換
        image_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        facerect = cascade.detectMultiScale(image_gray, scaleFactor=1.1, minNeighbors=2, minSize=(30, 30))
        img=frame
        if len(facerect) > 0:
            #検出した顔を囲む矩形の作成
            for rect in facerect:
                y,x=tuple(rect[0:2])
                y2,x2=tuple(rect[0:2]+rect[2:4])
                print(y,x,y2,x2)
                cv2.rectangle(frame, tuple(rect[0:2]),tuple(rect[0:2]+rect[2:4]), color, thickness=2)
                roi = frame[y:y2, x:x2]  #frame[y:y+h, x:x+w]
                try:
                    roi = cv2.resize(roi, (int(224), 224))
                    #cv2.imshow('roi',roi) #確認のために表示
                    txt=yomikomi(model,roi)
                    #print("txt",txt) #確認のために表示
                except:
                    txt=""
                    continue
                img_pil = Image.fromarray(frame) # 配列の各値を8bit(1byte)整数型(0～255)をPIL Imageに変換。
                draw = ImageDraw.Draw(img_pil) # drawインスタンスを生成
                position = (y, x) # テキスト表示位置
                draw.text(position, txt, font = font , fill = (b, g, r, a) ) # drawにテキストを記載 fill:色 BGRA (RGB)
                img = np.array(img_pil) # PIL を配列に変換
        cv2.imshow('test',img)
        key = cv2.waitKey(1)&0xff
        if is_video=="True":
            img_dst = cv2.resize(img, (int(224), 224)) #1280x960
            out.write(img_dst)
            print(is_video)
```
###結果
ここまでやったが、どうも精度が悪い。
もともと傾いていると顔さえ認識できないが、さらに認識しても誰なのかの識別がどうもできない。
以下に「あかりんだーすーさん」の画像を変化させて見せた時の記録された動画を貼っておく。

[face recognition by vgg16](https://youtu.be/uAVlo0ZQvo0)
<a href="http://www.youtube.com/watch?feature=player_embedded&v=uAVlo0ZQvo0" target="_blank"><img src="http://img.youtube.com/vi/uAVlo0ZQvo0/0.jpg"  </a>
※画像をクリックするとYouTube動画につながります
###まとめ
・顔画像を学習してカメラ動画で顔識別してみた
・識別結果の日本語を動画に貼り付けた

・静的識別結果はいいはずなのに精度が悪い理由が不明である