Merge branch 'release-0.10.0' of github.com:blakeblackshear/frigate into retain-select-events

This commit is contained in:
Nick Mowen 2022-02-08 07:21:05 -07:00
commit f7911c6ad7
6 changed files with 61 additions and 30 deletions

View File

@ -159,9 +159,11 @@ detect:
enabled: True enabled: True
# Optional: Number of frames without a detection before frigate considers an object to be gone. (default: 5x the frame rate) # Optional: Number of frames without a detection before frigate considers an object to be gone. (default: 5x the frame rate)
max_disappeared: 25 max_disappeared: 25
# Optional: Frequency for running detection on stationary objects (default: 0) # Optional: Frequency for running detection on stationary objects (default: shown below)
# When set to 0, object detection will never be run on stationary objects. If set to 10, it will be run on every 10th frame. # When set to 0, object detection will never be run on stationary objects. If set to 10, it will be run on every 10th frame.
stationary_interval: 0 stationary_interval: 0
# Optional: Number of frames without a position change for an object to be considered stationary (default: shown below)
stationary_threshold: 10
# Optional: Object configuration # Optional: Object configuration
# NOTE: Can be overridden at the camera level # NOTE: Can be overridden at the camera level

View File

@ -177,6 +177,11 @@ class DetectConfig(FrigateBaseModel):
title="Frame interval for checking stationary objects.", title="Frame interval for checking stationary objects.",
ge=0, ge=0,
) )
stationary_threshold: Optional[int] = Field(
default=10,
title="Number of frames without a position change for an object to be considered stationary",
ge=1,
)
class FilterConfig(FrigateBaseModel): class FilterConfig(FrigateBaseModel):

View File

@ -15,6 +15,16 @@ from frigate.models import Event
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def should_update_db(prev_event, current_event):
return (
prev_event["top_score"] != current_event["top_score"]
or prev_event["entered_zones"] != current_event["entered_zones"]
or prev_event["thumbnail"] != current_event["thumbnail"]
or prev_event["has_clip"] != current_event["has_clip"]
or prev_event["has_snapshot"] != current_event["has_snapshot"]
)
class EventProcessor(threading.Thread): class EventProcessor(threading.Thread):
def __init__( def __init__(
self, config, camera_processes, event_queue, event_processed_queue, stop_event self, config, camera_processes, event_queue, event_processed_queue, stop_event
@ -48,7 +58,9 @@ class EventProcessor(threading.Thread):
if event_type == "start": if event_type == "start":
self.events_in_process[event_data["id"]] = event_data self.events_in_process[event_data["id"]] = event_data
elif event_type == "update": elif event_type == "update" and should_update_db(
self.events_in_process[event_data["id"]], event_data
):
self.events_in_process[event_data["id"]] = event_data self.events_in_process[event_data["id"]] = event_data
# TODO: this will generate a lot of db activity possibly # TODO: this will generate a lot of db activity possibly
if event_data["has_clip"] or event_data["has_snapshot"]: if event_data["has_clip"] or event_data["has_snapshot"]:

View File

@ -101,14 +101,13 @@ class TrackedObject:
return median(scores) return median(scores)
def update(self, current_frame_time, obj_data): def update(self, current_frame_time, obj_data):
significant_update = False thumb_update = False
zone_change = False significant_change = False
self.obj_data.update(obj_data)
# if the object is not in the current frame, add a 0.0 to the score history # if the object is not in the current frame, add a 0.0 to the score history
if self.obj_data["frame_time"] != current_frame_time: if obj_data["frame_time"] != current_frame_time:
self.score_history.append(0.0) self.score_history.append(0.0)
else: else:
self.score_history.append(self.obj_data["score"]) self.score_history.append(obj_data["score"])
# only keep the last 10 scores # only keep the last 10 scores
if len(self.score_history) > 10: if len(self.score_history) > 10:
self.score_history = self.score_history[-10:] self.score_history = self.score_history[-10:]
@ -122,24 +121,24 @@ class TrackedObject:
if not self.false_positive: if not self.false_positive:
# determine if this frame is a better thumbnail # determine if this frame is a better thumbnail
if self.thumbnail_data is None or is_better_thumbnail( if self.thumbnail_data is None or is_better_thumbnail(
self.thumbnail_data, self.obj_data, self.camera_config.frame_shape self.thumbnail_data, obj_data, self.camera_config.frame_shape
): ):
self.thumbnail_data = { self.thumbnail_data = {
"frame_time": self.obj_data["frame_time"], "frame_time": obj_data["frame_time"],
"box": self.obj_data["box"], "box": obj_data["box"],
"area": self.obj_data["area"], "area": obj_data["area"],
"region": self.obj_data["region"], "region": obj_data["region"],
"score": self.obj_data["score"], "score": obj_data["score"],
} }
significant_update = True thumb_update = True
# check zones # check zones
current_zones = [] current_zones = []
bottom_center = (self.obj_data["centroid"][0], self.obj_data["box"][3]) bottom_center = (obj_data["centroid"][0], obj_data["box"][3])
# check each zone # check each zone
for name, zone in self.camera_config.zones.items(): for name, zone in self.camera_config.zones.items():
# if the zone is not for this object type, skip # if the zone is not for this object type, skip
if len(zone.objects) > 0 and not self.obj_data["label"] in zone.objects: if len(zone.objects) > 0 and not obj_data["label"] in zone.objects:
continue continue
contour = zone.contour contour = zone.contour
# check if the object is in the zone # check if the object is in the zone
@ -150,12 +149,25 @@ class TrackedObject:
if name not in self.entered_zones: if name not in self.entered_zones:
self.entered_zones.append(name) self.entered_zones.append(name)
# if the zones changed, signal an update if not self.false_positive:
if not self.false_positive and set(self.current_zones) != set(current_zones): # if the zones changed, signal an update
zone_change = True if set(self.current_zones) != set(current_zones):
significant_change = True
# if the position changed, signal an update
if self.obj_data["position_changes"] != obj_data["position_changes"]:
significant_change = True
# if the motionless_count crosses the stationary threshold
if (
self.obj_data["motionless_count"]
> self.camera_config.detect.stationary_threshold
):
significant_change = True
self.obj_data.update(obj_data)
self.current_zones = current_zones self.current_zones = current_zones
return (significant_update, zone_change) return (thumb_update, significant_change)
def to_dict(self, include_thumbnail: bool = False): def to_dict(self, include_thumbnail: bool = False):
snapshot_time = ( snapshot_time = (
@ -466,11 +478,11 @@ class CameraState:
for id in updated_ids: for id in updated_ids:
updated_obj = tracked_objects[id] updated_obj = tracked_objects[id]
significant_update, zone_change = updated_obj.update( thumb_update, significant_update = updated_obj.update(
frame_time, current_detections[id] frame_time, current_detections[id]
) )
if significant_update: if thumb_update:
# ensure this frame is stored in the cache # ensure this frame is stored in the cache
if ( if (
updated_obj.thumbnail_data["frame_time"] == frame_time updated_obj.thumbnail_data["frame_time"] == frame_time
@ -480,13 +492,13 @@ class CameraState:
updated_obj.last_updated = frame_time updated_obj.last_updated = frame_time
# if it has been more than 5 seconds since the last publish # if it has been more than 5 seconds since the last thumb update
# and the last update is greater than the last publish or # and the last update is greater than the last publish or
# the object has changed zones # the object has changed significantly
if ( if (
frame_time - updated_obj.last_published > 5 frame_time - updated_obj.last_published > 5
and updated_obj.last_updated > updated_obj.last_published and updated_obj.last_updated > updated_obj.last_published
) or zone_change: ) or significant_update:
# call event handlers # call event handlers
for c in self.callbacks["update"]: for c in self.callbacks["update"]:
c(self.name, updated_obj, frame_time) c(self.name, updated_obj, frame_time)

View File

@ -48,7 +48,7 @@ class ObjectTracker:
del self.tracked_objects[id] del self.tracked_objects[id]
del self.disappeared[id] del self.disappeared[id]
# tracks the current position of the object based on the last 10 bounding boxes # tracks the current position of the object based on the last N bounding boxes
# returns False if the object has moved outside its previous position # returns False if the object has moved outside its previous position
def update_position(self, id, box): def update_position(self, id, box):
position = self.positions[id] position = self.positions[id]
@ -78,9 +78,9 @@ class ObjectTracker:
} }
return False return False
# if there are less than 10 entries for the position, add the bounding box # if there are less than stationary_threshold entries for the position, add the bounding box
# and recompute the position box # and recompute the position box
if len(position["xmins"]) < 10: if len(position["xmins"]) < self.detect_config.stationary_threshold:
position["xmins"].append(xmin) position["xmins"].append(xmin)
position["ymins"].append(ymin) position["ymins"].append(ymin)
position["xmaxs"].append(xmax) position["xmaxs"].append(xmax)

View File

@ -507,8 +507,8 @@ def process_frames(
stationary_object_ids = [ stationary_object_ids = [
obj["id"] obj["id"]
for obj in object_tracker.tracked_objects.values() for obj in object_tracker.tracked_objects.values()
# if there hasn't been motion for 10 frames # if there hasn't been motion for N frames
if obj["motionless_count"] >= 10 if obj["motionless_count"] >= detect_config.stationary_threshold
# and it isn't due for a periodic check # and it isn't due for a periodic check
and ( and (
detect_config.stationary_interval == 0 detect_config.stationary_interval == 0