PyTorchのfacenetモデルで同一人物を判定→年齢・性別判定

#カメラに映っている限り同一人物でも判別し続けてしまう問題
UTKFaceデータセットを使って
・性別判断
・年代を推測（１０代、２０代、３０代...）
する学習モデルを作成し、カメラに映った人物を判定するプログラムを作りましたが、同一人物でも何回も同じ判別結果を出してしまう状態では実用的とは言えないなという事で。
少し工夫しようとPyTorchのfacenetモデルを利用し実装してみました。

###ライブラリやその機能を使うための準備
```csharp
from tensorflow.python.keras.models import load_model

model = load_model('./Gender_judgment_fullmodel.h5', compile=False)
model_men = load_model('./men_Age_judgment_model.h5', compile=False)
model_women = load_model('./women_Age_judgment_model.h5', compile=False)
```
ディープラーニング学習済モデルの読み出しです。
Gender_judgment_fullmodel.h5　は性別を判定
men_Age_judgment_model.h5　は男性の年齢を判定
women_Age_judgment_model.h5　は女性の年齢を判定します。

```csharp
import cv2
import numpy as np

face_cascade_path = './opencv/haarcascade_frontalface_default.xml'
eye_cascade_path = './opencv/haarcascade_eye.xml'
face_cascade = cv2.CascadeClassifier(face_cascade_path)
eye_cascade = cv2.CascadeClassifier(eye_cascade_path)
```
OpenCVの検出器を保存した場所からデータを読み出しています。
haarcascade_frontalface_default.xml　は顔を検出
haarcascade_eye.xml　は瞳を検出してくれます。

```csharp
from facenet_pytorch import MTCNN, InceptionResnetV1

mtcnn = MTCNN(image_size=160, margin=10)
resnet = InceptionResnetV1(pretrained='vggface2').eval()
```
同一人物を判別するためのモデルを読み出します。


###同一人物を判定するための関数を定義
```csharp
def cos_similarity(p1, p2):
    return np.dot(p1, p2) / (np.linalg.norm(p1) * np.linalg.norm(p2))

img_st = []
def sameface(img1):
    if len(img_st) == 0:
        img_st.append(img1)
        return 0
    else:
        samecount = 0
        img_cropped1 = mtcnn(img1)
        img_embedding1 = resnet(img_cropped1.unsqueeze(0))
        p1 = img_embedding1.squeeze().to('cpu').detach().numpy().copy()

        for img2 in img_st:
            img_cropped2 = mtcnn(img2)
            img_embedding2 = resnet(img_cropped2.unsqueeze(0))
            p2 = img_embedding2.squeeze().to('cpu').detach().numpy().copy()

            img1vs = cos_similarity(p1, p2)

            if img1vs > 0.6:
                samecount = 1
        if samecount == 0:
            img_st.append(img1)
        return samecount
```
関数samefaceに入力された画像「img1」とそれまでに記録された画像から１枚ずつ抜き出した「img2」の
２枚の画像データを比較し、同一人物か判定するものです。
img1vs = cos_similarity(p1, p2)　この値が高いほど同一人物である可能性が高くなります。

###同一人物であれば年齢・性別判定は実行しない
```csharp
n = 0
cap_interval = 0
cap = cv2.VideoCapture(0)
while True:
    if cap_interval == 100:
        cap_interval = 0
        ret, img = cap.read()
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        faces = face_cascade.detectMultiScale(gray, scaleFactor=1.3, minNeighbors=5)
        for x, y, w, h in faces:
            facecut = img[y - 100: y + h + 100, x - 50: x + w + 50]
            face_gray = gray[y: y + h, x: x + w]
            eyes = eye_cascade.detectMultiScale(face_gray)
            if len(eyes) != 0:
                for (ex, ey, ew, eh) in eyes:
                    if sameface(facecut) == 0:
                        facecut2 = cv2.resize(facecut, (299, 299))
                        facecut2 = np.asarray(facecut2)
                        facecut2 = facecut2[None, ...]
                        preds = model.predict(facecut2)
                        facecut3 = facecut2.astype('float32') / 255
                        if np.argmax(preds) == 0:
                            sex = "men"
                            preds2 = model_men.predict(facecut3)
                        else:
                            sex = "women"
                            preds2 = model_women.predict(facecut3)
                        print(preds2)

                        if preds2 < 0.8:
                            age = "10代"
                        elif preds2 < 1.8:
                            age = "20代"
                        elif preds2 < 2.8:
                            age = "30代"
                        elif preds2 < 3.8:
                            age = "40代"
                        elif preds2 < 4.8:
                            age = "50代"
                        elif preds2 < 5.8:
                            age = "70代"
                        elif preds2 < 6.8:
                            age = "80代"
                        elif preds2 < 7.8:
                            age = "90代"
                        cv2.imwrite('{}_{}.{}'.format('data/temp/' + sex + age , n, "jpg"), facecut)
                        n += 1
                        cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
        cv2.imshow('video image', img)
    cap_interval += 1



    key = cv2.waitKey(10)
    if key == 27:  # ESCキーで終了
        break

cap.release()
cv2.destroyAllWindows()
```
実行すると落ちるようになったのでカメラのキャプチャ頻度を下げましたm(_ _)m
瞳を検出した時点で同一人物か判定し後処理をするようにしています。