diff --git a/server/cluster.py b/server/cluster.py index d028d32..0423672 100644 --- a/server/cluster.py +++ b/server/cluster.py @@ -9,6 +9,10 @@ import numpy as np from deepface import DeepFace from retinaface import RetinaFace + +sqlite3.register_adapter(np.array, lambda arr: arr.tobytes()) +sqlite3.register_converter("array", np.frombuffer) + class NpEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, np.integer): @@ -96,6 +100,29 @@ def update_face_count(conn, photoId, faces): conn.commit() return None +def findCosineDistance(source_representation, test_representation): + if type(source_representation) == list: + source_representation = np.array(source_representation) + if type(test_representation) == list: + test_representation = np.array(test_representation) + a = np.matmul(np.transpose(source_representation), test_representation) + b = np.sum(np.multiply(source_representation, source_representation)) + c = np.sum(np.multiply(test_representation, test_representation)) + return 1 - (a / (np.sqrt(b) * np.sqrt(c))) + +def findEuclideanDistance(source_representation, test_representation): + if type(source_representation) == list: + source_representation = np.array(source_representation) + if type(test_representation) == list: + test_representation = np.array(test_representation) + euclidean_distance = source_representation - test_representation + euclidean_distance = np.sum(np.multiply(euclidean_distance, euclidean_distance)) + euclidean_distance = np.sqrt(euclidean_distance) + return euclidean_distance + +def l2_normalize(x): + return x / np.sqrt(np.sum(np.multiply(x, x))) + base = '/pictures/' conn = create_connection('../db/photos.db') faces = {} @@ -106,16 +133,55 @@ with conn: res = cur.execute(''' SELECT faces.id,facedescriptors.descriptors FROM faces - LEFT JOIN facedescriptors ON (faces.descriptorId=facedescriptors.id) + JOIN facedescriptors ON (faces.descriptorId=facedescriptors.id) WHERE faces.identityId IS null ''') for row in res.fetchall(): id, descriptors = row - if faces[id] is None: - face = {} - faces[id] = face - else: + if id in faces: face = faces[id] - face['descriptors'] = descriptors + else: + face = { + 'id': id, + 'scanned': False + } + faces[id] = face + face['descriptors'] = np.frombuffer(descriptors) + + for key1 in faces: + face1 = faces[key1] + if face1['scanned'] == True: + continue + face1['scanned'] = True + for key2 in faces: + if key1 == key2: + continue + face2 = faces[key2] + face = { + 'between': (face1['id'], face2['id']) + } + + face['distanceCosine'] = findCosineDistance( + face1['descriptors'], + face2['descriptors'] + ) + face['distanceEuclidian'] = findEuclideanDistance( + face1['descriptors'], + face2['descriptors'] + ) + face['distanceEuclidianL2'] = findEuclideanDistance( + l2_normalize(face1['descriptors']), + l2_normalize(face2['descriptors']) + ) + face['scoring'] = 0 + if face['distanceCosine'] >= 0.68: + face['scoring'] += 1 + if face['distanceEuclidian'] >= 4.15: + face['scoring'] += 1 + if face['distanceEuclidianL2'] >= 1.13: + face['scoring'] += 1 + + if face['scoring'] == 3: # Same face! + print(face) # update_face_count(conn, photoId, len(faces)) diff --git a/server/detect.py b/server/detect.py index 406c3d5..e733b7f 100644 --- a/server/detect.py +++ b/server/detect.py @@ -62,11 +62,10 @@ def extract_faces(img, threshold=0.75, model = None, allow_upscaling = True): # Re-implementation of 'extract_faces' with the addition of keeping a # copy of the face image for caching on disk if type(faces) == dict: - print(f'Found {len(faces)} faces') - i=1 + k=1 for key in faces: - print(f'Processing face {i}/{len(faces)}') - i+=1 + print(f'Processing face {k}/{len(faces)}') + k+=1 identity = faces[key] facial_area = identity["facial_area"] landmarks = identity["landmarks"] @@ -112,7 +111,7 @@ def extract_faces(img, threshold=0.75, model = None, allow_upscaling = True): # Eye order is reversed as the routine does them backwards image = Image.fromarray(facial_img) image = alignment_procedure(image, right_eye, left_eye) - #image = image.resize(size = input_shape, resample = Image.LANCZOS) + image = image.resize(size = input_shape, resample = Image.LANCZOS) resized = np.asarray(image) identity['vector'] = DeepFace.represent( @@ -122,7 +121,6 @@ def extract_faces(img, threshold=0.75, model = None, allow_upscaling = True): detector_backend = 'retinaface', enforce_detection = False) - print(img.shape) identity["face"] = { 'top': facial_area[1] / img.shape[0], 'left': facial_area[0] / img.shape[1], @@ -159,8 +157,8 @@ def create_face(conn, face): :return: face id """ sql = ''' - INSERT INTO faces(photoId,scanVersion,faceConfidence,top,left,bottom,right) - VALUES(?,?,?,?,?,?,?) + INSERT INTO faces(photoId,scanVersion,faceConfidence,top,left,bottom,right,descriptorId) + VALUES(?,?,?,?,?,?,?,?) ''' cur = conn.cursor() cur.execute(sql, ( @@ -170,7 +168,8 @@ def create_face(conn, face): face['top'], face['left'], face['bottom'], - face['right'] + face['right'], + face['descriptorId'] )) conn.commit() return cur.lastrowid @@ -179,8 +178,7 @@ def create_face_descriptor(conn, face): """ Create a new face in the faces table :param conn: - :param faceId: - :param descriptor: + :param face: :return: descriptor id """ sql = ''' @@ -232,10 +230,12 @@ with conn: if faces is None: update_face_count(conn, photoId, 0) continue - print(f'Handling {len(faces)} faces') + j=1 for key in faces: face = faces[key] image = face['image'] + print(f'Writing face {j}/{len(faces)}') + j+=1 #face['analysis'] = DeepFace.analyze(img_path = img, actions = ['age', 'gender', 'race', 'emotion'], enforce_detection = False) #face['analysis'] = DeepFace.analyze(img, actions = ['emotion']) @@ -279,15 +279,3 @@ with conn: #print(df.head()) update_face_count(conn, photoId, len(faces)) - exit(0) - - #img2_path = sys.argv[2] -#print("image 1: ", img1_path); -#print("image 2: ", img2_path); -#result = DeepFace.verify(img1_path = img1_path, img2_path = img2_path, #model_name = models[1]) -#print("result: ", result) - -#face recognition -#df = DeepFace.find(img_path = img1_path, db_path = "./db/deepface", model_name = models[1]) - -#print("df: ", df)