顔認証アプリのプロトタイプ

# はじめに
顔認証による入退室管理のWebアプリケーションのプロトタイプを作ってみたので記事にしてみました。
# コード
```
import streamlit as st
import cv2
import face_recognition
import numpy as np
import os

def load_known_faces(directory="known_faces"):
    known_faces = []
    known_names = []

    for filename in os.listdir(directory):
        path = os.path.join(directory, filename)
        image = face_recognition.load_image_file(path)
        encoding = face_recognition.face_encodings(image)[0]
        known_faces.append(encoding)
        known_names.append(os.path.splitext(filename)[0])

    return known_faces, known_names

def main():
    st.title("入室")

    # カメラを開く
    video_capture = cv2.VideoCapture(0)

    # 事前に保存された顔の情報を読み込む
    known_faces, known_names = load_known_faces()

    # Streamlit上で表示する画像と名前を格納する変数
    st_image = st.empty()
    st_name = st.empty()

    # 入室のボタン
    entry_button = st.button("入室")

    while True:
        # カメラからフレームを取得
        ret, frame = video_capture.read()

        # 顔認識の処理
        face_locations = face_recognition.face_locations(frame)
        face_encodings = face_recognition.face_encodings(frame, face_locations)

        # 画像に顔の枠を描画
        for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
            cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2)

            # 保存された顔との照合を行う
            matches = face_recognition.compare_faces(known_faces, face_encoding)
            name = "Unknown"

            # 照合が成功した場合、名前を取得
            face_distances = face_recognition.face_distance(known_faces, face_encoding)
            best_match_index = np.argmin(face_distances)

            if matches[best_match_index]:
                # 近似度が一定以下の場合、"Unknown"を表示
                if face_distances[best_match_index] > 0.4:
                    name = "Unknown"
                    st_name.text(name)
                else:
                    name = known_names[best_match_index]

                    # 照合が成功したらボタンを表示
                    st_name.text(name)
                    entry_clicked = entry_button

                    if entry_clicked:
                        st.text("入室しました")
        # Streamlit上で画像を表示
        st_image.image(frame, channels="BGR", width=640)


if __name__ == "__main__":
    main()

```
# 説明
現状の処理はかなり簡単なものとなっており、
```
def load_known_faces(directory="known_faces"):
    known_faces = []
    known_names = []

    for filename in os.listdir(directory):
        path = os.path.join(directory, filename)
        image = face_recognition.load_image_file(path)
        encoding = face_recognition.face_encodings(image)[0]
        known_faces.append(encoding)
        known_names.append(os.path.splitext(filename)[0])

    return known_faces, known_names
```
で同フォルダ内にあるknown_facesから人物画像を読み込み、それぞれの人物の顔の特徴量を抽出します。

その後、
```
    while True:
        # カメラからフレームを取得
        ret, frame = video_capture.read()

        # 顔認識の処理
        face_locations = face_recognition.face_locations(frame)
        face_encodings = face_recognition.face_encodings(frame, face_locations)

        # 画像に顔の枠を描画
        for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
            cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2)

            # 保存された顔との照合を行う
            matches = face_recognition.compare_faces(known_faces, face_encoding)
            name = "Unknown"

            # 照合が成功した場合、名前を取得
            face_distances = face_recognition.face_distance(known_faces, face_encoding)
            best_match_index = np.argmin(face_distances)

            if matches[best_match_index]:
                # 近似度が一定以下の場合、"Unknown"を表示
                if face_distances[best_match_index] > 0.4:
                    name = "Unknown"
                    st_name.text(name)
                else:
                    name = known_names[best_match_index]

                    # 照合が成功したらボタンを表示
                    st_name.text(name)
                    entry_clicked = entry_button

                    if entry_clicked:
                        st.text("入室しました")
        # Streamlit上で画像を表示
        st_image.image(frame, channels="BGR", width=640)

```
で、カメラから一枚ずつフレームを抽出し、face_recognitionでフレームから人物の顔を認識します。

その後、認識した顔とknown_faceに登録されている顔画像の照合を行い、一定以上の近似値であった場合は該当者の名前を表示、近似値が一定以下であった場合にはUnknownを表示する形になっています。


# 今後の目標
今後の目標としては、データベースを接続し、メンバーの入退室状況を管理できるようにしたいです。