mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-02 09:15:22 +03:00
Merge branch 'release-0.10.0' of github.com:blakeblackshear/frigate into retain-select-events
This commit is contained in:
commit
f7911c6ad7
@ -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
|
||||||
|
|||||||
@ -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):
|
||||||
|
|||||||
@ -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"]:
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user