Convert camera tracking to process

This commit is contained in:
Nicolas Mowen 2025-06-12 11:22:15 -06:00
parent bd2cc18260
commit 4b913953a1
3 changed files with 91 additions and 93 deletions

View File

@ -22,7 +22,7 @@ from frigate.util import Process as FrigateProcess
from frigate.util.builtin import empty_and_close_queue from frigate.util.builtin import empty_and_close_queue
from frigate.util.image import SharedMemoryFrameManager, UntrackedSharedMemory from frigate.util.image import SharedMemoryFrameManager, UntrackedSharedMemory
from frigate.util.object import get_camera_regions_grid from frigate.util.object import get_camera_regions_grid
from frigate.video import capture_camera, track_camera from frigate.video import CameraTracker, capture_camera
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -155,21 +155,15 @@ class CameraMaintainer(threading.Thread):
except FileExistsError: except FileExistsError:
pass pass
camera_process = FrigateProcess( camera_process = CameraTracker(
target=track_camera, config,
name=f"camera_processor:{name}", self.config.model,
args=( self.config.model.merged_labelmap,
config.name, self.detection_queue,
config, self.detected_frames_queue,
self.config.model, self.camera_metrics[name],
self.config.model.merged_labelmap, self.ptz_metrics[name],
self.detection_queue, self.region_grids[name],
self.detected_frames_queue,
self.camera_metrics[name],
self.ptz_metrics[name],
self.region_grids[name],
),
daemon=True,
) )
self.camera_processes[config.name] = camera_process self.camera_processes[config.name] = camera_process
camera_process.start() camera_process.start()

View File

@ -12,8 +12,6 @@ from typing import Any, Tuple
import numpy as np import numpy as np
import frigate.util as util import frigate.util as util
from frigate.camera import CameraMetrics
from logging.handlers import QueueHandler
from frigate.comms.detections_updater import DetectionPublisher, DetectionTypeEnum from frigate.comms.detections_updater import DetectionPublisher, DetectionTypeEnum
from frigate.comms.event_metadata_updater import ( from frigate.comms.event_metadata_updater import (
EventMetadataPublisher, EventMetadataPublisher,

View File

@ -14,6 +14,7 @@ from typing import Any
import cv2 import cv2
from setproctitle import setproctitle from setproctitle import setproctitle
import frigate.util as util
from frigate.camera import CameraMetrics, PTZMetrics from frigate.camera import CameraMetrics, PTZMetrics
from frigate.comms.inter_process import InterProcessRequestor from frigate.comms.inter_process import InterProcessRequestor
from frigate.config import CameraConfig, DetectConfig, ModelConfig from frigate.config import CameraConfig, DetectConfig, ModelConfig
@ -53,7 +54,6 @@ from frigate.util.object import (
is_object_filtered, is_object_filtered,
reduce_detections, reduce_detections,
) )
from frigate.util.services import listen
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -467,76 +467,79 @@ def capture_camera(
camera_watchdog.join() camera_watchdog.join()
def track_camera( class CameraTracker(util.Process):
name, def __init__(
config: CameraConfig, self,
model_config: ModelConfig, config: CameraConfig,
labelmap: dict[int, str], model_config: ModelConfig,
detection_queue: Queue, labelmap: dict[int, str],
detected_objects_queue, detection_queue: Queue,
camera_metrics: CameraMetrics,
ptz_metrics: PTZMetrics,
region_grid: list[list[dict[str, Any]]],
):
stop_event = mp.Event()
def receiveSignal(signalNumber, frame):
stop_event.set()
signal.signal(signal.SIGTERM, receiveSignal)
signal.signal(signal.SIGINT, receiveSignal)
threading.current_thread().name = f"process:{name}"
setproctitle(f"frigate.process:{name}")
listen()
frame_queue = camera_metrics.frame_queue
frame_shape = config.frame_shape
motion_detector = ImprovedMotionDetector(
frame_shape,
config.motion,
config.detect.fps,
name=config.name,
ptz_metrics=ptz_metrics,
)
object_detector = RemoteObjectDetector(
name, labelmap, detection_queue, model_config, stop_event
)
object_tracker = NorfairTracker(config, ptz_metrics)
frame_manager = SharedMemoryFrameManager()
# create communication for region grid updates
requestor = InterProcessRequestor()
process_frames(
name,
requestor,
frame_queue,
frame_shape,
model_config,
config,
frame_manager,
motion_detector,
object_detector,
object_tracker,
detected_objects_queue, detected_objects_queue,
camera_metrics, camera_metrics: CameraMetrics,
stop_event, ptz_metrics: PTZMetrics,
ptz_metrics, region_grid: list[list[dict[str, Any]]],
region_grid, ) -> None:
) super().__init__(name=f"camera_processor:{config.name}", daemon=True)
self.config = config
self.model_config = model_config
self.labelmap = labelmap
self.detection_queue = detection_queue
self.detected_objects_queue = detected_objects_queue
self.camera_metrics = camera_metrics
self.ptz_metrics = ptz_metrics
self.region_grid = region_grid
# empty the frame queue def run(self) -> None:
logger.info(f"{name}: emptying frame queue") self.pre_run_setup()
while not frame_queue.empty(): frame_queue = self.camera_metrics.frame_queue
(frame_name, _) = frame_queue.get(False) frame_shape = self.config.frame_shape
frame_manager.delete(frame_name)
logger.info(f"{name}: exiting subprocess") motion_detector = ImprovedMotionDetector(
frame_shape,
self.config.motion,
self.config.detect.fps,
name=self.config.name,
ptz_metrics=self.ptz_metrics,
)
object_detector = RemoteObjectDetector(
self.config.name,
self.labelmap,
self.detection_queue,
self.model_config,
self.stop_event,
)
object_tracker = NorfairTracker(self.config, self.ptz_metrics)
frame_manager = SharedMemoryFrameManager()
# create communication for region grid updates
requestor = InterProcessRequestor()
process_frames(
requestor,
frame_queue,
frame_shape,
self.model_config,
self.config,
frame_manager,
motion_detector,
object_detector,
object_tracker,
self.detected_objects_queue,
self.camera_metrics,
self.stop_event,
self.ptz_metrics,
self.region_grid,
)
# empty the frame queue
logger.info(f"{self.config.name}: emptying frame queue")
while not frame_queue.empty():
(frame_name, _) = frame_queue.get(False)
frame_manager.delete(frame_name)
logger.info(f"{self.config.name}: exiting subprocess")
def detect( def detect(
@ -577,7 +580,6 @@ def detect(
def process_frames( def process_frames(
camera_name: str,
requestor: InterProcessRequestor, requestor: InterProcessRequestor,
frame_queue: Queue, frame_queue: Queue,
frame_shape: tuple[int, int], frame_shape: tuple[int, int],
@ -597,7 +599,7 @@ def process_frames(
next_region_update = get_tomorrow_at_time(2) next_region_update = get_tomorrow_at_time(2)
config_subscriber = CameraConfigUpdateSubscriber( config_subscriber = CameraConfigUpdateSubscriber(
None, None,
{camera_name: camera_config}, {camera_config.name: camera_config},
[ [
CameraConfigUpdateEnum.detect, CameraConfigUpdateEnum.detect,
CameraConfigUpdateEnum.enabled, CameraConfigUpdateEnum.enabled,
@ -653,7 +655,9 @@ def process_frames(
and prev_enabled != camera_enabled and prev_enabled != camera_enabled
and camera_metrics.frame_queue.empty() and camera_metrics.frame_queue.empty()
): ):
logger.debug(f"Camera {camera_name} disabled, clearing tracked objects") logger.debug(
f"Camera {camera_config.name} disabled, clearing tracked objects"
)
prev_enabled = camera_enabled prev_enabled = camera_enabled
# Clear norfair's dictionaries # Clear norfair's dictionaries
@ -678,7 +682,7 @@ def process_frames(
datetime.datetime.now().astimezone(datetime.timezone.utc) datetime.datetime.now().astimezone(datetime.timezone.utc)
> next_region_update > next_region_update
): ):
region_grid = requestor.send_data(REQUEST_REGION_GRID, camera_name) region_grid = requestor.send_data(REQUEST_REGION_GRID, camera_config.name)
next_region_update = get_tomorrow_at_time(2) next_region_update = get_tomorrow_at_time(2)
try: try:
@ -698,7 +702,9 @@ def process_frames(
frame = frame_manager.get(frame_name, (frame_shape[0] * 3 // 2, frame_shape[1])) frame = frame_manager.get(frame_name, (frame_shape[0] * 3 // 2, frame_shape[1]))
if frame is None: if frame is None:
logger.debug(f"{camera_name}: frame {frame_time} is not in memory store.") logger.debug(
f"{camera_config.name}: frame {frame_time} is not in memory store."
)
continue continue
# look for motion if enabled # look for motion if enabled
@ -937,7 +943,7 @@ def process_frames(
) )
cv2.imwrite( cv2.imwrite(
f"debug/frames/{camera_name}-{'{:.6f}'.format(frame_time)}.jpg", f"debug/frames/{camera_config.name}-{'{:.6f}'.format(frame_time)}.jpg",
bgr_frame, bgr_frame,
) )
# add to the queue if not full # add to the queue if not full
@ -949,7 +955,7 @@ def process_frames(
camera_metrics.process_fps.value = fps_tracker.eps() camera_metrics.process_fps.value = fps_tracker.eps()
detected_objects_queue.put( detected_objects_queue.put(
( (
camera_name, camera_config.name,
frame_name, frame_name,
frame_time, frame_time,
detections, detections,