"""顔画像ファイルから`npKnown.npz`ファイルを作成する.
Summary:
`preset_face_images`ディレクトリを捜査して、`npKnown.npz`ファイルを作成します。
"""
import os
import shutil
import sys
from os.path import exists
from typing import List, Tuple
import numpy as np
import numpy.typing as npt
from face01lib.api import Dlib_api
from face01lib.Calc import Cal
from face01lib.combine import Comb as C
from face01lib.logger import Logger
from face01lib.utils import Utils
# グローバルオブジェクトの初期化
Dlib_api_obj = Dlib_api()
Utils_obj = Utils()
[ドキュメント]
class LoadPresetImage:
def __init__(
self,
log_level: str = "info"
) -> None:
self.log_level = log_level
# Setup logger
name: str = __name__
dir: str = os.path.dirname(__file__)
parent_dir, _ = os.path.split(dir)
self.logger = Logger(self.log_level).logger(name, parent_dir)
# Cal().cal_specify_date(self.logger) # 廃止
def _save_as_noKnown(self, known_face_encodings, known_face_names):
"""_save_as_noKnown エンコードされた顔のデータと名前のリストを、npKnown.npzファイルとして保存するメソッド.
Args:
known_face_encodings (_type_): エンコードされた顔のデータ
known_face_names (_type_): 名前のリスト
"""
if self.deep_learning_model == 0:
np.savez(
os.path.join(self.RootDir, 'npKnown'),
name=known_face_names,
dlib=known_face_encodings
)
elif self.deep_learning_model == 1:
np.savez(
os.path.join(self.RootDir, 'npKnown'),
name=known_face_names,
efficientnetv2_arcface=known_face_encodings
)
# フォルダーの作成
def _make_folder(self) -> None:
# `noFace`フォルダが存在しない場合は作成する
if not exists(os.path.join(self.RootDir, 'noFace')):
os.mkdir(os.path.join(self.RootDir, 'noFace'))
self.logger.info("Create 'noFace' folder")
# `multipleFaces`フォルダが存在しない場合は作成する
if not exists(os.path.join(self.RootDir, 'multipleFaces')):
os.mkdir(os.path.join(self.RootDir, 'multipleFaces'))
self.logger.info("Create 'multipleFaces' folder")
def _get_known_face_data(self) -> Tuple[List[np.ndarray], List[str]]:
"""既知の顔データをnpKnown.npzからロードするメソッド
Returns:
エンコードされた顔の情報と名前のリスト
"""
known_face_encodings_list: List[np.ndarray] = []
known_face_names_list: List[str] = []
# npKnown.npzファイルが存在する場合の処理
if exists(os.path.join(self.RootDir, "npKnown.npz")):
npKnown = np.load(
os.path.join(self.RootDir, 'npKnown.npz'),
allow_pickle=True
)
# npKnown.npzファイルのdeep_learning_modelとself.deep_learning_modelが一致するか確認
if 'dlib' in npKnown and self.deep_learning_model == 0:
known_face_encodings_ndarray = npKnown['dlib']
known_face_names_ndarray = npKnown['name']
elif 'efficientnetv2_arcface' in npKnown and self.deep_learning_model == 1:
known_face_encodings_ndarray = npKnown['efficientnetv2_arcface']
known_face_names_ndarray = npKnown['name']
else:
self.logger.error("npKnown.npzとdeep_learning_modelが一致しません。")
self.logger.error("config.iniのdeep_learning_modelの値を変更し、再度起動して下さい。")
self.logger.error("終了します。")
# npKnown.npzファイルを削除する
os.remove(os.path.join(self.RootDir, 'npKnown.npz'))
sys.exit(0)
# ndarrayをlistに変換
known_face_encodings_list = [i for i in known_face_encodings_ndarray]
known_face_names_list = known_face_names_ndarray.tolist()
# npKnown.npzファイルが存在しない場合の処理
elif not exists("npKnown.npz"):
face_images_list = self._get_face_images(self.preset_face_imagesDir)
known_face_encodings_list, known_face_names_list = \
self._encode_face_images(face_images_list)
return known_face_encodings_list, known_face_names_list
def _update_known_face_data(self) -> Tuple[List[np.ndarray], List[str]]:
"""
npKnown.npzファイルに存在するデータ名リストと顔画像ファイル名リストが異なる場合に
顔画像ファイルを追加してnpKnown.npzファイルを更新するメソッド
"""
known_face_encodings: List[np.ndarray] = []
known_face_names: List[str] = []
known_face_encodings, known_face_names = self._get_known_face_data()
face_image_filename_list = self._get_face_images(self.preset_face_imagesDir)
# known_face_namesをアルファベット順にソート
known_face_names.sort()
# face_image_filename_listをアルファベット順にソート
face_image_filename_list.sort()
# known_face_namesとface_image_filename_listを比較して、
# 内容が異なる場合には、npKnown.npzファイルを更新する
if known_face_names != face_image_filename_list:
# npKnown.npzファイルを削除する
os.remove(os.path.join(self.RootDir, 'npKnown.npz'))
# 顔画像ファイルから顔画像のエンコードリストと顔画像名リストを取得する
face_encoding_list, face_file_name_list = \
self._encode_face_images(face_image_filename_list)
# npKnown.npzファイルとして保存する
self._save_as_noKnown(face_encoding_list, face_file_name_list)
else:
# npKnown.npzファイルに変更はないので、npKnown.npzファイルをロードする
face_encoding_list, face_file_name_list = self._get_known_face_data()
return face_encoding_list, face_file_name_list
# ディレクトリから顔画像ファイルを取得するメソッド
def _get_face_images(self, dir: str) -> List[str]:
# 拡張子がpng, jpeg, jpg, webpの場合のみface_images_listに追加
face_image_filename_list = [] # 顔画像のリストを初期化
# preset_face_imagesDir内のファイルを一つずつ確認
for filename in os.listdir(dir):
# ファイルの拡張子を取得
extension = os.path.splitext(filename)[1].lower() # 拡張子を小文字に変換
# 拡張子がpng, jpeg, jpg, webpのいずれかの場合、リストに追加
if extension in ['.png', '.jpeg', '.jpg', '.webp']:
face_image_filename_list = C.comb(
face_image_filename_list, [filename])
# face_image_filename_list.append(filename)
return face_image_filename_list
def _encode_face_images(self, face_images_list: List[str]) -> Tuple[List[np.ndarray], List[str]]:
"""_encode_face_images 顔画像エンコードリストと顔画像名リストを返すメソッド
Args:
face_images_list (List[str]): ファイルリスト
Returns:
Tuple[List[np.ndarray], List[str]]: face_encoding_list, face_image_filename_list
"""
# フォルダー存在の確認と作成
self._make_folder()
# 変数の初期化
face_encoding_list: List[np.ndarray] = []
face_file_name_list: List[str] = []
face_location_list: List[Tuple[int, int, int, int]] = []
for face_image in face_images_list:
# 顔画像ファイルを読み込む
face_image_ndarray: npt.NDArray[np.uint8] = \
Dlib_api_obj.load_image_file(
os.path.join(self.preset_face_imagesDir, face_image)
)
# 顔画像ファイルから顔の位置を検出
face_location_list = Dlib_api_obj.face_locations(
face_image_ndarray,
self.upsampling,
self.mode
)
# 顔検出できなかった場合hogからcnnへチェンジして再度顔検出する
if len(face_location_list) == 0:
if self.mode == 'hog':
self.logger.info(
"Face could not be detected. Temporarily switch to 'cnn' mode")
face_location_list = Dlib_api_obj.face_locations(
face_image_ndarray, self.upsampling, 'cnn')
# modeをhogに戻す
self.mode = 'hog'
self.logger.info('Back to HOG mode')
# cnnでも顔検出できない場合はnoFaceフォルダへファイルを移動して次のファイルへ。
# ファイルをnoFaceフォルダへ移動
if len(face_location_list) == 0:
try:
shutil.move(face_image, os.path.join(
self.RootDir, 'noFace')) # 既にファイルが存在する場合は上書きされる
except:
pass
self.logger.info(
f"No face detected in registered face image {face_image}(CNN mode). Move it to the 'noFace' folder")
continue
# ファイルをmultipleFacesフォルダへ移動
elif len(face_location_list) > 1:
self.logger.info(
f"Multiple faces detected in registered face image {face_image}(CNN mode). Move it to the 'multipleFaces' folder")
shutil.move(face_image, os.path.join(
self.RootDir, 'multipleFaces'))
continue
# # 複数の顔が検出された場合はmultipleFacesフォルダへファイルを移動する
# elif len(face_location_list) > 1:
# self.logger.info(f"Multiple faces detected in registered face image {face_image}. Move it to the 'multipleFaces' folder")
# shutil.move(face_image, os.path.join(self.RootDir, 'multipleFaces')) # 既にファイルが存在する場合は上書きされる
# continue
# 複数の顔が検出された場合の処理
# 上記のコード(エスケープされている箇所)だと異なるドライブに対しての操作ができない。(無効なクロスデバイスリンク)この場合はshutil.moveではなく、shutil.copyとos.removeを組み合わせて処理を行う。
elif len(face_location_list) > 1:
self.logger.info(f"Multiple faces detected in registered face image {face_image}. Move it to the 'multipleFaces' folder")
# 移動先のパスを設定
destination_path = os.path.join(self.RootDir, 'multipleFaces', os.path.basename(face_image))
try:
# ファイルをコピーし、その後元のファイルを削除することで移動と同じ処理を実現
shutil.copy(face_image, destination_path) # コピーを実行
os.remove(face_image) # 元ファイルを削除して移動を完了
except FileNotFoundError:
self.logger.error(f"{face_image}が存在しません。ファイル名を確認してください。")
except OSError as e:
self.logger.error(f"ファイルの移動中にエラーが発生しました: {e}")
continue
elif len(face_location_list) == 1:
# ログ出力
self.logger.info(f"Encoding {face_image}")
# Dlib使用時の顔画像のエンコーディング処理
if self.deep_learning_model == 0:
face_encode_data: List[np.ndarray] = Dlib_api_obj.face_encodings(
deep_learning_model=0,
resized_frame=face_image_ndarray,
face_location_list=face_location_list,
num_jitters=self.jitters,
model=self.model
)
# JAPANESE_FACE_V1.onnx使用時の顔画像のエンコーディング処理
elif self.deep_learning_model == 1:
face_encode_data: List[np.ndarray] = Dlib_api_obj.face_encodings(
deep_learning_model=1,
resized_frame=face_image_ndarray,
face_location_list=face_location_list,
num_jitters=self.jitters,
model=self.model
)
# self.deep_learning_modelの値が不正な場合
else:
face_encode_data = []
# ログ出力
self.logger.error("deep_learning_modelの値が不正です。")
self.logger.error("Now deep_learning_model: {self.deep_learning_model}}")
sys.exit(1)
face_encoding_list = C.comb(face_encoding_list, face_encode_data)
face_file_name_list = C.comb(face_file_name_list, [face_image])
else:
self.logger.error("face_location_listの値が不正です。")
self.logger.error("Now face_location_list: {face_location_list}")
sys.exit(1)
return face_encoding_list, face_file_name_list
[ドキュメント]
def load_preset_image(
self,
deep_learning_model: int,
RootDir: str,
preset_face_imagesDir: str,
upsampling: int = 0,
jitters: int = 100,
mode: str = 'hog',
model: str = 'small'
) -> Tuple[List[np.ndarray], List[str]]:
"""load_preset_image npKnown.npzを作成する.
Args:
deep_learning_model (int): 0または1. 0: dlib, 1: JAPANESE FACE V1
RootDir (str): npKnown.npzを作成するディレクトリ
preset_face_imagesDir (str): 顔画像が格納されているディレクトリ
upsampling (int, optional): upsampling値. Defaults to 0.
jitters (int, optional): jitter値. Defaults to 100.
mode (str, optional): HOG OR CNN. Defaults to 'hog'.
model (str, optional): Defaults to 'small'.
Returns:
Tuple[List[np.ndarray], List[str]]: npKnown.npzを作成する
Tuple[List, List]: known_face_encodings_list, known_face_names_list
- known_face_encodings_list
- List of encoded many face images as ndarray
- known_face_names_list
- List of name which encoded as ndarray
Example:
>>> known_face_encodings, known_face_names = LoadPresetImage().load_preset_image(
self,
self.conf_dict["RootDir"],
self.conf_dict["preset_face_imagesDir"]
)
"""
self.deep_learning_model = deep_learning_model
self.RootDir = RootDir
self.preset_face_imagesDir = preset_face_imagesDir
self.upsampling = upsampling
self.jitters = jitters
self.mode = mode
self.model = model
self.logger.info("Loading npKnown.npz")
self._make_folder()
# 初期化
face_encoding_list: List[np.ndarray] = []
face_image_filename_list: List[str] = []
if exists(os.path.join(self.RootDir, "npKnown.npz")):
face_encoding_list, face_image_filename_list = self._update_known_face_data()
else:
file_list = self._get_face_images(self.preset_face_imagesDir)
face_encoding_list, face_image_filename_list = self._encode_face_images(
file_list)
self._save_as_noKnown(face_encoding_list, face_image_filename_list)
return face_encoding_list, face_image_filename_list