GenAI: enable blurring detected faces if desired

This commit is contained in:
leccelecce 2024-12-23 17:44:43 +00:00
parent 00371546a3
commit 6a05e791c8
4 changed files with 44 additions and 0 deletions

View File

@ -174,12 +174,15 @@ genai:
object_prompts: object_prompts:
person: "Examine the main person in these images. What are they doing and what might their actions suggest about their intent (e.g., approaching a door, leaving an area, standing still)? Do not describe the surroundings or static details." person: "Examine the main person in these images. What are they doing and what might their actions suggest about their intent (e.g., approaching a door, leaving an area, standing still)? Do not describe the surroundings or static details."
car: "Observe the primary vehicle in these images. Focus on its movement, direction, or purpose (e.g., parking, approaching, circling). If it's a delivery vehicle, mention the company." car: "Observe the primary vehicle in these images. Focus on its movement, direction, or purpose (e.g., parking, approaching, circling). If it's a delivery vehicle, mention the company."
blur_faces: False
``` ```
Prompts can also be overriden at the camera level to provide a more detailed prompt to the model about your specific camera, if you desire. By default, descriptions will be generated for all tracked objects and all zones. But you can also optionally specify `objects` and `required_zones` to only generate descriptions for certain tracked objects or zones. Prompts can also be overriden at the camera level to provide a more detailed prompt to the model about your specific camera, if you desire. By default, descriptions will be generated for all tracked objects and all zones. But you can also optionally specify `objects` and `required_zones` to only generate descriptions for certain tracked objects or zones.
Optionally, you can generate the description using a snapshot (if enabled) by setting `use_snapshot` to `True`. By default, this is set to `False`, which sends the uncompressed images from the `detect` stream collected over the object's lifetime to the model. Once the object lifecycle ends, only a single compressed and cropped thumbnail is saved with the tracked object. Using a snapshot might be useful when you want to _regenerate_ a tracked object's description as it will provide the AI with a higher-quality image (typically downscaled by the AI itself) than the cropped/compressed thumbnail. Using a snapshot otherwise has a trade-off in that only a single image is sent to your provider, which will limit the model's ability to determine object movement or direction. Optionally, you can generate the description using a snapshot (if enabled) by setting `use_snapshot` to `True`. By default, this is set to `False`, which sends the uncompressed images from the `detect` stream collected over the object's lifetime to the model. Once the object lifecycle ends, only a single compressed and cropped thumbnail is saved with the tracked object. Using a snapshot might be useful when you want to _regenerate_ a tracked object's description as it will provide the AI with a higher-quality image (typically downscaled by the AI itself) than the cropped/compressed thumbnail. Using a snapshot otherwise has a trade-off in that only a single image is sent to your provider, which will limit the model's ability to determine object movement or direction.
If required for privacy purposes, Frigate can use OpenCV to attempt to detect faces in an image and blur them before sending to the provider. You can enable this by setting `blur_faces` to `True`.
```yaml ```yaml
cameras: cameras:
front_door: front_door:

View File

@ -543,6 +543,8 @@ genai:
# Format: {label}: {prompt} # Format: {label}: {prompt}
object_prompts: object_prompts:
person: "My special person prompt." person: "My special person prompt."
# Optional: Attempt to detect faces in the image and blur for privacy before sending to provider (default: shown below)
blur_faces: False
# Optional: Restream configuration # Optional: Restream configuration
# Uses https://github.com/AlexxIT/go2rtc (v1.9.2) # Uses https://github.com/AlexxIT/go2rtc (v1.9.2)

View File

@ -68,3 +68,6 @@ class GenAIConfig(FrigateBaseModel):
provider: GenAIProviderEnum = Field( provider: GenAIProviderEnum = Field(
default=GenAIProviderEnum.openai, title="GenAI provider." default=GenAIProviderEnum.openai, title="GenAI provider."
) )
blur_faces: bool = Field(
default=False, title="Blur faces if detected in the image."
)

View File

@ -5,6 +5,8 @@ import logging
import os import os
from typing import Optional from typing import Optional
import cv2
import numpy as np
from playhouse.shortcuts import model_to_dict from playhouse.shortcuts import model_to_dict
from frigate.config import CameraConfig, FrigateConfig, GenAIConfig, GenAIProviderEnum from frigate.config import CameraConfig, FrigateConfig, GenAIConfig, GenAIProviderEnum
@ -33,6 +35,14 @@ class GenAIClient:
self.timeout = timeout self.timeout = timeout
self.provider = self._init_provider() self.provider = self._init_provider()
if self.genai_config.blur_faces:
self.face_cascade_classifier = cv2.CascadeClassifier(
cv2.data.haarcascades + "/haarcascade_frontalface_alt.xml"
)
self.face_profile_cascade_classifier = cv2.CascadeClassifier(
cv2.data.haarcascades + "/haarcascade_profileface.xml"
)
def generate_description( def generate_description(
self, self,
camera_config: CameraConfig, camera_config: CameraConfig,
@ -44,6 +54,8 @@ class GenAIClient:
event.label, event.label,
camera_config.genai.prompt, camera_config.genai.prompt,
).format(**model_to_dict(event)) ).format(**model_to_dict(event))
if self.genai_config.blur_faces:
self.blur_faces(thumbnails)
logger.debug(f"Sending images to genai provider with prompt: {prompt}") logger.debug(f"Sending images to genai provider with prompt: {prompt}")
return self._send(prompt, thumbnails) return self._send(prompt, thumbnails)
@ -55,6 +67,30 @@ class GenAIClient:
"""Submit a request to the provider.""" """Submit a request to the provider."""
return None return None
def blur_faces(self, thumbnails: list[bytes]):
for thumb in thumbnails:
image = cv2.imdecode(np.frombuffer(thumb, dtype=np.int8), cv2.IMREAD_COLOR)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
face_data = self.face_cascade_classifier.detectMultiScale(
image=gray, scaleFactor=1.1, minNeighbors=2, minSize=(25, 25)
)
face_profile_data = self.face_profile_cascade_classifier.detectMultiScale(
image=gray, scaleFactor=1.1, minNeighbors=2, minSize=(25, 25)
)
for x, y, w, h in face_data:
roi = image[y : y + h, x : x + w]
roi = cv2.GaussianBlur(roi, (23, 23), 30)
image[y : y + h, x : x + w] = roi
for x, y, w, h in face_profile_data:
roi = image[y : y + h, x : x + w]
roi = cv2.GaussianBlur(roi, (23, 23), 30)
image[y : y + h, x : x + w] = roi
def get_genai_client(config: FrigateConfig) -> Optional[GenAIClient]: def get_genai_client(config: FrigateConfig) -> Optional[GenAIClient]:
"""Get the GenAI client.""" """Get the GenAI client."""