mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	Improve face recognition (#15670)
* Face recognition tuning * Support face alignment * Cleanup * Correctly download model
This commit is contained in:
		
							parent
							
								
									cc6a740a0f
								
							
						
					
					
						commit
						f58fc4c367
					
				@ -123,6 +123,18 @@ class Embeddings:
 | 
				
			|||||||
            device="GPU" if config.semantic_search.model_size == "large" else "CPU",
 | 
					            device="GPU" if config.semantic_search.model_size == "large" else "CPU",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.config.face_recognition.enabled:
 | 
				
			||||||
 | 
					            self.face_embedding = GenericONNXEmbedding(
 | 
				
			||||||
 | 
					                model_name="facedet",
 | 
				
			||||||
 | 
					                model_file="facedet.onnx",
 | 
				
			||||||
 | 
					                download_urls={
 | 
				
			||||||
 | 
					                    "facedet.onnx": "https://github.com/NickM-27/facenet-onnx/releases/download/v1.0/facedet.onnx",
 | 
				
			||||||
 | 
					                    "landmarkdet.yaml": "https://github.com/NickM-27/facenet-onnx/releases/download/v1.0/landmarkdet.yaml",
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                model_type=ModelTypeEnum.face,
 | 
				
			||||||
 | 
					                requestor=self.requestor,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.lpr_detection_model = None
 | 
					        self.lpr_detection_model = None
 | 
				
			||||||
        self.lpr_classification_model = None
 | 
					        self.lpr_classification_model = None
 | 
				
			||||||
        self.lpr_recognition_model = None
 | 
					        self.lpr_recognition_model = None
 | 
				
			||||||
 | 
				
			|||||||
@ -162,8 +162,12 @@ class FaceClassificationModel:
 | 
				
			|||||||
    def __init__(self, config: FaceRecognitionConfig, db: SqliteQueueDatabase):
 | 
					    def __init__(self, config: FaceRecognitionConfig, db: SqliteQueueDatabase):
 | 
				
			||||||
        self.config = config
 | 
					        self.config = config
 | 
				
			||||||
        self.db = db
 | 
					        self.db = db
 | 
				
			||||||
        self.recognizer = cv2.face.LBPHFaceRecognizer_create(
 | 
					        self.landmark_detector = cv2.face.createFacemarkLBF()
 | 
				
			||||||
            radius=4, threshold=(1 - config.threshold) * 1000
 | 
					        self.landmark_detector.loadModel("/config/model_cache/facenet/landmarkdet.yaml")
 | 
				
			||||||
 | 
					        self.recognizer: cv2.face.LBPHFaceRecognizer = (
 | 
				
			||||||
 | 
					            cv2.face.LBPHFaceRecognizer_create(
 | 
				
			||||||
 | 
					                radius=2, threshold=(1 - config.threshold) * 1000
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.label_map: dict[int, str] = {}
 | 
					        self.label_map: dict[int, str] = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -177,13 +181,70 @@ class FaceClassificationModel:
 | 
				
			|||||||
            face_folder = os.path.join(dir, name)
 | 
					            face_folder = os.path.join(dir, name)
 | 
				
			||||||
            for image in os.listdir(face_folder):
 | 
					            for image in os.listdir(face_folder):
 | 
				
			||||||
                img = cv2.imread(os.path.join(face_folder, image))
 | 
					                img = cv2.imread(os.path.join(face_folder, image))
 | 
				
			||||||
                gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
 | 
					                img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
 | 
				
			||||||
                equ = cv2.equalizeHist(gray)
 | 
					                img = self.__align_face(img, img.shape[1], img.shape[0])
 | 
				
			||||||
                faces.append(equ)
 | 
					                faces.append(img)
 | 
				
			||||||
                labels.append(idx)
 | 
					                labels.append(idx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.recognizer.train(faces, np.array(labels))
 | 
					        self.recognizer.train(faces, np.array(labels))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __align_face(
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        image: np.ndarray,
 | 
				
			||||||
 | 
					        output_width: int,
 | 
				
			||||||
 | 
					        output_height: int,
 | 
				
			||||||
 | 
					    ) -> np.ndarray:
 | 
				
			||||||
 | 
					        _, lands = self.landmark_detector.fit(
 | 
				
			||||||
 | 
					            image, np.array([(0, 0, image.shape[1], image.shape[0])])
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        landmarks = lands[0][0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # get landmarks for eyes
 | 
				
			||||||
 | 
					        leftEyePts = landmarks[42:48]
 | 
				
			||||||
 | 
					        rightEyePts = landmarks[36:42]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # compute the center of mass for each eye
 | 
				
			||||||
 | 
					        leftEyeCenter = leftEyePts.mean(axis=0).astype("int")
 | 
				
			||||||
 | 
					        rightEyeCenter = rightEyePts.mean(axis=0).astype("int")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # compute the angle between the eye centroids
 | 
				
			||||||
 | 
					        dY = rightEyeCenter[1] - leftEyeCenter[1]
 | 
				
			||||||
 | 
					        dX = rightEyeCenter[0] - leftEyeCenter[0]
 | 
				
			||||||
 | 
					        angle = np.degrees(np.arctan2(dY, dX)) - 180
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # compute the desired right eye x-coordinate based on the
 | 
				
			||||||
 | 
					        # desired x-coordinate of the left eye
 | 
				
			||||||
 | 
					        desiredRightEyeX = 1.0 - 0.35
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # determine the scale of the new resulting image by taking
 | 
				
			||||||
 | 
					        # the ratio of the distance between eyes in the *current*
 | 
				
			||||||
 | 
					        # image to the ratio of distance between eyes in the
 | 
				
			||||||
 | 
					        # *desired* image
 | 
				
			||||||
 | 
					        dist = np.sqrt((dX**2) + (dY**2))
 | 
				
			||||||
 | 
					        desiredDist = desiredRightEyeX - 0.35
 | 
				
			||||||
 | 
					        desiredDist *= output_width
 | 
				
			||||||
 | 
					        scale = desiredDist / dist
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # compute center (x, y)-coordinates (i.e., the median point)
 | 
				
			||||||
 | 
					        # between the two eyes in the input image
 | 
				
			||||||
 | 
					        # grab the rotation matrix for rotating and scaling the face
 | 
				
			||||||
 | 
					        eyesCenter = (
 | 
				
			||||||
 | 
					            int((leftEyeCenter[0] + rightEyeCenter[0]) // 2),
 | 
				
			||||||
 | 
					            int((leftEyeCenter[1] + rightEyeCenter[1]) // 2),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        M = cv2.getRotationMatrix2D(eyesCenter, angle, scale)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # update the translation component of the matrix
 | 
				
			||||||
 | 
					        tX = output_width * 0.5
 | 
				
			||||||
 | 
					        tY = output_height * 0.35
 | 
				
			||||||
 | 
					        M[0, 2] += tX - eyesCenter[0]
 | 
				
			||||||
 | 
					        M[1, 2] += tY - eyesCenter[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # apply the affine transformation
 | 
				
			||||||
 | 
					        return cv2.warpAffine(
 | 
				
			||||||
 | 
					            image, M, (output_width, output_height), flags=cv2.INTER_CUBIC
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def clear_classifier(self) -> None:
 | 
					    def clear_classifier(self) -> None:
 | 
				
			||||||
        self.classifier = None
 | 
					        self.classifier = None
 | 
				
			||||||
        self.labeler = None
 | 
					        self.labeler = None
 | 
				
			||||||
@ -192,9 +253,9 @@ class FaceClassificationModel:
 | 
				
			|||||||
        if not self.label_map:
 | 
					        if not self.label_map:
 | 
				
			||||||
            self.__build_classifier()
 | 
					            self.__build_classifier()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        index, distance = self.recognizer.predict(
 | 
					        img = cv2.cvtColor(face_image, cv2.COLOR_BGR2GRAY)
 | 
				
			||||||
            cv2.equalizeHist(cv2.cvtColor(face_image, cv2.COLOR_BGR2GRAY))
 | 
					        img = self.__align_face(img, img.shape[1], img.shape[0])
 | 
				
			||||||
        )
 | 
					        index, distance = self.recognizer.predict(img)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if index == -1:
 | 
					        if index == -1:
 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user