diff --git a/docs/docs/integrations/mqtt.md b/docs/docs/integrations/mqtt.md index fca590b61..7d5aeb049 100644 --- a/docs/docs/integrations/mqtt.md +++ b/docs/docs/integrations/mqtt.md @@ -140,3 +140,19 @@ Topic to turn improve_contrast for a camera on and off. Expected values are `ON` ### `frigate//improve_contrast/state` Topic with current state of improve_contrast for a camera. Published values are `ON` and `OFF`. + +### `frigate//motion_threshold/set` + +Topic to adjust motion threshold for a camera. Expected value is an integer. + +### `frigate//motion_threshold/state` + +Topic with current motion threshold for a camera. Published value is an integer. + +### `frigate//motion_contour_area/set` + +Topic to adjust motion contour area for a camera. Expected value is an integer. + +### `frigate//motion_contour_area/state` + +Topic with current motion contour area for a camera. Published value is an integer. \ No newline at end of file diff --git a/frigate/app.py b/frigate/app.py index 7de851234..81603b4e0 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -95,6 +95,12 @@ class FrigateApp: "improve_contrast_enabled": mp.Value( "i", self.config.cameras[camera_name].motion.improve_contrast ), + "motion_threshold": mp.Value( + "i", self.config.cameras[camera_name].motion.threshold + ), + "motion_contour_area": mp.Value( + "i", self.config.cameras[camera_name].motion.contour_area + ), "detection_fps": mp.Value("d", 0.0), "detection_frame": mp.Value("d", 0.0), "read_start": mp.Value("d", 0.0), diff --git a/frigate/motion.py b/frigate/motion.py index 093359ab0..9068d79b4 100644 --- a/frigate/motion.py +++ b/frigate/motion.py @@ -5,7 +5,14 @@ from frigate.config import MotionConfig class MotionDetector: - def __init__(self, frame_shape, config: MotionConfig, improve_contrast_enabled): + def __init__( + self, + frame_shape, + config: MotionConfig, + improve_contrast_enabled, + motion_threshold, + motion_contour_area, + ): self.config = config self.frame_shape = frame_shape self.resize_factor = frame_shape[0] / config.frame_height @@ -25,6 +32,8 @@ class MotionDetector: self.mask = np.where(resized_mask == [0]) self.save_images = False self.improve_contrast = improve_contrast_enabled + self.threshold = motion_threshold + self.contour_area = motion_contour_area def detect(self, frame): motion_boxes = [] @@ -69,7 +78,7 @@ class MotionDetector: # compute the threshold image for the current frame current_thresh = cv2.threshold( - frameDelta, self.config.threshold, 255, cv2.THRESH_BINARY + frameDelta, self.threshold.value, 255, cv2.THRESH_BINARY )[1] # black out everything in the avg_delta where there isnt motion in the current frame @@ -79,7 +88,7 @@ class MotionDetector: # then look for deltas above the threshold, but only in areas where there is a delta # in the current frame. this prevents deltas from previous frames from being included thresh = cv2.threshold( - avg_delta_image, self.config.threshold, 255, cv2.THRESH_BINARY + avg_delta_image, self.threshold.value, 255, cv2.THRESH_BINARY )[1] # dilate the thresholded image to fill in holes, then find contours @@ -94,7 +103,7 @@ class MotionDetector: for c in cnts: # if the contour is big enough, count it as motion contour_area = cv2.contourArea(c) - if contour_area > self.config.contour_area: + if contour_area > self.contour_area.value: x, y, w, h = cv2.boundingRect(c) motion_boxes.append( ( @@ -111,8 +120,7 @@ class MotionDetector: # print(self.frame_counter) for c in cnts: contour_area = cv2.contourArea(c) - # print(contour_area) - if contour_area > self.config.contour_area: + if contour_area > self.contour_area.value: x, y, w, h = cv2.boundingRect(c) cv2.rectangle( thresh_dilated, diff --git a/frigate/mqtt.py b/frigate/mqtt.py index 1b5ea1afc..4b74d6f62 100644 --- a/frigate/mqtt.py +++ b/frigate/mqtt.py @@ -145,6 +145,52 @@ def create_mqtt_client(config: FrigateConfig, camera_metrics): state_topic = f"{message.topic[:-4]}/state" client.publish(state_topic, payload, retain=True) + def on_motion_threshold_command(client, userdata, message): + try: + payload = int(message.payload.decode()) + except ValueError: + logger.warning( + f"Received unsupported value at {message.topic}: {message.payload.decode()}" + ) + return + + logger.debug(f"on_motion_threshold_toggle: {message.topic} {payload}") + + camera_name = message.topic.split("/")[-3] + + motion_settings = config.cameras[camera_name].motion + + logger.info(f"Setting motion threshold for {camera_name} via mqtt: {payload}") + camera_metrics[camera_name]["motion_threshold"].value = payload + motion_settings.threshold = payload + + state_topic = f"{message.topic[:-4]}/state" + client.publish(state_topic, payload, retain=True) + + def on_motion_contour_area_command(client, userdata, message): + try: + payload = int(message.payload.decode()) + except ValueError: + logger.warning( + f"Received unsupported value at {message.topic}: {message.payload.decode()}" + ) + return + + logger.debug(f"on_motion_contour_area_toggle: {message.topic} {payload}") + + camera_name = message.topic.split("/")[-3] + + motion_settings = config.cameras[camera_name].motion + + logger.info( + f"Setting motion contour area for {camera_name} via mqtt: {payload}" + ) + camera_metrics[camera_name]["motion_contour_area"].value = payload + motion_settings.contour_area = payload + + state_topic = f"{message.topic[:-4]}/state" + client.publish(state_topic, payload, retain=True) + def on_restart_command(client, userdata, message): restart_frigate() @@ -195,6 +241,14 @@ def create_mqtt_client(config: FrigateConfig, camera_metrics): f"{mqtt_config.topic_prefix}/{name}/improve_contrast/set", on_improve_contrast_command, ) + client.message_callback_add( + f"{mqtt_config.topic_prefix}/{name}/motion_threshold/set", + on_motion_threshold_command, + ) + client.message_callback_add( + f"{mqtt_config.topic_prefix}/{name}/motion_contour_area/set", + on_motion_contour_area_command, + ) client.message_callback_add( f"{mqtt_config.topic_prefix}/restart", on_restart_command @@ -250,6 +304,16 @@ def create_mqtt_client(config: FrigateConfig, camera_metrics): "ON" if config.cameras[name].motion.improve_contrast else "OFF", retain=True, ) + client.publish( + f"{mqtt_config.topic_prefix}/{name}/motion_threshold/state", + config.cameras[name].motion.threshold, + retain=True, + ) + client.publish( + f"{mqtt_config.topic_prefix}/{name}/motion_contour_area/state", + config.cameras[name].motion.contour_area, + retain=True, + ) return client diff --git a/frigate/types.py b/frigate/types.py index f582c6c0f..2c9c50ec1 100644 --- a/frigate/types.py +++ b/frigate/types.py @@ -16,6 +16,8 @@ class CameraMetricsTypes(TypedDict): frame_queue: Queue motion_enabled: Synchronized improve_contrast_enabled: Synchronized + motion_threshold: Synchronized + motion_contour_area: Synchronized process: Optional[Process] process_fps: Synchronized read_start: Synchronized diff --git a/frigate/video.py b/frigate/video.py index c7fc62ad5..e6364268f 100755 --- a/frigate/video.py +++ b/frigate/video.py @@ -363,13 +363,19 @@ def track_camera( detection_enabled = process_info["detection_enabled"] motion_enabled = process_info["motion_enabled"] improve_contrast_enabled = process_info["improve_contrast_enabled"] + motion_threshold = process_info["motion_threshold"] + motion_contour_area = process_info["motion_contour_area"] frame_shape = config.frame_shape objects_to_track = config.objects.track object_filters = config.objects.filters motion_detector = MotionDetector( - frame_shape, config.motion, improve_contrast_enabled + frame_shape, + config.motion, + improve_contrast_enabled, + motion_threshold, + motion_contour_area, ) object_detector = RemoteObjectDetector( name, labelmap, detection_queue, result_connection, model_shape