class ProcessClip: def __init__(self, clip_path, frame_shape, config: FrigateConfig): self.clip_path = clip_path self.camera_name = "camera" self.config = config self.camera_config = self.config.cameras["camera"] self.frame_shape = self.camera_config.frame_shape self.ffmpeg_cmd = [ c["cmd"] for c in self.camera_config.ffmpeg_cmds if "detect" in c["roles"] ][0] self.frame_manager = SharedMemoryFrameManager() self.frame_queue = mp.Queue() self.detected_objects_queue = mp.Queue() self.camera_state = CameraState(self.camera_name, config, self.frame_manager) def load_frames(self): fps = EventsPerSecond() skipped_fps = EventsPerSecond() current_frame = mp.Value("d", 0.0) frame_size = (self.camera_config.frame_shape_yuv[0] * self.camera_config.frame_shape_yuv[1]) ffmpeg_process = start_or_restart_ffmpeg(self.ffmpeg_cmd, logger, sp.DEVNULL, frame_size) capture_frames( ffmpeg_process, self.camera_name, self.camera_config.frame_shape_yuv, self.frame_manager, self.frame_queue, fps, skipped_fps, current_frame, ) ffmpeg_process.wait() ffmpeg_process.communicate() def process_frames(self, objects_to_track=["person"], object_filters={}): mask = np.zeros((self.frame_shape[0], self.frame_shape[1], 1), np.uint8) mask[:] = 255 motion_detector = MotionDetector(self.frame_shape, mask, self.camera_config.motion) object_detector = LocalObjectDetector(labels="/labelmap.txt") object_tracker = ObjectTracker(self.camera_config.detect) process_info = { "process_fps": mp.Value("d", 0.0), "detection_fps": mp.Value("d", 0.0), "detection_frame": mp.Value("d", 0.0), } stop_event = mp.Event() model_shape = (self.config.model.height, self.config.model.width) process_frames( self.camera_name, self.frame_queue, self.frame_shape, model_shape, self.frame_manager, motion_detector, object_detector, object_tracker, self.detected_objects_queue, process_info, objects_to_track, object_filters, mask, stop_event, exit_on_empty=True, ) def top_object(self, debug_path=None): obj_detected = False top_computed_score = 0.0 def handle_event(name, obj, frame_time): nonlocal obj_detected nonlocal top_computed_score if obj.computed_score > top_computed_score: top_computed_score = obj.computed_score if not obj.false_positive: obj_detected = True self.camera_state.on("new", handle_event) self.camera_state.on("update", handle_event) while not self.detected_objects_queue.empty(): ( camera_name, frame_time, current_tracked_objects, motion_boxes, regions, ) = self.detected_objects_queue.get() if not debug_path is None: self.save_debug_frame(debug_path, frame_time, current_tracked_objects.values()) self.camera_state.update(frame_time, current_tracked_objects, motion_boxes, regions) self.frame_manager.delete(self.camera_state.previous_frame_id) return { "object_detected": obj_detected, "top_score": top_computed_score } def save_debug_frame(self, debug_path, frame_time, tracked_objects): current_frame = cv2.cvtColor( self.frame_manager.get(f"{self.camera_name}{frame_time}", self.camera_config.frame_shape_yuv), cv2.COLOR_YUV2BGR_I420, ) # draw the bounding boxes on the frame for obj in tracked_objects: thickness = 2 color = (0, 0, 175) if obj["frame_time"] != frame_time: thickness = 1 color = (255, 0, 0) else: color = (255, 255, 0) # draw the bounding boxes on the frame box = obj["box"] draw_box_with_label( current_frame, box[0], box[1], box[2], box[3], obj["id"], f"{int(obj['score']*100)}% {int(obj['area'])}", thickness=thickness, color=color, ) # draw the regions on the frame region = obj["region"] draw_box_with_label( current_frame, region[0], region[1], region[2], region[3], "region", "", thickness=1, color=(0, 255, 0), ) cv2.imwrite( f"{os.path.join(debug_path, os.path.basename(self.clip_path))}.{int(frame_time*1000000)}.jpg", current_frame, )
def output_frames(config: FrigateConfig, video_output_queue): threading.current_thread().name = f"output" setproctitle(f"frigate.output") stop_event = mp.Event() def receiveSignal(signalNumber, frame): stop_event.set() signal.signal(signal.SIGTERM, receiveSignal) signal.signal(signal.SIGINT, receiveSignal) frame_manager = SharedMemoryFrameManager() previous_frames = {} # start a websocket server on 8082 WebSocketWSGIHandler.http_version = "1.1" websocket_server = make_server( "127.0.0.1", 8082, server_class=WSGIServer, handler_class=WebSocketWSGIRequestHandler, app=WebSocketWSGIApplication(handler_cls=WebSocket), ) websocket_server.initialize_websockets_manager() websocket_thread = threading.Thread(target=websocket_server.serve_forever) converters = {} broadcasters = {} for camera, cam_config in config.cameras.items(): width = int(cam_config.live.height * (cam_config.frame_shape[1] / cam_config.frame_shape[0])) converters[camera] = FFMpegConverter( cam_config.frame_shape[1], cam_config.frame_shape[0], width, cam_config.live.height, cam_config.live.quality, ) broadcasters[camera] = BroadcastThread(camera, converters[camera], websocket_server) if config.birdseye.enabled: converters["birdseye"] = FFMpegConverter( config.birdseye.width, config.birdseye.height, config.birdseye.width, config.birdseye.height, config.birdseye.quality, ) broadcasters["birdseye"] = BroadcastThread("birdseye", converters["birdseye"], websocket_server) websocket_thread.start() for t in broadcasters.values(): t.start() birdseye_manager = BirdsEyeFrameManager(config, frame_manager) while not stop_event.is_set(): try: ( camera, frame_time, current_tracked_objects, motion_boxes, regions, ) = video_output_queue.get(True, 10) except queue.Empty: continue frame_id = f"{camera}{frame_time}" frame = frame_manager.get(frame_id, config.cameras[camera].frame_shape_yuv) # send camera frame to ffmpeg process if websockets are connected if any(ws.environ["PATH_INFO"].endswith(camera) for ws in websocket_server.manager): # write to the converter for the camera if clients are listening to the specific camera converters[camera].write(frame.tobytes()) # update birdseye if websockets are connected if config.birdseye.enabled and any( ws.environ["PATH_INFO"].endswith("birdseye") for ws in websocket_server.manager): if birdseye_manager.update( camera, len(current_tracked_objects), len(motion_boxes), frame_time, frame, ): converters["birdseye"].write(birdseye_manager.frame.tobytes()) if camera in previous_frames: frame_manager.delete(f"{camera}{previous_frames[camera]}") previous_frames[camera] = frame_time while not video_output_queue.empty(): ( camera, frame_time, current_tracked_objects, motion_boxes, regions, ) = video_output_queue.get(True, 10) frame_id = f"{camera}{frame_time}" frame = frame_manager.get(frame_id, config.cameras[camera].frame_shape_yuv) frame_manager.delete(frame_id) for c in converters.values(): c.exit() for b in broadcasters.values(): b.join() websocket_server.manager.close_all() websocket_server.manager.stop() websocket_server.manager.join() websocket_server.shutdown() websocket_thread.join() logger.info("exiting output process...")