import PIL from PIL import ImageOps import xml.etree.ElementTree as ET import shutil import os import numpy as np from glob import glob import tensorflow as tf from tensorflow.keras import backend as K import cv2 import matplotlib.pyplot as plt import importlib from face_recognition import config from face_recognition.aligner import Aligner class face_recognition: def __init__(self,model_path,thres=None,min_aligner_confidence=None): config_file_path='.'.join(model_path.split("/"))+".config" # print(config_file_path) self.model_config= importlib.import_module(config_file_path) # print(self.model_config) self.thres=thres if thres is not None else self.model_config.d_thres self.aligner=Aligner(min_aligner_confidence) if min_aligner_confidence is not None else Aligner(config.min_aligner_confidence) self.feature_extractor=tf.keras.models.load_model(model_path+"/model.h5",compile=False) # def euclidean_distance(self,vectors): # squared_sum=np.sum(np.square(vectors[0]-vectors[1]),axis=-1,keepdims=True) # return np.sqrt(np.maximum(squared_sum,1e-7)) def new_distance(self,vectors): ''' this distance metric is -1 to 1 and it gives values close to 1 when matching and values close to -1 when not matching ''' return (vectors[0]*vectors[1]).sum(-1) def calculate_distance(self,crop_img,db_faces_features,mode='avg'): """ mode= 'avg' or 'best' """ if mode not in ['avg','best']: raise ValueError(f"Unknown mode:{mode} \nMode should be one of these:{['avg','best']}") crop_img_features=self.feature_extractor.predict(crop_img[None,:,:,:],verbose=0) all_distances=[] # distance of this particular crop with all faces in database for face_idx in range(len(self.faces)): if mode=='avg': db_face_features=db_faces_features[face_idx].mean(axis=0,keepdims=True) # avg method new_crop_img_features=crop_img_features.copy() else: db_face_features=db_faces_features[face_idx] # best method new_crop_img_features=np.tile(crop_img_features,[db_face_features.shape[0],1]) try: assert(db_face_features.shape==new_crop_img_features.shape) except: raise AssertionError(f"db_face_features shape{db_face_features.shape} does not match crop_img_features shape{new_crop_img_features.shape}") distance=np.max(self.new_distance([db_face_features,new_crop_img_features]),axis=0) if distance>self.thres: all_distances.append(distance) # obj distance wrt to all faces in database else: all_distances.append(self.model_config.large_distance) # not the person guaranteed return all_distances def repeat_allowed_face_recognition(self,distance_dict): faceidx_to_obj_dict=dict() for obj in distance_dict.keys(): distances=np.array(distance_dict[obj]) min_distance,min_distance_idx = distances.max(),distances.argmax() if min_distance>self.thres: obj.find('name').text = self.faces[min_distance_idx] distance_tag=ET.Element("distance") distance_tag.text="{:.2f}".format(min_distance) obj.append(distance_tag) return faceidx_to_obj_dict def no_repeat_allowed_face_recognition(self,distance_dict): def assign_face_label(obj): # find min and argmin min_distance,min_distance_idx = distance_dict[obj].max(),distance_dict[obj].argmax() # base condition if min_distance