def filter_fov(self, frame): """Filter field of view.""" objects_in_fov = [] labels_in_fov = [] for obj in frame.objects: if self._object_filters.get(obj.label) and self._object_filters[ obj.label].filter_object(obj): obj.relevant = True objects_in_fov.append(obj) labels_in_fov.append(obj.label) if self._object_filters[obj.label].triggers_recording: obj.trigger_recorder = True if self._object_filters[obj.label].post_processor: DataStream.publish_data( (f"{self._post_processor_topic}/" f"{self._object_filters[obj.label].post_processor}"), { "camera_config": self.config, "frame": frame, "object": obj, "zone": None, }, ) self.objects_in_fov = objects_in_fov self.labels_in_fov = labels_in_fov
def __init__( self, logger, config, interval, stream: Stream, decode_error, width, height, topic_decode, topic_scan, ): self._logger = logger self._config = config self.interval = interval self.interval_calculated = round(interval * stream.fps) self.decode_error = False self.scan = Event() self._frame_number = 0 self._decode_error = decode_error self._width = width self._height = height self._decoder_queue: Queue = Queue(maxsize=5) self._topic_scan = f"{config.camera.name_slug}/{topic_scan}" self._topic_decode = f"{config.camera.name_slug}/{topic_decode}" DataStream.subscribe_data(self._topic_decode, self._decoder_queue) decode_thread = Thread(target=self.decode_frame) decode_thread.daemon = True decode_thread.start()
def motion_detection(self): """Perform motion detection and publish the results.""" while True: frame_to_scan: FrameToScan = self._motion_detection_queue.get() frame_to_scan.frame.motion_contours = self._motion_detector.detect( frame_to_scan) DataStream.publish_data(self.topic_processed_motion, frame_to_scan)
def filter_zone(self, frame: Frame): """Filter out objects to see if they are within the zone.""" objects_in_zone = [] labels_in_zone = [] for obj in frame.objects: if self._object_filters.get(obj.label) and self._object_filters[ obj.label].filter_object(obj): if helpers.object_in_polygon(self._camera_resolution, obj, self.coordinates): obj.relevant = True objects_in_zone.append(obj) if obj.label not in labels_in_zone: labels_in_zone.append(obj.label) if self._object_filters[obj.label].trigger_recorder: obj.trigger_recorder = True if self._object_filters[obj.label].post_processor: DataStream.publish_data( (f"{self._post_processor_topic}/" f"{self._object_filters[obj.label].post_processor}" ), PostProcessorFrame(self._config, frame, obj, self), ) self.objects_in_zone = objects_in_zone self.labels_in_zone = labels_in_zone
def __init__(self, object_detection_config): detector = importlib.import_module("viseron.detector." + object_detection_config["type"]) config = detector.Config(detector.SCHEMA(object_detection_config)) if getattr(config.logging, "level", None): LOGGER.setLevel(config.logging.level) LOGGER.debug( f"Initializing object detector {object_detection_config['type']}") self.config = config self.detection_lock = Lock() # Activate OpenCL if cv2.ocl.haveOpenCL(): LOGGER.debug("OpenCL activated") cv2.ocl.setUseOpenCL(True) self.object_detector = detector.ObjectDetection(config) self._topic_scan_object = f"*/{TOPIC_FRAME_SCAN_OBJECT}" self._object_detection_queue = Queue() object_detection_thread = Thread(target=self.object_detection) object_detection_thread.daemon = True object_detection_thread.start() DataStream.subscribe_data(self._topic_scan_object, self._object_detection_queue) LOGGER.debug("Object detector initialized")
def __init__( self, config: ViseronConfig, processor_type: str, processor_config: Dict[str, Any], ): if getattr(config.post_processors.logging, "level", None): LOGGER.setLevel(config.post_processors.logging.level) processor = self.import_processor(processor_type, processor_config) self._post_processor = processor.Processor( # type: ignore config, processor.Config( # type: ignore processor.SCHEMA(processor_config), # type: ignore ), ) self._topic_scan = f"*/{TOPIC_FRAME_SCAN_POSTPROC}/{processor_type}" self._post_processor_queue: Queue = Queue(maxsize=10) processor_thread = RestartableThread( name=__name__, target=self.post_process, daemon=True, register=True ) processor_thread.start() DataStream.subscribe_data(self._topic_scan, self._post_processor_queue) self.post_processor_list.append(self) LOGGER.debug(f"Post processor {processor_type} initialized")
async def get(self, camera, mjpeg_stream): """Handle GET request.""" nvr = FFMPEGNVR.nvr_list.get(camera, None) if not nvr: self.set_status(404) self.write(f"Camera {camera} not found.") self.finish() return mjpeg_stream_config = nvr.config.camera.static_mjpeg_streams.get( mjpeg_stream, None ) if not mjpeg_stream_config: self.set_status(404) self.write(f"Stream {mjpeg_stream} not defined.") self.finish() return frame_queue = Queue(maxsize=10) frame_topic = ( f"{TOPIC_STATIC_MJPEG_STREAMS}/{nvr.config.camera.name_slug}/{mjpeg_stream}" ) unique_id = DataStream.subscribe_data(frame_topic, frame_queue) if self.active_streams.get(mjpeg_stream, False): self.active_streams[mjpeg_stream] += 1 LOGGER.debug( "Stream {mjpeg_stream} already active, number of streams: " f"{self.active_streams[mjpeg_stream]}" ) else: LOGGER.debug(f"Stream {mjpeg_stream} is not active, starting") self.active_streams[mjpeg_stream] = 1 tornado.ioloop.IOLoop.current().spawn_callback( lambda: self.stream( nvr, mjpeg_stream, mjpeg_stream_config, frame_topic ), ) self.set_header( "Content-Type", "multipart/x-mixed-replace;boundary=--jpgboundary" ) self.set_header("Connection", "close") while True: try: jpg = await frame_queue.get() self.write("--jpgboundary") self.write("Content-type: image/jpeg\r\n") self.write("Content-length: %s\r\n\r\n" % len(jpg)) self.write(jpg.tobytes()) await self.flush() except tornado.iostream.StreamClosedError: DataStream.unsubscribe_data(frame_topic, unique_id) LOGGER.debug( f"Stream {mjpeg_stream} closed for camera " f"{nvr.config.camera.name_slug}" ) break self.active_streams[mjpeg_stream] -= 1
def object_detection(self): """Perform object detection and publish the results.""" while True: frame = self._object_detection_queue.get() self.detection_lock.acquire() frame["frame"].objects = self.object_detector.return_objects(frame) self.detection_lock.release() DataStream.publish_data( (f"{frame['camera_config'].camera.name_slug}/" f"{TOPIC_FRAME_PROCESSED_OBJECT}"), frame, )
def decode_frame(self): """Decodes the frame, leaves any other potential keys in the dict untouched.""" self._logger.debug("Starting decoder thread") while True: frame = self._decoder_queue.get() if frame["frame"].decode_frame(): frame["frame"].resize( frame["decoder_name"], frame["width"], frame["height"] ) DataStream.publish_data(self._topic_scan, frame) continue self._decode_error.set() self._logger.error("Unable to decode frame. FFmpeg pipe seems broken")
def decode_frame(self): """Decode received frame from scan_frame.""" self._logger.debug("Starting decoder thread") while True: frame_to_scan: FrameToScan = self._decoder_queue.get() if frame_to_scan.frame.decode_frame(): if self._preprocessor_callback: self._preprocessor_callback(frame_to_scan) DataStream.publish_data(self._topic_scan, frame_to_scan) continue self._decode_error.set() self._logger.error( "Unable to decode frame. FFmpeg pipe seems broken")
def __init__( self, logger: logging.Logger, config: NVRConfig, name: str, interval: float, stream: Stream, decode_error: Event, topic_decode: str, topic_scan: str, preprocess_callback: Callable = None, ): self._logger = logger self._config = config self.name = name self.interval = interval self._interval_fps = None self._stream = stream self.decode_error = False self.scan = Event() self._frame_number = 0 self._decode_error = decode_error self._decoder_queue: Queue = Queue(maxsize=5) self._preprocessor_callback = preprocess_callback self._topic_scan = f"{config.camera.name_slug}/{topic_scan}" self._topic_decode = f"{config.camera.name_slug}/{topic_decode}" DataStream.subscribe_data(self._topic_decode, self._decoder_queue) decode_thread = RestartableThread( name=__name__ + "." + config.camera.name_slug, target=self.decode_frame, daemon=True, register=True, ) decode_thread.start() if stream.decoders.get(name, None): raise DuplicateDecoderName(name) stream.decoders[name] = self stream.calculate_output_fps() for decoder in stream.decoders.values(): decoder.calculate_interval( ) # Re-calculate interval for all decoders self._logger.debug(f"Running decoder {name} at {interval}s interval, " f"every {interval * stream.fps} frame(s)")
def capture_pipe(self): """Start capturing frames from camera.""" self._logger.debug("Starting capture thread") self._connected = True self.decode_error.clear() self._poll_timer[0] = datetime.datetime.now().timestamp() empty_frames = 0 self.stream.start_pipe() if self._segments: self._segments.start_pipe() while self._connected: if self.decode_error.is_set(): DataStream.publish_data( f"{self._config.camera.name_slug}/status", "disconnected") sleep(5) self._logger.error("Restarting frame pipe") self.stream.close_pipe() self.stream.start_pipe() self.decode_error.clear() empty_frames = 0 current_frame = self.stream.read() if current_frame: empty_frames = 0 self._poll_timer[0] = datetime.datetime.now().timestamp() for decoder in self.stream.decoders.values(): decoder.scan_frame(current_frame) self.frame_ready.set() self.frame_ready.clear() continue if self.stream.poll is not None: self._logger.error("FFmpeg process has exited") self.decode_error.set() continue empty_frames += 1 if empty_frames >= 10: self._logger.error("Did not receive a frame") self.decode_error.set() self.stream.close_pipe() if self._segments: self._segments.close_pipe() self._logger.info("FFMPEG frame grabber stopped")
def object_detection(self): """Perform object detection and publish the results.""" while True: frame_to_scan: FrameToScan = self._object_detection_queue.get() if (frame_age := time.time() - frame_to_scan.capture_time ) > frame_to_scan.camera_config.object_detection.max_frame_age: LOGGER.debug(f"Frame is {frame_age} seconds old for " f"{frame_to_scan.decoder_name}. Discarding") continue frame_to_scan.frame.objects = self.object_detector.return_objects( frame_to_scan) DataStream.publish_data( (f"{frame_to_scan.camera_config.camera.name_slug}/" f"{TOPIC_FRAME_PROCESSED_OBJECT}"), frame_to_scan, )
def stream(self, nvr, mjpeg_stream, mjpeg_stream_config, publish_frame_topic): """Subscribe to frames, draw on them, then publish processed frame.""" frame_queue = Queue(maxsize=10) subscribe_frame_topic = ( f"{nvr.config.camera.name_slug}/{TOPIC_FRAME_PROCESSED}/*" ) unique_id = DataStream.subscribe_data(subscribe_frame_topic, frame_queue) while self.active_streams[mjpeg_stream]: item = yield frame_queue.get() frame = copy.copy(item.frame) ret, jpg = yield self.process_frame(nvr, frame, mjpeg_stream_config) if ret: DataStream.publish_data(publish_frame_topic, jpg) DataStream.unsubscribe_data(subscribe_frame_topic, unique_id) LOGGER.debug(f"Closing stream {mjpeg_stream}")
def scan_frame(self, current_frame): """Publish frame if marked for scanning.""" if self.scan.is_set(): if self._frame_number % self.interval_calculated == 0: self._frame_number = 0 DataStream.publish_data( self._topic_decode, { "decoder_name": self, "frame": current_frame, "width": self._width, "height": self._height, "camera_config": self._config, }, ) self._frame_number += 1 else: self._frame_number = 0
def filter_zone(self, frame): """Filter out objects to see if they are within the zone.""" objects_in_zone = [] labels_in_zone = [] for obj in frame.objects: if self._object_filters.get(obj.label) and self._object_filters[ obj.label].filter_object(obj): x1, _, x2, y2 = calculate_absolute_coords( ( obj.rel_x1, obj.rel_y1, obj.rel_x2, obj.rel_y2, ), self._camera_resolution, ) middle = ((x2 - x1) / 2) + x1 if cv2.pointPolygonTest(self.coordinates, (middle, y2), False) >= 0: obj.relevant = True objects_in_zone.append(obj) if obj.label not in labels_in_zone: labels_in_zone.append(obj.label) if self._object_filters[obj.label].triggers_recording: obj.trigger_recorder = True if self._object_filters[obj.label].post_processor: DataStream.publish_data( (f"{self._post_processor_topic}/" f"{self._object_filters[obj.label].post_processor}" ), { "camera_config": self._config, "frame": frame, "object": obj, "zone": self._name, }, ) self.objects_in_zone = objects_in_zone self.labels_in_zone = labels_in_zone
async def get(self, camera): """Handle a GET request.""" request_arguments = { k: self.get_argument(k) for k in self.request.arguments } LOGGER.debug(request_arguments) mjpeg_stream_config = MJPEG_STREAM_SCHEMA(request_arguments) LOGGER.debug(mjpeg_stream_config) nvr = FFMPEGNVR.nvr_list.get(camera, None) if not nvr: self.set_status(404) self.write(f"Camera {camera} not found.") self.finish() return self.set_header("Content-Type", "multipart/x-mixed-replace;boundary=--jpgboundary") self.set_header("Connection", "close") frame_queue = Queue(maxsize=10) frame_topic = f"{nvr.config.camera.name_slug}/{TOPIC_FRAME_PROCESSED}/*" unique_id = DataStream.subscribe_data(frame_topic, frame_queue) while True: try: item = await frame_queue.get() frame = copy.copy(item["frame"]) ret, jpg = await self.process_frame(nvr, frame, mjpeg_stream_config) if ret: self.write("--jpgboundary") self.write("Content-type: image/jpeg\r\n") self.write("Content-length: %s\r\n\r\n" % len(jpg)) self.write(jpg.tobytes()) await self.flush() except tornado.iostream.StreamClosedError: DataStream.unsubscribe_data(frame_topic, unique_id) LOGGER.debug( f"Stream closed for camera {nvr.config.camera.name_slug}") break
def scan_frame(self, current_frame): """Publish frame if marked for scanning.""" if self.scan.is_set(): if self._frame_number % self._interval_fps == 0: self._frame_number = 0 DataStream.publish_data( self._topic_decode, FrameToScan( self.name, current_frame, self._stream.width, self._stream.height, self._config, time.time(), ), ) self._frame_number += 1 else: self._frame_number = 0
def __init__(self, config): self.config = config self._status_state = None self.status_attributes = {} self.devices = {} if viseron.mqtt.MQTT.client: self.devices["motion_detected"] = MQTTBinarySensor( config, "motion_detected") self.devices["object_detected"] = MQTTBinarySensor( config, "object_detected") for label in config.object_detection.labels: self.devices[label.label] = MQTTBinarySensor( config, f"object_detected {label.label}", ) self.devices["switch"] = MQTTSwitch(config) self.devices["camera"] = MQTTCamera(config) self.devices["sensor"] = MQTTSensor(config, "status") DataStream.subscribe_data(f"{config.camera.name_slug}/status", self.status_state_callback)
def filter_fov(self, frame): """Filter field of view.""" objects_in_fov = [] labels_in_fov = [] for obj in frame.objects: if self._object_filters.get(obj.label) and self._object_filters[ obj.label].filter_object(obj): obj.relevant = True objects_in_fov.append(obj) labels_in_fov.append(obj.label) if self._object_filters[obj.label].trigger_recorder: obj.trigger_recorder = True if self._object_filters[obj.label].post_processor: DataStream.publish_data( (f"{self._post_processor_topic}/" f"{self._object_filters[obj.label].post_processor}"), PostProcessorFrame(self.config, frame, obj), ) self.objects_in_fov = objects_in_fov self.labels_in_fov = labels_in_fov
def __init__(self, object_detection_config): # Config is not validated yet so we need to access the dictionary value if not object_detection_config["enable"]: return config_module, detector_module = import_object_detection( object_detection_config) config = config_module.Config( config_module.SCHEMA(object_detection_config)) if getattr(config.logging, "level", None): LOGGER.setLevel(config.logging.level) LOGGER.debug(f"Initializing object detector {config.type}") # Activate OpenCL if cv2.ocl.haveOpenCL(): LOGGER.debug("OpenCL activated") cv2.ocl.setUseOpenCL(True) self.object_detector = detector_module.ObjectDetection(config) self._topic_scan_object = f"*/{TOPIC_FRAME_SCAN_OBJECT}" self._object_detection_queue: Queue[ # pylint: disable=unsubscriptable-object FrameToScan] = Queue(maxsize=100) object_detection_thread = RestartableThread( target=self.object_detection, name="object_detection", register=True, daemon=True, ) object_detection_thread.daemon = True object_detection_thread.start() DataStream.subscribe_data(self._topic_scan_object, self._object_detection_queue) LOGGER.debug("Object detector initialized")
def __init__(self, config: ViseronConfig, processor_type, processor_config, mqtt_queue): self.post_processor_list.append(self) if getattr(config.post_processors.logging, "level", None): LOGGER.setLevel(config.post_processors.logging.level) LOGGER.debug(f"Initializing post processor {processor_type}") processor = self.import_processor(processor_type, processor_config) LOGGER.debug("Successfully imported post processor") self._post_processor = processor.Processor( config, processor.Config(config.post_processors, processor.SCHEMA(processor_config)), mqtt_queue, ) self._topic_scan = f"*/{TOPIC_FRAME_SCAN_POSTPROC_FACEREC}" self._post_processor_queue: Queue = Queue(maxsize=10) processor_thread = Thread(target=self.post_process) processor_thread.daemon = True processor_thread.start() DataStream.subscribe_data(self._topic_scan, self._post_processor_queue) LOGGER.debug(f"Post processor {processor_type} initialized")
def __init__(self, config, detector, mqtt_queue=None): Thread.__init__(self) self.setup_loggers(config) self._logger.debug("Initializing NVR thread") # Use FFMPEG to read from camera. Used for reading/recording self.camera = FFMPEGCamera(config, detector) self._mqtt = MQTT(config, mqtt_queue) self.config = config self.kill_received = False self.camera_grabber = None self._objects_in_fov = [] self._labels_in_fov = [] self._reported_label_count = {} self._object_return_queue = Queue(maxsize=10) self._object_filters = {} DataStream.subscribe_data( f"{config.camera.name_slug}/{TOPIC_FRAME_PROCESSED_OBJECT}", self._object_return_queue, ) for object_filter in config.object_detection.labels: self._object_filters[object_filter.label] = Filter(object_filter) self.zones = [] for zone in config.camera.zones: self.zones.append( Zone( zone, self.camera.resolution, config, self._mqtt.mqtt_queue, )) self._motion_frames = 0 self._motion_detected = False self._motion_only_frames = 0 self._motion_max_timeout_reached = False self._motion_return_queue = Queue(maxsize=5) if config.motion_detection.timeout or config.motion_detection.trigger_detector: self.motion_detector = MotionDetection(config, self.camera.resolution) DataStream.subscribe_data( self.motion_detector.topic_processed_motion, self._motion_return_queue) if config.motion_detection.trigger_detector: self.camera.decoders["motion_detection"].scan.set() self.camera.decoders["object_detection"].scan.clear() else: self.camera.decoders["object_detection"].scan.set() self.camera.decoders["motion_detection"].scan.clear() self.idle_frames = 0 self._post_processor_topic = ( f"{config.camera.name_slug}/{TOPIC_FRAME_SCAN_POSTPROC}") self.start_camera() # Initialize recorder self._start_recorder = False self.recorder = FFMPEGRecorder(config, detector.detection_lock, mqtt_queue) self.nvr_list[config.camera.name_slug] = self self._logger.debug("NVR thread initialized")
def __init__(self): config = ViseronConfig(CONFIG) log_settings(config) LOGGER.info("-------------------------------------------") LOGGER.info("Initializing...") webserver = WebServer() webserver.start() DataStream(webserver.ioloop) schedule_cleanup(config) mqtt_queue = None mqtt = None if config.mqtt: mqtt_queue = Queue(maxsize=100) mqtt = MQTT(config) mqtt_publisher = Thread(target=mqtt.publisher, args=(mqtt_queue, )) mqtt_publisher.daemon = True detector = Detector(config.object_detection) post_processors = {} for ( post_processor_type, post_processor_config, ) in config.post_processors.post_processors.items(): post_processors[post_processor_type] = PostProcessor( config, post_processor_type, post_processor_config, mqtt_queue) LOGGER.info("Initializing NVR threads") self.setup_threads = [] self.nvr_threads = [] for camera in config.cameras: setup_thread = Thread( target=self.setup_nvr, args=( config, camera, detector, mqtt_queue, ), ) setup_thread.start() self.setup_threads.append(setup_thread) for thread in self.setup_threads: thread.join() if mqtt: mqtt.connect() mqtt_publisher.start() for thread in self.nvr_threads: thread.start() LOGGER.info("Initialization complete") def signal_term(*_): LOGGER.info("Kill received! Sending kill to threads..") for thread in self.nvr_threads: thread.stop() for thread in self.nvr_threads: thread.join() # Listen to signals signal.signal(signal.SIGTERM, signal_term) signal.signal(signal.SIGINT, signal_term) try: for thread in self.nvr_threads: thread.join() except KeyboardInterrupt: LOGGER.info("Ctrl-C received! Sending kill to threads..") for thread in self.nvr_threads: thread.stop() for thread in self.nvr_threads: thread.join() LOGGER.info("Exiting") os._exit(1)
def __init__(self, config, detector): self.setup_loggers(config) self._logger.debug("Initializing NVR thread") # Use FFMPEG to read from camera. Used for reading/recording self.camera = FFMPEGCamera(config, detector) self._mqtt = MQTTInterface(config) self.config = config self.kill_received = False self.camera_grabber = None self._objects_in_fov = [] self._labels_in_fov = [] self._reported_label_count = {} self._object_return_queue = Queue(maxsize=10) self._object_filters = {} self._object_decoder = f"{config.camera.name_slug}.object_detection" DataStream.subscribe_data( f"{config.camera.name_slug}/{TOPIC_FRAME_PROCESSED_OBJECT}", self._object_return_queue, ) for object_filter in config.object_detection.labels: self._object_filters[object_filter.label] = Filter( config, self.camera.resolution, object_filter) self.zones: List[Zone] = [] for zone in config.camera.zones: self.zones.append(Zone( zone, self.camera.resolution, config, )) self._motion_frames = 0 self._motion_detected = False self._motion_only_frames = 0 self._motion_max_timeout_reached = False self._motion_return_queue = Queue(maxsize=5) self._motion_decoder = f"{config.camera.name_slug}.motion_detection" if config.motion_detection.timeout or config.motion_detection.trigger_detector: self.motion_detector = MotionDetection(config, self.camera) DataStream.subscribe_data( self.motion_detector.topic_processed_motion, self._motion_return_queue) if config.motion_detection.trigger_detector: self.camera.stream.decoders[self._motion_decoder].scan.set() if config.object_detection.enable: self.camera.stream.decoders[self._object_decoder].scan.clear() else: if config.object_detection.enable: self.camera.stream.decoders[self._object_decoder].scan.set() self.camera.stream.decoders[self._motion_decoder].scan.clear() self.idle_frames = 0 self._post_processor_topic = ( f"{config.camera.name_slug}/{TOPIC_FRAME_SCAN_POSTPROC}") self.start_camera() # Initialize recorder self._start_recorder = False self.recorder = FFMPEGRecorder(config) self.nvr_list[config.camera.name_slug] = self RestartableThread( name=str(self), target=self.run, stop_target=self.stop, thread_store_category=THREAD_STORE_CATEGORY_NVR, daemon=False, register=True, ).start() if viseron.mqtt.MQTT.client: self.setup_mqtt() self._logger.debug("NVR thread initialized")
def __init__(self, config: NVRConfig, camera: FFMPEGCamera): self._logger = logging.getLogger(__name__ + "." + config.camera.name_slug) if getattr(config.motion_detection.logging, "level", None): self._logger.setLevel(config.motion_detection.logging.level) elif getattr(config.camera.logging, "level", None): self._logger.setLevel(config.camera.logging.level) self._logger.debug( f"Initializing motion detector {config.motion_detection.type}") self._config = config self._resolution = ( config.motion_detection.width, config.motion_detection.height, ) self._mask = None if config.motion_detection.mask: self._logger.debug("Creating mask") # Scale mask to fit resized frame scaled_mask = [] for point_list in config.motion_detection.mask: rel_mask = np.divide((point_list), camera.resolution) scaled_mask.append( np.multiply(rel_mask, self._resolution).astype("int32")) mask = np.zeros( (config.motion_detection.width, config.motion_detection.height, 3), np.uint8, ) mask[:] = 255 cv2.fillPoly(mask, pts=scaled_mask, color=(0, 0, 0)) self._mask = np.where(mask[:, :, 0] == [0]) self._topic_scan_motion = f"{config.camera.name_slug}/{TOPIC_FRAME_SCAN_MOTION}" self.topic_processed_motion = ( f"{config.camera.name_slug}/{TOPIC_FRAME_PROCESSED_MOTION}") self._motion_detector = importlib.import_module( # type: ignore "viseron.motion." + config.motion_detection.type).MotionDetection( self._logger, config.motion_detection, self._mask) self._motion_detection_queue: Queue[ # pylint: disable=unsubscriptable-object FrameToScan, ] = Queue(maxsize=5) motion_detection_thread = RestartableThread( name=__name__ + "." + config.camera.name_slug, target=self.motion_detection, daemon=True, register=True, ) motion_detection_thread.start() FrameDecoder( self._logger, self._config, f"{config.camera.name_slug}.motion_detection", config.motion_detection.interval, camera.stream, camera.decode_error, TOPIC_FRAME_DECODE_MOTION, TOPIC_FRAME_SCAN_MOTION, preprocess_callback=self._motion_detector.preprocess, ) DataStream.subscribe_data(self._topic_scan_motion, self._motion_detection_queue) self._logger.debug("Motion detector initialized")
def __init__(self): config = ViseronConfig(load_config()) log_settings(config) LOGGER.info("-------------------------------------------") LOGGER.info("Initializing...") thread_watchdog = ThreadWatchDog() subprocess_watchdog = SubprocessWatchDog() webserver = WebServer() webserver.start() DataStream(webserver.ioloop) schedule_cleanup(config) mqtt = None if config.mqtt: mqtt = MQTT(config) mqtt_publisher = RestartableThread( name="mqtt_publisher", target=mqtt.publisher, daemon=True, register=True, ) mqtt.connect() mqtt_publisher.start() detector = Detector(config.object_detection) post_processors = {} for ( post_processor_type, post_processor_config, ) in config.post_processors.post_processors.items(): try: post_processors[post_processor_type] = PostProcessor( config, post_processor_type, post_processor_config, ) except (PostProcessorImportError, PostProcessorStructureError) as error: LOGGER.error("Error loading post processor {}. {}".format( post_processor_type, error)) LOGGER.info("Initializing NVR threads") self.setup_threads = [] for camera in config.cameras: setup_thread = SetupNVR( config, camera, detector, ) self.setup_threads.append(setup_thread) for thread in self.setup_threads: thread.join() LOGGER.info("Initialization complete") def signal_term(*_): LOGGER.info("Kill received! Sending kill to threads..") thread_watchdog.stop() subprocess_watchdog.stop() nvr_threads = RestartableThread.thread_store.get( THREAD_STORE_CATEGORY_NVR, []).copy() for thread in nvr_threads: thread.stop() for thread in nvr_threads: thread.join() webserver.stop() webserver.join() LOGGER.info("Exiting") # Listen to signals signal.signal(signal.SIGTERM, signal_term) signal.signal(signal.SIGINT, signal_term)