Update region grid on startup

This commit is contained in:
Nick Mowen 2023-10-17 06:50:25 -06:00
parent de8232f722
commit 01da588fdf
3 changed files with 70 additions and 28 deletions

View File

@ -36,7 +36,7 @@ from frigate.events.external import ExternalEventProcessor
from frigate.events.maintainer import EventProcessor
from frigate.http import create_app
from frigate.log import log_process, root_configurer
from frigate.models import Event, Recordings, RecordingsToDelete, Timeline
from frigate.models import Event, Recordings, RecordingsToDelete, Regions, Timeline
from frigate.object_detection import ObjectDetectProcess
from frigate.object_processing import TrackedObjectProcessor
from frigate.output import output_frames
@ -329,7 +329,7 @@ class FrigateApp:
60, 10 * len([c for c in self.config.cameras.values() if c.enabled])
),
)
models = [Event, Recordings, RecordingsToDelete, Timeline]
models = [Event, Recordings, RecordingsToDelete, Regions, Timeline]
self.db.bind(models)
def init_stats(self) -> None:

View File

@ -1,13 +1,15 @@
"""Utils for reading and writing object detection data."""
import datetime
import logging
import cv2
import numpy as np
from peewee import DoesNotExist
from frigate.config import CameraConfig, ModelConfig
from frigate.detectors.detector_config import PixelFormatEnum
from frigate.models import Timeline
from frigate.models import Event, Regions, Timeline
from frigate.util.image import (
calculate_region,
yuv_region_2_bgr,
@ -17,19 +19,40 @@ from frigate.util.image import (
logger = logging.getLogger(__name__)
GRID_SIZE = 8
def get_camera_regions_grid(
camera: CameraConfig, grid_size: int = 8
) -> list[list[dict[str, any]]]:
def get_camera_regions_grid(camera: CameraConfig) -> list[list[dict[str, any]]]:
"""Build a grid of expected region sizes for a camera."""
# create a grid
# get grid from db if available
try:
regions: Regions = Regions.select().where(Regions.camera == camera.name).get()
logger.error(f"The existing grid for is {regions.grid}")
grid = regions.grid
last_update = regions.last_update
except DoesNotExist:
grid = []
for x in range(grid_size):
for x in range(GRID_SIZE):
row = []
for y in range(grid_size):
for y in range(GRID_SIZE):
row.append({"sizes": []})
grid.append(row)
last_update = 0
# get events for timeline entries
events = (
Event.select(Event.id)
.where(Event.camera == camera.name)
.where(Event.false_positive != True)
.where(Event.start_time > last_update)
)
valid_event_ids = [e["id"] for e in events.dicts()]
# no new events, return as is
if not valid_event_ids:
return grid
new_update = datetime.datetime.now().timestamp()
timeline = (
Timeline.select(
*[
@ -38,20 +61,15 @@ def get_camera_regions_grid(
Timeline.data,
]
)
.where(Timeline.camera == camera.name)
.where(Timeline.source_id << valid_event_ids)
.limit(10000)
.dicts()
)
if not timeline:
return grid
logger.debug(f"There are {len(timeline)} entries for {camera.name}")
width = camera.detect.width
height = camera.detect.height
logger.debug(f"The size of grid is {len(grid)} x {len(grid[grid_size - 1])}")
grid_coef = 1.0 / grid_size
grid_coef = 1.0 / GRID_SIZE
for t in timeline:
if t.get("source") != "tracked_object":
@ -63,8 +81,8 @@ def get_camera_regions_grid(
x = box[0] + (box[2] / 2)
y = box[1] + (box[3] / 2)
x_pos = int(x * grid_size)
y_pos = int(y * grid_size)
x_pos = int(x * GRID_SIZE)
y_pos = int(y * GRID_SIZE)
calculated_region = calculate_region(
(height, width),
@ -80,8 +98,8 @@ def get_camera_regions_grid(
(calculated_region[2] - calculated_region[0]) / width
)
for x in range(grid_size):
for y in range(grid_size):
for x in range(GRID_SIZE):
for y in range(GRID_SIZE):
cell = grid[x][y]
logger.debug(
f"stats for cell {x * grid_coef * width},{y * grid_coef * height} -> {(x + 1) * grid_coef * width},{(y + 1) * grid_coef * height} :: {len(cell['sizes'])} objects"
@ -96,6 +114,21 @@ def get_camera_regions_grid(
cell["std_dev"] = std_dev
cell["mean"] = mean
# update db with new grid
region = {
Regions.camera: camera.name,
Regions.grid: grid,
Regions.last_update: new_update,
}
(
Regions.insert(region)
.on_conflict(
conflict_target=[Regions.camera],
update=region,
)
.execute()
)
return grid
@ -121,8 +154,13 @@ def get_region_from_grid(
region_grid: list[list[dict[str, any]]],
) -> list[int]:
"""Get a region for a box based on the region grid."""
box = calculate_region(frame_shape, cluster[0], cluster[1], cluster[2], cluster[3], min_region)
centroid = (box[0] + (min(frame_shape[1], box[2]) - box[0]) / 2, box[1] + (min(frame_shape[0], box[3]) - box[1]) / 2)
box = calculate_region(
frame_shape, cluster[0], cluster[1], cluster[2], cluster[3], min_region
)
centroid = (
box[0] + (min(frame_shape[1], box[2]) - box[0]) / 2,
box[1] + (min(frame_shape[0], box[3]) - box[1]) / 2,
)
grid_x = int(centroid[0] / frame_shape[1] * len(region_grid))
grid_y = int(centroid[1] / frame_shape[0] * len(region_grid))
@ -135,7 +173,11 @@ def get_region_from_grid(
calc_size = (box[2] - box[0]) / frame_shape[1]
# if region is within expected size, don't resize
if (cell["mean"] - cell["std_dev"]) <= calc_size <= (cell["mean"] + cell["std_dev"]):
if (
(cell["mean"] - cell["std_dev"])
<= calc_size
<= (cell["mean"] + cell["std_dev"])
):
return box
# TODO not sure how to handle case where cluster is larger than expected region
elif calc_size > (cell["mean"] + cell["std_dev"]):

View File

@ -27,7 +27,7 @@ SQL = pw.SQL
def migrate(migrator, database, fake=False, **kwargs):
migrator.sql(
'CREATE TABLE IF NOT EXISTS "regions" ("camera" VARCHAR(20) NOT NULL PRIMARY KEY, "last_updated" DATETIME NOT NULL, "grid" JSON)'
'CREATE TABLE IF NOT EXISTS "regions" ("camera" VARCHAR(20) NOT NULL PRIMARY KEY, "last_update" DATETIME NOT NULL, "grid" JSON)'
)