add layout calculations

This commit is contained in:
Blake Blackshear 2021-06-04 07:09:16 -05:00
parent 8e2ba4a8ea
commit 4eed27e178

View File

@ -1,12 +1,14 @@
import datetime
import math
import multiprocessing as mp
import queue
import signal
import subprocess as sp
import threading
import numpy as np
from multiprocessing import shared_memory
from wsgiref.simple_server import make_server
import numpy as np
from setproctitle import setproctitle
from ws4py.server.wsgirefserver import (
WebSocketWSGIHandler,
@ -68,29 +70,80 @@ class BroadcastThread(threading.Thread):
class BirdsEyeFrameManager:
def __init__(self, height, width):
frame_shape = (height, width)
yuv_shape = (height * 3 // 2, width)
self.frame = np.ndarray(yuv_shape, dtype=np.uint8)
self.frame_shape = (height, width)
self.yuv_shape = (height * 3 // 2, width)
self.frame = np.ndarray(self.yuv_shape, dtype=np.uint8)
# initialize the frame as black and with the frigate logo
self.blank_frame = np.zeros(yuv_shape, np.uint8)
self.blank_frame = np.zeros(self.yuv_shape, np.uint8)
self.blank_frame[:] = 128
self.blank_frame[0 : frame_shape[0], 0 : frame_shape[1]] = 16
self.blank_frame[0 : self.frame_shape[0], 0 : self.frame_shape[1]] = 16
self.frame[:] = self.blank_frame
def update(self, camera, object_count, motion_count, frame_time, frame):
# determine how many cameras are tracking objects (or recently were)
# decide on a layout for the birdseye view (try to avoid too much churn)
# calculate position of each camera
self.last_active_frames = {}
self.camera_layout = []
def clear_frame(self):
self.frame[:] = self.blank_frame
def update(self, camera, object_count, motion_count, frame_time, frame) -> bool:
# maintain time of most recent active frame for each camera
if object_count > 0:
self.last_active_frames[camera] = frame_time
# TODO: avoid the remaining work if exceeding 5 fps and return False
# determine how many cameras are tracking objects within the last 30 seconds
now = datetime.datetime.now().timestamp()
active_cameras = [
cam
for cam, frame_time in self.last_active_frames.items()
if now - frame_time < 30
]
if len(active_cameras) == 0 and len(self.camera_layout) == 0:
return False
# if the sqrt of the layout and the active cameras don't round to the same value,
# we need to resize the layout
if round(math.sqrt(len(active_cameras))) != round(
math.sqrt(len(self.camera_layout))
):
# decide on a layout for the birdseye view (try to avoid too much churn)
self.columns = math.ceil(math.sqrt(len(active_cameras)))
self.rows = round(math.sqrt(len(active_cameras)))
self.camera_layout = [None] * (self.columns * self.rows)
self.clear_frame()
# remove inactive cameras from the layout
self.camera_layout = [
cam if cam in active_cameras else None for cam in self.camera_layout
]
# place the active cameras in the layout
while len(active_cameras) > 0:
cam = active_cameras.pop()
if cam in self.camera_layout:
continue
# place camera in the first available spot in the layout
for i in range(0, len(self.camera_layout) - 1):
if self.camera_layout[i] is None:
self.camera_layout[i] = cam
break
# calculate resolution of each position in the layout
# if layout is changing, wipe the frame black again
# For each camera currently tracking objects (alphabetical):
width = self.frame_shape[1] / self.columns
height = self.frame_shape[0] / self.rows
# For each camera in the layout:
# - resize the current frame and copy into the birdseye view
# signal to birdseye process that the frame is ready to send
self.frame[:] = frame
return True
def output_frames(config, video_output_queue):
threading.current_thread().name = f"output"
@ -170,14 +223,14 @@ def output_frames(config, video_output_queue):
ws.environ["PATH_INFO"].endswith("birdseye")
for ws in websocket_server.manager
):
birdseye_manager.update(
if birdseye_manager.update(
camera,
len(current_tracked_objects),
len(motion_boxes),
frame_time,
frame,
)
converters["birdseye"].write(birdseye_manager.frame.tobytes())
):
converters["birdseye"].write(birdseye_manager.frame.tobytes())
if camera in previous_frames:
frame_manager.delete(previous_frames[camera])