機械が言うと角が立たない3

# 背景
[前回](https://qiita.com/goichiya/items/2f268c23f2b120916d81)からの続きです
ローカル環境でしゃべる所まで完成

# 何をする
[これも](https://qiita.com/goichiya/items/2f268c23f2b120916d81#%E4%BD%95%E3%82%92%E3%81%99%E3%82%8B)前回からの続きです

# 各処理の内容を作ってゆく

処理を以下の順番に実行する

1. takepic  
    カメラを使って写真を撮る 
1. extract_face  
    写真の中から顔を抜き出す
1. judge_mask  
    マスクをしているか否か判定
1. speak_judge  
    判定内容に合わせてしゃべる内容を決定
1. speak_out  
    しゃべる

## 環境など
- SurfacePro3
    - Lobeを実行できる手持ちのwin10環境。HWの条件がそろわないとLobeはインストールすらできなかった  
    - pythonのバージョンは3.7。環境には3.8もあるものの、tfliteが対応していなかった。Lobeが出力したサンプルは3.6を前提にしているみたいだが、用意するのが面倒だった、、
- Azureのアカウント
    - FaceDetection、顔検証の機能を使用。1分当たり20件、月10,000件までは無料という設定。月30日とした場合、1日当たり300回程度までは無料なので今回の件を試してみるには十分。

## takepic
処理内容はシンプル。が、マイコンボードに移して稼働確認する時に何か起こりそう

```python:takepic.py
import cv2
import os
import glob
import datetime

def take_pic():
    dt_now = datetime.datetime.now()
    file_name = dt_now.strftime('%Y%m%d%M%S%f')
    # フォルダ初期化
    file_list = glob.glob('./image_raw/*')
    for file in file_list:
        os.remove(file)
    # front
    video_capture = cv2.VideoCapture(1)
    # back(フロントカメラとバックカメラで繋ぎ先が異なる。今回はフロントカメラでテスト)
    # video_capture = cv2.VideoCapture(0)
    ret, img = video_capture.read()
    if ret == True:
        cv2.imwrite('./image_raw/raw_' + file_name + '.jpg', img)
    return True
```

## extract_face
azureからFaceAPIの設定を[クイックスタート](https://docs.microsoft.com/ja-jp/azure/cognitive-services/face/quickstarts/client-libraries?tabs=visual-studio&pivots=programming-language-python)通りに設定
マイコンボードでAzure用のモジュールが動けばAPIを叩くだけの処理

```python:extract_face.py
import glob
import os
import cv2
from azure.cognitiveservices.vision.face import FaceClient
from msrest.authentication import CognitiveServicesCredentials

# AzureAPIに写真を送り、顔の部分を取得する
KEY = "PASTE_YOUR_FACE_SUBSCRIPTION_KEY_HERE"
ENDPOINT = "PASTE_YOUR_FACE_ENDPOINT_HERE"
face_client = FaceClient(ENDPOINT, CognitiveServicesCredentials(KEY))

def fetch_face_and_info():
    img_file = glob.glob('./image_raw/*.jpg')
    image_raw_path = img_file[0]
    single_image_name = os.path.basename(image_raw_path)
    img = open(image_raw_path, 'r+b')
    img_org = cv2.imread(image_raw_path)
    # モデルを選択できるようになっている。精度が出ないとき、調整の余地があるということか、、
    detected_faces = face_client.face.detect_with_stream(
        img, detection_model='detection_03', return_face_landmarks=True)
    if not detected_faces:
        raise Exception(
            'No face detected from image {}'.format(single_image_name))
    # img_faceフォルダのクリア
    file_list = glob.glob('./image_face/*')
    for file in file_list:
        os.remove(file)
    for face in detected_faces:
        cv2.imwrite('./image_face/' + face.face_id + '.jpg',
                    img_org[face.face_rectangle.top: face.face_rectangle.top + face.face_rectangle.height,
                            face.face_rectangle.left: face.face_rectangle.left + face.face_rectangle.width])
    # face_idの保存。同一人物判定に使用する
    with open('./face_info/face_info.txt', 'a') as f:
        for info in detected_faces:
            f.write(info.face_id)
            f.write('\n')
    return True
```

## judge_mask
Lobeが出力したサンプルスクリプトをcontrolから呼び出せるように改修。
写真を投入して判定結果を返す

```python:judge_mask.py
import tflite_example
import glob
def judge_mask_ctrl():
    file_path = glob.glob('./image_raw/*.jpg')
    if len(file_path) > 0:
        tflite_example.judge_mask(file_path[0])
        return True, tflite_example.judge_mask(file_path[0])
    else:
        return False
```

出力されたサンプルコードにコードを追加して呼び出せるように改修

```python:tflight_example.py
def judge_mask(file_path):
    model_dir = os.path.join(os.getcwd(), "..")

    if os.path.isfile(file_path):
        image = Image.open(file_path)
        model = TFLiteModel(model_dir)
        model.load()
        outputs = model.predict(image)
        print(f"Predicted: {outputs}")
        return outputs
    else:
        print(f"Couldn't find image file")
        return False
```

## speak_judge
マスク着用は判定されているので、同一人物の判定を行い出力内容を決定する

```python:speak_judge.py
from azure.cognitiveservices.vision.face import FaceClient
from msrest.authentication import CognitiveServicesCredentials

KEY = "PASTE_YOUR_FACE_SUBSCRIPTION_KEY_HERE"
ENDPOINT = "PASTE_YOUR_FACE_ENDPOINT_HERE"
face_client = FaceClient(ENDPOINT, CognitiveServicesCredentials(KEY))

def judge_similarity():
    # face_infoから最後の2件のidを取り出し、同じ人か確認
    face_id_list = []
    with open('./face_info/face_info.txt', 'r') as f:
        face_id_list = f.read().split('\n')
    face_id_list.pop()
    if len(face_id_list) < 2:
        print('not enough data to check similarity')
        return False
    else:
        face_list = []
        face_list.append(face_id_list[len(face_id_list)-2])
        similar_faces = face_client.face.find_similar(face_id=face_id_list[len(
            face_id_list)-1], face_ids=face_list)
        if similar_faces[0].confidence < 0.9:
            print('No similar faces found in', face_id_list[len(
                face_id_list)-1], face_id_list[len(face_id_list)-2], '.')
            return False
        return True

def speak_judge(process_control, judge_m):
    ret = judge_similarity()
    if ret is False:
        # 前回と同一人物ではないか十分なデータがない
        if judge_m == 'Mask':
            return('Congra')
        else:
            return('FirstAndUnMask')
    else:
        # 前回と同一人物
        if judge_m == 'Mask':
            return('Congra')
        else:
            return('SameAndUnMask')
```

## speak_out
決定に従って音声ファイルを再生する
処理自体は超シンプルだが、マイコンボードから音を出すときにドライバーとかモジュールで困難がありそう。
出力する内容は[音読さん](https://ondoku3.com/ja/)で作成し、wavファイルに変換しておく

```python:speak_out.py
import os
from playsound import playsound
def speak_out_message(str):
    playsound('./message_file/' + str + '.wav')
    return True
```

全体的にブロックを積み上げて、繋ぎのところちょっと作ったイメージ。
判定処理はすべてありものを使っている。改めて見てみるとすべてMicrosoftのプロダクト。
それが無料で使えるようになっているとは、、すごい世界になったものだ。

# 確認とこのあと
前回のcontrol.pyを実行するとsufaceのカメラがちらっと動いて判定結果に応じた音声ファイルを再生する。
エラー処理やログ出力がないとかは置いておいて、マイコンボードを調達して環境を再現してみる。
動くことは確認できているので、マイコンボード上で何があっても迷いなく対応できる( ´艸｀)