diff --git a/frigate/data_processing/real_time/face.py b/frigate/data_processing/real_time/face.py index 6936e3ea1..2b6239742 100644 --- a/frigate/data_processing/real_time/face.py +++ b/frigate/data_processing/real_time/face.py @@ -221,6 +221,13 @@ class FaceRealTimeProcessor(RealTimeProcessorApi): max(0, face_box[0]) : min(frame.shape[1], face_box[2]), ] + # check that face is correct size + if area(face_box) < self.config.cameras[camera].face_recognition.min_area: + logger.debug( + f"Detected face that is smaller than the min_area {face} < {self.config.cameras[camera].face_recognition.min_area}" + ) + return + try: face_frame = cv2.cvtColor(face_frame, cv2.COLOR_RGB2BGR) except Exception: diff --git a/frigate/detectors/plugins/hailo8l.py b/frigate/detectors/plugins/hailo8l.py index d4ef40d62..3f561c5f5 100755 --- a/frigate/detectors/plugins/hailo8l.py +++ b/frigate/detectors/plugins/hailo8l.py @@ -8,17 +8,6 @@ from typing import Dict, List, Optional, Tuple import cv2 import numpy as np - -try: - from hailo_platform import ( - HEF, - FormatType, - HailoSchedulingAlgorithm, - VDevice, - ) -except ModuleNotFoundError: - pass - from pydantic import Field from typing_extensions import Literal @@ -102,6 +91,18 @@ class HailoAsyncInference: output_type: Optional[Dict[str, str]] = None, send_original_frame: bool = False, ) -> None: + # when importing hailo it activates the driver + # which leaves processes running even though it may not be used. + try: + from hailo_platform import ( + HEF, + FormatType, + HailoSchedulingAlgorithm, + VDevice, + ) + except ModuleNotFoundError: + pass + self.input_store = input_store self.output_store = output_store @@ -112,24 +113,19 @@ class HailoAsyncInference: self.target = VDevice(params) self.infer_model = self.target.create_infer_model(hef_path) self.infer_model.set_batch_size(batch_size) + if input_type is not None: - self._set_input_type(input_type) + self.infer_model.input().set_format_type(getattr(FormatType, input_type)) + if output_type is not None: - self._set_output_type(output_type) + for output_name, output_type in output_type.items(): + self.infer_model.output(output_name).set_format_type( + getattr(FormatType, output_type) + ) + self.output_type = output_type self.send_original_frame = send_original_frame - def _set_input_type(self, input_type: Optional[str] = None) -> None: - self.infer_model.input().set_format_type(getattr(FormatType, input_type)) - - def _set_output_type( - self, output_type_dict: Optional[Dict[str, str]] = None - ) -> None: - for output_name, output_type in output_type_dict.items(): - self.infer_model.output(output_name).set_format_type( - getattr(FormatType, output_type) - ) - def callback( self, completion_info, diff --git a/frigate/record/cleanup.py b/frigate/record/cleanup.py index c86c81859..1de08a899 100644 --- a/frigate/record/cleanup.py +++ b/frigate/record/cleanup.py @@ -69,7 +69,7 @@ class RecordingCleanup(threading.Thread): now - datetime.timedelta(days=config.record.detections.retain.days) ).timestamp() expired_reviews: ReviewSegment = ( - ReviewSegment.select(ReviewSegment.id) + ReviewSegment.select(ReviewSegment.id, ReviewSegment.thumb_path) .where(ReviewSegment.camera == config.name) .where( ( @@ -84,6 +84,10 @@ class RecordingCleanup(threading.Thread): .namedtuples() ) + thumbs_to_delete = list(map(lambda x: x[1], expired_reviews)) + for thumb_path in thumbs_to_delete: + Path(thumb_path).unlink(missing_ok=True) + max_deletes = 100000 deleted_reviews_list = list(map(lambda x: x[0], expired_reviews)) for i in range(0, len(deleted_reviews_list), max_deletes): diff --git a/frigate/util/builtin.py b/frigate/util/builtin.py index 0f245107a..e7a03052d 100644 --- a/frigate/util/builtin.py +++ b/frigate/util/builtin.py @@ -187,6 +187,9 @@ def update_yaml_from_url(file_path, url): parsed_url = urllib.parse.urlparse(url) query_string = urllib.parse.parse_qs(parsed_url.query, keep_blank_values=True) + # Filter out empty keys but keep blank values for non-empty keys + query_string = {k: v for k, v in query_string.items() if k} + for key_path_str, new_value_list in query_string.items(): key_path = key_path_str.split(".") for i in range(len(key_path)): diff --git a/web/src/views/recording/RecordingView.tsx b/web/src/views/recording/RecordingView.tsx index 9e38503d1..03d83d105 100644 --- a/web/src/views/recording/RecordingView.tsx +++ b/web/src/views/recording/RecordingView.tsx @@ -50,6 +50,11 @@ import { useFullscreen } from "@/hooks/use-fullscreen"; import { useTimezone } from "@/hooks/use-date-utils"; import { useTimelineZoom } from "@/hooks/use-timeline-zoom"; import { useTranslation } from "react-i18next"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; type RecordingViewProps = { startCamera: string; @@ -670,31 +675,38 @@ export function RecordingView({ } return ( -
- { - previewRefs.current[cam] = controller; - controller.scrubToTimestamp(startTime); - }} - onClick={() => onSelectCamera(cam)} - /> -
+ + +
+ { + previewRefs.current[cam] = controller; + controller.scrubToTimestamp(startTime); + }} + onClick={() => onSelectCamera(cam)} + /> +
+
+ + {cam.replaceAll("_", " ")} + +
); })}