2024-09-28 22:21:42 +03:00
from typing import Any , Optional , Union
2025-08-08 16:33:11 -06:00
from pydantic import Field , PrivateAttr , field_serializer , field_validator
2024-09-28 22:21:42 +03:00
from ..base import FrigateBaseModel
2026-02-28 08:04:43 -06:00
from .mask import ObjectMaskConfig
2024-09-28 22:21:42 +03:00
2025-08-08 16:33:11 -06:00
__all__ = [ "ObjectConfig" , "GenAIObjectConfig" , "FilterConfig" ]
2024-09-28 22:21:42 +03:00
DEFAULT_TRACKED_OBJECTS = [ "person" ]
class FilterConfig ( FrigateBaseModel ):
2025-02-09 15:48:23 -06:00
min_area : Union [ int , float ] = Field (
default = 0 ,
2026-02-27 09:55:36 -06:00
title = "Minimum object area" ,
description = "Minimum bounding box area (pixels or percentage) required for this object type. Can be pixels (int) or percentage (float between 0.000001 and 0.99)." ,
2024-09-28 22:21:42 +03:00
)
2025-02-09 15:48:23 -06:00
max_area : Union [ int , float ] = Field (
default = 24000000 ,
2026-02-27 09:55:36 -06:00
title = "Maximum object area" ,
description = "Maximum bounding box area (pixels or percentage) allowed for this object type. Can be pixels (int) or percentage (float between 0.000001 and 0.99)." ,
2024-09-28 22:21:42 +03:00
)
min_ratio : float = Field (
default = 0 ,
2026-02-27 09:55:36 -06:00
title = "Minimum aspect ratio" ,
description = "Minimum width/height ratio required for the bounding box to qualify." ,
2024-09-28 22:21:42 +03:00
)
max_ratio : float = Field (
default = 24000000 ,
2026-02-27 09:55:36 -06:00
title = "Maximum aspect ratio" ,
description = "Maximum width/height ratio allowed for the bounding box to qualify." ,
2024-09-28 22:21:42 +03:00
)
threshold : float = Field (
default = 0.7 ,
2026-02-27 09:55:36 -06:00
title = "Confidence threshold" ,
description = "Average detection confidence threshold required for the object to be considered a true positive." ,
2024-09-28 22:21:42 +03:00
)
min_score : float = Field (
2026-02-27 09:55:36 -06:00
default = 0.5 ,
title = "Minimum confidence" ,
description = "Minimum single-frame detection confidence required for the object to be counted." ,
2024-09-28 22:21:42 +03:00
)
2026-02-28 08:04:43 -06:00
mask : dict [ str , Optional [ ObjectMaskConfig ]] = Field (
default_factory = dict ,
2026-02-27 09:55:36 -06:00
title = "Filter mask" ,
description = "Polygon coordinates defining where this filter applies within the frame." ,
2024-09-28 22:21:42 +03:00
)
2026-02-28 08:04:43 -06:00
raw_mask : dict [ str , Optional [ ObjectMaskConfig ]] = Field (
default_factory = dict , exclude = True
)
2024-09-28 22:21:42 +03:00
@field_serializer ( "mask" , when_used = "json" )
def serialize_mask ( self , value : Any , info ):
2026-02-28 08:04:43 -06:00
if self . raw_mask :
return self . raw_mask
return value
2024-09-28 22:21:42 +03:00
@field_serializer ( "raw_mask" , when_used = "json" )
def serialize_raw_mask ( self , value : Any , info ):
return None
2025-08-08 16:33:11 -06:00
class GenAIObjectTriggerConfig ( FrigateBaseModel ):
tracked_object_end : bool = Field (
2026-02-27 09:55:36 -06:00
default = True ,
title = "Send on end" ,
description = "Send a request to GenAI when the tracked object ends." ,
2025-08-08 16:33:11 -06:00
)
after_significant_updates : Optional [ int ] = Field (
default = None ,
2026-02-27 09:55:36 -06:00
title = "Early GenAI trigger" ,
description = "Send a request to GenAI after a specified number of significant updates for the tracked object." ,
2025-08-08 16:33:11 -06:00
ge = 1 ,
)
class GenAIObjectConfig ( FrigateBaseModel ):
2026-02-27 09:55:36 -06:00
enabled : bool = Field (
default = False ,
title = "Enable GenAI" ,
description = "Enable GenAI generation of descriptions for tracked objects by default." ,
)
2025-08-08 16:33:11 -06:00
use_snapshot : bool = Field (
2026-02-27 09:55:36 -06:00
default = False ,
title = "Use snapshots" ,
description = "Use object snapshots instead of thumbnails for GenAI description generation." ,
2025-08-08 16:33:11 -06:00
)
prompt : str = Field (
default = "Analyze the sequence of images containing the {label} . Focus on the likely intent or behavior of the {label} based on its actions and movement, rather than describing its appearance or the surroundings. Consider what the {label} is doing, why, and what it might do next." ,
2026-02-27 09:55:36 -06:00
title = "Caption prompt" ,
description = "Default prompt template used when generating descriptions with GenAI." ,
2025-08-08 16:33:11 -06:00
)
object_prompts : dict [ str , str ] = Field (
2026-02-27 09:55:36 -06:00
default_factory = dict ,
title = "Object prompts" ,
description = "Per-object prompts to customize GenAI outputs for specific labels." ,
2025-08-08 16:33:11 -06:00
)
objects : Union [ str , list [ str ]] = Field (
default_factory = list ,
2026-02-27 09:55:36 -06:00
title = "GenAI objects" ,
description = "List of object labels to send to GenAI by default." ,
2025-08-08 16:33:11 -06:00
)
required_zones : Union [ str , list [ str ]] = Field (
default_factory = list ,
2026-02-27 09:55:36 -06:00
title = "Required zones" ,
description = "Zones that must be entered for objects to qualify for GenAI description generation." ,
2025-08-08 16:33:11 -06:00
)
debug_save_thumbnails : bool = Field (
default = False ,
2026-02-27 09:55:36 -06:00
title = "Save thumbnails" ,
description = "Save thumbnails sent to GenAI for debugging and review." ,
2025-08-08 16:33:11 -06:00
)
send_triggers : GenAIObjectTriggerConfig = Field (
default_factory = GenAIObjectTriggerConfig ,
2026-02-27 09:55:36 -06:00
title = "GenAI triggers" ,
description = "Defines when frames should be sent to GenAI (on end, after updates, etc.)." ,
2025-08-08 16:33:11 -06:00
)
enabled_in_config : Optional [ bool ] = Field (
2026-02-27 09:55:36 -06:00
default = None ,
title = "Original GenAI state" ,
description = "Indicates whether GenAI was enabled in the original static config." ,
2025-08-08 16:33:11 -06:00
)
@field_validator ( "required_zones" , mode = "before" )
@classmethod
def validate_required_zones ( cls , v ):
if isinstance ( v , str ) and "," not in v :
return [ v ]
return v
2024-09-28 22:21:42 +03:00
class ObjectConfig ( FrigateBaseModel ):
2026-02-27 09:55:36 -06:00
track : list [ str ] = Field (
default = DEFAULT_TRACKED_OBJECTS ,
title = "Objects to track" ,
description = "List of object labels to track for all cameras; can be overridden per-camera." ,
)
2024-09-28 22:21:42 +03:00
filters : dict [ str , FilterConfig ] = Field (
2026-02-27 09:55:36 -06:00
default_factory = dict ,
title = "Object filters" ,
description = "Filters applied to detected objects to reduce false positives (area, ratio, confidence)." ,
)
2026-02-28 08:04:43 -06:00
mask : dict [ str , Optional [ ObjectMaskConfig ]] = Field (
default_factory = dict ,
2026-02-27 09:55:36 -06:00
title = "Object mask" ,
description = "Mask polygon used to prevent object detection in specified areas." ,
2024-09-28 22:21:42 +03:00
)
2026-02-28 08:04:43 -06:00
raw_mask : dict [ str , Optional [ ObjectMaskConfig ]] = Field (
default_factory = dict , exclude = True
)
2025-08-08 16:33:11 -06:00
genai : GenAIObjectConfig = Field (
default_factory = GenAIObjectConfig ,
2026-02-27 09:55:36 -06:00
title = "GenAI object config" ,
description = "GenAI options for describing tracked objects and sending frames for generation." ,
2025-08-08 16:33:11 -06:00
)
2024-10-26 12:14:21 -06:00
_all_objects : list [ str ] = PrivateAttr ()
@property
def all_objects ( self ) -> list [ str ]:
return self . _all_objects
def parse_all_objects ( self , cameras ):
if "_all_objects" in self :
return
# get list of unique enabled labels for tracking
enabled_labels = set ( self . track )
for camera in cameras . values ():
enabled_labels . update ( camera . objects . track )
self . _all_objects = list ( enabled_labels )
2026-02-28 08:04:43 -06:00
@field_serializer ( "mask" , when_used = "json" )
def serialize_mask ( self , value : Any , info ):
if self . raw_mask :
return self . raw_mask
return value
@field_serializer ( "raw_mask" , when_used = "json" )
def serialize_raw_mask ( self , value : Any , info ):
return None