Esempio n. 1
0
    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
Esempio n. 2
0
    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()
Esempio n. 3
0
 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)
Esempio n. 4
0
    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
Esempio n. 5
0
    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")
Esempio n. 6
0
    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")
Esempio n. 7
0
    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
Esempio n. 8
0
 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,
         )
Esempio n. 9
0
    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")
Esempio n. 10
0
    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")
Esempio n. 11
0
    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)")
Esempio n. 12
0
    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")
Esempio n. 13
0
    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,
            )
Esempio n. 14
0
    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}")
Esempio n. 15
0
    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
Esempio n. 16
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
Esempio n. 17
0
    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
Esempio n. 18
0
    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
Esempio n. 19
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)
Esempio n. 20
0
    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
Esempio n. 21
0
    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")
Esempio n. 22
0
    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")
Esempio n. 23
0
    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")
Esempio n. 24
0
    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)
Esempio n. 25
0
    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")
Esempio n. 26
0
    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")
Esempio n. 27
0
    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)