Spaces:
Build error
Build error
import os | |
import cv2 | |
import threading | |
import numpy as np | |
from tqdm import tqdm | |
import concurrent.futures | |
import default_paths as dp | |
from dataclasses import dataclass | |
from utils.arcface import ArcFace | |
from utils.gender_age import GenderAge | |
from utils.retinaface import RetinaFace | |
cache = {} | |
class Face: | |
bbox: np.ndarray | |
kps: np.ndarray | |
det_score: float | |
embedding: np.ndarray | |
gender: int | |
age: int | |
def __getitem__(self, key): | |
return getattr(self, key) | |
def __setitem__(self, key, value): | |
if hasattr(self, key): | |
setattr(self, key, value) | |
else: | |
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{key}'") | |
single_face_detect_conditions = [ | |
"best detection", | |
"left most", | |
"right most", | |
"top most", | |
"bottom most", | |
"middle", | |
"biggest", | |
"smallest", | |
] | |
multi_face_detect_conditions = [ | |
"all face", | |
"specific face", | |
"age less than", | |
"age greater than", | |
"all male", | |
"all female" | |
] | |
face_detect_conditions = multi_face_detect_conditions + single_face_detect_conditions | |
def get_single_face(faces, method="best detection"): | |
total_faces = len(faces) | |
if total_faces == 0: | |
return None | |
if total_faces == 1: | |
return faces[0] | |
if method == "best detection": | |
return sorted(faces, key=lambda face: face["det_score"])[-1] | |
elif method == "left most": | |
return sorted(faces, key=lambda face: face["bbox"][0])[0] | |
elif method == "right most": | |
return sorted(faces, key=lambda face: face["bbox"][0])[-1] | |
elif method == "top most": | |
return sorted(faces, key=lambda face: face["bbox"][1])[0] | |
elif method == "bottom most": | |
return sorted(faces, key=lambda face: face["bbox"][1])[-1] | |
elif method == "middle": | |
return sorted(faces, key=lambda face: ( | |
(face["bbox"][0] + face["bbox"][2]) / 2 - 0.5) ** 2 + | |
((face["bbox"][1] + face["bbox"][3]) / 2 - 0.5) ** 2)[len(faces) // 2] | |
elif method == "biggest": | |
return sorted(faces, key=lambda face: (face["bbox"][2] - face["bbox"][0]) * (face["bbox"][3] - face["bbox"][1]))[-1] | |
elif method == "smallest": | |
return sorted(faces, key=lambda face: (face["bbox"][2] - face["bbox"][0]) * (face["bbox"][3] - face["bbox"][1]))[0] | |
def filter_face_by_age(faces, age, method="age less than"): | |
if method == "age less than": | |
return [face for face in faces if face["age"] < age] | |
elif method == "age greater than": | |
return [face for face in faces if face["age"] > age] | |
elif method == "age equals to": | |
return [face for face in faces if face["age"] == age] | |
def cosine_distance(a, b): | |
a /= np.linalg.norm(a) | |
b /= np.linalg.norm(b) | |
return 1 - np.dot(a, b) | |
def is_similar_face(face1, face2, threshold=0.6): | |
distance = cosine_distance(face1["embedding"], face2["embedding"]) | |
return distance < threshold | |
class AnalyseFace: | |
def __init__(self, provider=["CPUExecutionProvider"], session_options=None): | |
self.detector = RetinaFace(model_file=dp.RETINAFACE_PATH, provider=provider, session_options=session_options) | |
self.recognizer = ArcFace(model_file=dp.ARCFACE_PATH, provider=provider, session_options=session_options) | |
self.gender_age = GenderAge(model_file=dp.GENDERAGE_PATH, provider=provider, session_options=session_options) | |
self.detect_condition = "best detection" | |
self.detection_size = (640, 640) | |
self.detection_threshold = 0.5 | |
def analyser(self, img, skip_task=[]): | |
bboxes, kpss = self.detector.detect(img, input_size=self.detection_size, det_thresh=self.detection_threshold) | |
faces = [] | |
for i in range(bboxes.shape[0]): | |
feat, gender, age = None, None, None | |
bbox = bboxes[i, 0:4] | |
det_score = bboxes[i, 4] | |
kps = None | |
if kpss is not None: | |
kps = kpss[i] | |
if 'embedding' not in skip_task: | |
feat = self.recognizer.get(img, kpss[i]) | |
if 'gender_age' not in skip_task: | |
gender, age = self.gender_age.predict(img, kpss[i]) | |
face = Face(bbox=bbox, kps=kps, det_score=det_score, embedding=feat, gender=gender, age=age) | |
faces.append(face) | |
return faces | |
def get_faces(self, image, scale=1., skip_task=[]): | |
if isinstance(image, str): | |
image = cv2.imread(image) | |
faces = self.analyser(image, skip_task=skip_task) | |
if scale != 1: # landmark-scale | |
for i, face in enumerate(faces): | |
landmark = face['kps'] | |
center = np.mean(landmark, axis=0) | |
landmark = center + (landmark - center) * scale | |
faces[i]['kps'] = landmark | |
return faces | |
def get_face(self, image, scale=1., skip_task=[]): | |
faces = self.get_faces(image, scale=scale, skip_task=skip_task) | |
return get_single_face(faces, method=self.detect_condition) | |
def get_averaged_face(self, images, method="mean"): | |
if not isinstance(images, list): | |
images = [images] | |
face = self.get_face(images[0], scale=1., skip_task=[]) | |
if len(images) > 1: | |
embeddings = [face['embedding']] | |
for image in images[1:]: | |
face = self.get_face(image, scale=1., skip_task=[]) | |
embeddings.append(face['embedding']) | |
if method == "mean": | |
avg_embedding = np.mean(embeddings, axis=0) | |
elif method == "median": | |
avg_embedding = np.median(embeddings, axis=0) | |
face['embedding'] = avg_embedding | |
return face | |