Ability to configure loitering time in a zone (#10543)

* Add loitering config

* Configure loitering

* simplify

* Add docs

* grammar

* Formatting
This commit is contained in:
Nicolas Mowen 2024-03-19 14:58:04 -06:00 committed by GitHub
parent ab6bac1d2c
commit 8589ef50a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 38 additions and 8 deletions

View File

@ -488,6 +488,8 @@ cameras:
coordinates: 545,1077,747,939,788,805 coordinates: 545,1077,747,939,788,805
# Optional: Number of consecutive frames required for object to be considered present in the zone (default: shown below). # Optional: Number of consecutive frames required for object to be considered present in the zone (default: shown below).
inertia: 3 inertia: 3
# Optional: Number of seconds that an object must loiter to be considered in the zone (default: shown below)
loitering_time: 0
# Optional: List of objects that can trigger this zone (default: all tracked objects) # Optional: List of objects that can trigger this zone (default: all tracked objects)
objects: objects:
- person - person

View File

@ -60,6 +60,19 @@ camera:
Only car objects can trigger the `front_yard_street` zone and only person can trigger the `entire_yard`. You will get events for person objects that enter anywhere in the yard, and events for cars only if they enter the street. Only car objects can trigger the `front_yard_street` zone and only person can trigger the `entire_yard`. You will get events for person objects that enter anywhere in the yard, and events for cars only if they enter the street.
### Zone Loitering
Sometimes objects are expected to be passing through a zone, but an object loitering in an area is unexpected. Zones can be configured to have a minimum loitering time before the object will be considered in the zone.
```yaml
camera:
zones:
sidewalk:
loitering_time: 4 # unit is in seconds
objects:
- person
```
### Zone Inertia ### Zone Inertia
Sometimes an objects bounding box may be slightly incorrect and the bottom center of the bounding box is inside the zone while the object is not actually in the zone. Zone inertia helps guard against this by requiring an object's bounding box to be within the zone for multiple consecutive frames. This value can be configured: Sometimes an objects bounding box may be slightly incorrect and the bottom center of the bounding box is inside the zone while the object is not actually in the zone. Zone inertia helps guard against this by requiring an object's bounding box to be within the zone for multiple consecutive frames. This value can be configured:

View File

@ -515,6 +515,11 @@ class ZoneConfig(BaseModel):
title="Number of consecutive frames required for object to be considered present in the zone.", title="Number of consecutive frames required for object to be considered present in the zone.",
gt=0, gt=0,
) )
loitering_time: int = Field(
default=0,
ge=0,
title="Number of seconds that an object must loiter to be considered in the zone.",
)
objects: List[str] = Field( objects: List[str] = Field(
default_factory=list, default_factory=list,
title="List of objects that can trigger the zone.", title="List of objects that can trigger the zone.",

View File

@ -116,7 +116,8 @@ class TrackedObject:
self.colormap = colormap self.colormap = colormap
self.camera_config = camera_config self.camera_config = camera_config
self.frame_cache = frame_cache self.frame_cache = frame_cache
self.zone_presence = {} self.zone_presence: dict[str, int] = {}
self.zone_loitering: dict[str, int] = {}
self.current_zones = [] self.current_zones = []
self.entered_zones = [] self.entered_zones = []
self.attributes = defaultdict(float) self.attributes = defaultdict(float)
@ -189,19 +190,28 @@ class TrackedObject:
if len(zone.objects) > 0 and obj_data["label"] not in zone.objects: if len(zone.objects) > 0 and obj_data["label"] not in zone.objects:
continue continue
contour = zone.contour contour = zone.contour
zone_score = self.zone_presence.get(name, 0) zone_score = self.zone_presence.get(name, 0) + 1
# check if the object is in the zone # check if the object is in the zone
if cv2.pointPolygonTest(contour, bottom_center, False) >= 0: if cv2.pointPolygonTest(contour, bottom_center, False) >= 0:
# if the object passed the filters once, dont apply again # if the object passed the filters once, dont apply again
if name in self.current_zones or not zone_filtered(self, zone.filters): if name in self.current_zones or not zone_filtered(self, zone.filters):
self.zone_presence[name] = zone_score + 1
# an object is only considered present in a zone if it has a zone inertia of 3+ # an object is only considered present in a zone if it has a zone inertia of 3+
if self.zone_presence[name] >= zone.inertia: if zone_score >= zone.inertia:
loitering_score = self.zone_loitering.get(name, 0) + 1
# loitering time is configured as seconds, convert to count of frames
if loitering_score >= (
self.camera_config.zones[name].loitering_time
* self.camera_config.detect.fps
):
current_zones.append(name) current_zones.append(name)
if name not in self.entered_zones: if name not in self.entered_zones:
self.entered_zones.append(name) self.entered_zones.append(name)
else:
self.zone_loitering[name] = loitering_score
else:
self.zone_presence[name] = zone_score
else: else:
# once an object has a zone inertia of 3+ it is not checked anymore # once an object has a zone inertia of 3+ it is not checked anymore
if 0 < zone_score < zone.inertia: if 0 < zone_score < zone.inertia: