diff --git a/viseron/components/compreface/__init__.py b/viseron/components/compreface/__init__.py index dfcbd6c57..b1fad5dc2 100644 --- a/viseron/components/compreface/__init__.py +++ b/viseron/components/compreface/__init__.py @@ -1,4 +1,5 @@ """CompreFace face recognition.""" + import logging from typing import Any @@ -134,7 +135,7 @@ def setup_domains(vis: Viseron, config: dict[str, Any]) -> None: if not config.get(CONFIG_FACE_RECOGNITION, None): return - for camera_identifier in config[CONFIG_FACE_RECOGNITION][CONFIG_CAMERAS].keys(): + for camera_identifier in config[CONFIG_FACE_RECOGNITION][CONFIG_CAMERAS]: setup_domain( vis, COMPONENT, @@ -148,3 +149,10 @@ def setup_domains(vis: Viseron, config: dict[str, Any]) -> None: ) ], ) + + +def unload(vis: Viseron) -> bool: + """Unload the compreface component.""" + if COMPONENT in vis.data: + del vis.data[COMPONENT] + return True diff --git a/viseron/components/compreface/face_recognition.py b/viseron/components/compreface/face_recognition.py index 02920f4c0..5014252ed 100644 --- a/viseron/components/compreface/face_recognition.py +++ b/viseron/components/compreface/face_recognition.py @@ -1,4 +1,5 @@ """CompreFace face recognition.""" + from __future__ import annotations import logging @@ -6,13 +7,11 @@ from typing import TYPE_CHECKING, Any import cv2 -import numpy as np from compreface import CompreFace -from compreface.service.recognition_service import RecognitionService from viseron.domains.face_recognition import AbstractFaceRecognition from viseron.domains.face_recognition.binary_sensor import FaceDetectionBinarySensor -from viseron.domains.face_recognition.const import CONFIG_FACE_RECOGNITION_PATH +from viseron.domains.face_recognition.const import CONFIG_FACE_RECOGNITION_PATH, DOMAIN from viseron.helpers import calculate_absolute_coords, get_image_files_in_folder from .const import ( @@ -32,6 +31,9 @@ ) if TYPE_CHECKING: + import numpy as np + from compreface.service.recognition_service import RecognitionService + from viseron import Viseron from viseron.domains.object_detector.detected_object import DetectedObject from viseron.domains.post_processor import PostProcessorFrame @@ -39,7 +41,7 @@ LOGGER = logging.getLogger(__name__) -def setup(vis: Viseron, config, identifier) -> bool: +def setup(vis: Viseron, config: dict[str, Any], identifier: str) -> bool: """Set up the CompreFace face_recognition domain.""" FaceRecognition(vis, config, identifier) @@ -49,13 +51,15 @@ def setup(vis: Viseron, config, identifier) -> bool: class FaceRecognition(AbstractFaceRecognition): """CompreFace face recognition processor.""" - def __init__(self, vis: Viseron, config, camera_identifier) -> None: + def __init__( + self, vis: Viseron, config: dict[str, Any], camera_identifier: str + ) -> None: super().__init__( vis, COMPONENT, config[CONFIG_FACE_RECOGNITION], camera_identifier, - not config[CONFIG_FACE_RECOGNITION][CONFIG_USE_SUBJECTS], + generate_entities=not config[CONFIG_FACE_RECOGNITION][CONFIG_USE_SUBJECTS], ) if COMPONENT not in self._vis.data: @@ -93,11 +97,13 @@ def update_subject_entities(self) -> list: added_subjects.append(f"{self._camera.identifier}_{subject}") self._vis.add_entity( COMPONENT, - FaceDetectionBinarySensor(self._vis, self._camera, subject), + binary_sensor, + DOMAIN, + self._camera.identifier, ) return added_subjects - def preprocess(self, frame) -> np.ndarray: + def preprocess(self, frame: np.ndarray) -> np.ndarray: """Preprocess frame.""" return frame @@ -120,8 +126,8 @@ def face_recognition( detections = self.recognition_service.recognize( cv2.imencode(".jpg", cropped_frame)[1].tobytes(), ) - except Exception as error: # pylint: disable=broad-except - self._logger.error("Error calling compreface: %s", error, exc_info=True) + except Exception: # pylint: disable=broad-except + self._logger.exception("Error calling compreface") return self._logger.debug(f"CompreFace response: {detections}") @@ -161,7 +167,7 @@ def face_recognition( class CompreFaceService: """CompreFace service.""" - def __init__(self, config: dict[str, Any]): + def __init__(self, config: dict[str, Any]) -> None: options = { CONFIG_LIMIT: config[CONFIG_FACE_RECOGNITION][CONFIG_LIMIT], CONFIG_DET_PROB_THRESHOLD: config[CONFIG_FACE_RECOGNITION][ @@ -200,7 +206,7 @@ def recognition_service(self) -> RecognitionService: class CompreFaceTrain: """Train CompreFace to recognize faces.""" - def __init__(self, vis: Viseron, config) -> None: + def __init__(self, vis: Viseron, config: dict[str, Any]) -> None: self._config = config self._vis = vis @@ -264,7 +270,5 @@ def train(self) -> None: LOGGER.debug(f"CompreFace response: {result}") if "message" in result: LOGGER.warning( - "Image {} not suitable for training: {}".format( - img_path, result - ) + f"Image {img_path} not suitable for training: {result}" ) diff --git a/viseron/domains/face_recognition/__init__.py b/viseron/domains/face_recognition/__init__.py index da02e49ff..df0495086 100644 --- a/viseron/domains/face_recognition/__init__.py +++ b/viseron/domains/face_recognition/__init__.py @@ -10,8 +10,6 @@ import voluptuous as vol -from viseron.domains.camera.shared_frames import SharedFrame -from viseron.domains.object_detector.detected_object import DetectedObject from viseron.domains.post_processor import ( BASE_CONFIG_SCHEMA, AbstractPostProcessor, @@ -47,6 +45,8 @@ if TYPE_CHECKING: from viseron import Viseron + from viseron.domains.camera.shared_frames import SharedFrame + from viseron.domains.object_detector.detected_object import DetectedObject BASE_CONFIG_SCHEMA = BASE_CONFIG_SCHEMA.extend( { @@ -122,6 +122,7 @@ def __init__( component: str, config: dict, camera_identifier: str, + *, generate_entities: bool = True, ) -> None: super().__init__(vis, config, camera_identifier) @@ -137,7 +138,7 @@ def __init__( camera_identifier, ) - def __post_init__(self, *args, **kwargs): + def __post_init__(self, *args, **kwargs) -> None: """Post init hook.""" self._vis.register_domain(DOMAIN, self._camera_identifier, self) @@ -228,7 +229,7 @@ def unknown_face_found( if self._config[CONFIG_SAVE_UNKNOWN_FACES]: self._save_face(face_dict, coordinates, shared_frame) - def expire_face(self, face) -> None: + def expire_face(self, face: str) -> None: """Expire no longer found face.""" self._logger.debug(f"Expiring face {face}") self._vis.dispatch_event( diff --git a/viseron/domains/face_recognition/binary_sensor.py b/viseron/domains/face_recognition/binary_sensor.py index 24488dcfa..5480fd7ef 100644 --- a/viseron/domains/face_recognition/binary_sensor.py +++ b/viseron/domains/face_recognition/binary_sensor.py @@ -1,4 +1,5 @@ """Binary sensor that represents face recognition.""" + from __future__ import annotations from typing import TYPE_CHECKING @@ -50,7 +51,7 @@ def _is_on(self): return self._detected @property - def extra_attributes(self): + def extra_attributes(self) -> dict: """Return entity attributes.""" if self._face: return {