顔画像から類似度を計算する

# 概要
乃木坂46のメンバー(斎藤飛鳥さん、生田絵梨花さん、白石麻衣さんなど)の顔と欅坂46の長濱ねるさんの顔がどれだけ類似しているかを出力するコードを作成しました。

<img width="180" alt="スクリーンショット 2018-11-21 22.23.20 1.png" src="https://qiita-image-store.s3.amazonaws.com/0/285122/729662e4-ac28-0c42-c1d8-aabccdf3d36b.png">

Confidenceが0に近くなれば、似ているということになります。
トレーニングに使用した画像をテストデータとして使用するとConfidenceが0になるので注意です。

# トレーニングデータ・テストデータを用意する
[PythonでGoogle Custom Search APIを使い画像収集してみた](https://qiita.com/kagami-r0927/items/f88d04cbb4a4f43a2c23)
これを参考に画像のスクレイピングをしてみてください！



# 類似度を計算するscript

[OpenCVで乃木坂46秋元真夏と銀シャリ鰻和弘の類似度を調べてみた](https://blog.aidemy.net/entry/2018/06/18/135601)
こちらの記事を参考に作成してみました。


```similarity.py
import cv2
import os
import numpy as np
from PIL import Image
import re
import os.path

# トレーニング画像
train_path = './train_乃木坂images'

# テスト画像
test_path = './test_images'

# Haar-like特徴分類器
cascadePath = './haarcascade_frontalface_alt.xml'
faceCascade = cv2.CascadeClassifier(cascadePath)
recognizer = cv2.face.LBPHFaceRecognizer_create()

# フォルダ内の画像を習得
def get_images_and_labels(path):
    # 画像を格納する配列
    images = []
    # ラベルを格納する配列
    labels = []
    for f in os.listdir(path):
        # 画像のパス
        image_path = os.path.join(path, f)
        # 白黒で読み込み
        image_pil = Image.open(image_path).convert('L')
        # Numpyの配列に格納
        image = np.array(image_pil, 'uint8')
        # Haar-like特徴分類器で顔を検知
        faces = faceCascade.detectMultiScale(image)
        # 検出した画像の処理
        for(x, y, w, h) in faces:
            # 200×200にリサイズ
            roi = cv2.resize(image[y: y + h, x: x + w], (200, 200), interpolation=cv2.INTER_LINEAR)
            #画像を配列に格納
            images.append(roi)
            int_number = re.findall("\d+", f)
            for number in int_number:
                labels.append(int(number))
    
    return images, labels
        
# トレーニング画像を取得
images, labels = get_images_and_labels(train_path)

# トレーニング実施
recognizer.train(images, np.array(labels))

# テスト画像を取得
test_images, test_labels = get_images_and_labels(test_path)

in_jpg= os.listdir('./test_images')
for  i in range(len(in_jpg)):
    # テスト画像に対して予測実施
    label, confidence = recognizer.predict(test_images[i])
    # 予測結果をコンソール出力
    print("Confidence: {:.2f}".format(confidence))
```

次に`similarity.py`をカメラを起動させて画像を取得し、画面に類似度を出力させるコードも作成してみました。

```camera.py
import numpy as np
import cv2
import os

if __name__ == '__main__':

    print(" * Loading pre-trained model ...")
    cascadePath = './haarcascade_frontalface_alt.xml'
    faceCascade = cv2.CascadeClassifier(cascadePath)
    recognizer = cv2.face.LBPHFaceRecognizer_create()
    # recognizerのloadは次回の記事で触れます
    recognizer.read('./sample_model.yml')

    print(' * Loading end')

    cap = cv2.VideoCapture(0)
 
    # フレームの取得
    ret,frame = cap.read()

    # 文字のフォント設定
    font = cv2.FONT_HERSHEY_PLAIN
    # 文字のサイズ設定
    font_size = 5

    while(True):
        ret, frame = cap.read()
        if ret == True:
            # Webカメラからの画像を白黒画像として読み込み
            image_pil = 299/1000 * frame[:, :, 0] + 587/1000 * frame[:, :, 1] + 114/1000 * frame[:, :, 2]
            image = np.array(image_pil, 'uint8')
            # Haar-like特徴分類器で顔を検知
            image1 = faceCascade.detectMultiScale(image)
            if len(image1) > 0 :
                x, y, w, h = image1[0][0], image1[0][1], image1[0][2], image1[0][3]
                # 200×200にリサイズ
                image2 = cv2.resize(image[y: y + h, x: x + w],(200, 200), interpolation=cv2.INTER_LINEAR)
                # 類似度を予測
                label, predict_Confidence = recognizer.predict(image2)
                # 類似度を画面に表示
                cv2.putText(frame,str(predict_Confidence),(50,700),font,font_size,(255,255,0),4)
                cv2.imshow('Show', frame)

            #1msecキー入力待ち
            cv2.waitKey(1)
        else:
            break
```

**次回の記事**
毎回`similarity.py`を実行していると時間がかかるので、次回は、`recognizer`を`Save`して、Flaskで`recognizer`を`Load`し、類似度をブラウザで出力するAPIを作成してみたいと思います！

参考記事

[OpenCVを使って誰の顔なのかを推定する（Eigenface, Fisherface, LBPH）](https://qiita.com/hitomatagi/items/8f2f37646179aca68649)

[OpenCVで乃木坂46秋元真夏と銀シャリ鰻和弘の類似度を調べてみた](https://blog.aidemy.net/entry/2018/06/18/135601)

[OpenCV ビデオCapture](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_gui/py_video_display/py_video_display.html)