diff --git a/docs/docs/configuration/index.md b/docs/docs/configuration/index.md index 4738d9236..94eec0a90 100644 --- a/docs/docs/configuration/index.md +++ b/docs/docs/configuration/index.md @@ -261,33 +261,29 @@ motion: # Optional: The threshold passed to cv2.threshold to determine if a pixel is different enough to be counted as motion. (default: shown below) # Increasing this value will make motion detection less sensitive and decreasing it will make motion detection more sensitive. # The value should be between 1 and 255. - threshold: 25 + threshold: 40 # Optional: The percentage of the image used to detect lightning or other substantial changes where motion detection # needs to recalibrate. (default: shown below) # Increasing this value will make motion detection more likely to consider lightning or ir mode changes as valid motion. # Decreasing this value will make motion detection more likely to ignore large amounts of motion such as a person approaching # a doorbell camera. lightning_threshold: 0.8 - # Optional: Minimum size in pixels in the resized motion image that counts as motion (default: 30) + # Optional: Minimum size in pixels in the resized motion image that counts as motion (default: shown below) # Increasing this value will prevent smaller areas of motion from being detected. Decreasing will # make motion detection more sensitive to smaller moving objects. # As a rule of thumb: # - 15 - high sensitivity # - 30 - medium sensitivity # - 50 - low sensitivity - contour_area: 30 - # Optional: Alpha value passed to cv2.accumulateWeighted when averaging the motion delta across multiple frames (default: shown below) - # Higher values mean the current frame impacts the delta a lot, and a single raindrop may register as motion. - # Too low and a fast moving person wont be detected as motion. - delta_alpha: 0.2 + contour_area: 15 # Optional: Alpha value passed to cv2.accumulateWeighted when averaging frames to determine the background (default: shown below) # Higher values mean the current frame impacts the average a lot, and a new object will be averaged into the background faster. # Low values will cause things like moving shadows to be detected as motion for longer. # https://www.geeksforgeeks.org/background-subtraction-in-an-image-using-concept-of-running-average/ - frame_alpha: 0.2 + frame_alpha: 0.02 # Optional: Height of the resized motion frame (default: 50) - # This operates as an efficient blur alternative. Higher values will result in more granular motion detection at the expense - # of higher CPU usage. Lower values result in less CPU, but small changes may not register as motion. + # Higher values will result in more granular motion detection at the expense of higher CPU usage. + # Lower values result in less CPU, but small changes may not register as motion. frame_height: 50 # Optional: motion mask # NOTE: see docs for more detailed info on creating masks @@ -295,7 +291,7 @@ motion: # Optional: improve contrast (default: shown below) # Enables dynamic contrast improvement. This should help improve night detections at the cost of making motion detection more sensitive # for daytime. - improve_contrast: False + improve_contrast: True # Optional: Delay when updating camera motion through MQTT from ON -> OFF (default: shown below). mqtt_off_delay: 30 diff --git a/frigate/config.py b/frigate/config.py index 213eeddff..7a078f093 100644 --- a/frigate/config.py +++ b/frigate/config.py @@ -191,7 +191,7 @@ class RecordConfig(FrigateBaseModel): class MotionConfig(FrigateBaseModel): threshold: int = Field( - default=25, + default=40, title="Motion detection threshold (1-255).", ge=1, le=255, @@ -199,10 +199,10 @@ class MotionConfig(FrigateBaseModel): lightning_threshold: float = Field( default=0.8, title="Lightning detection threshold (0.3-1.0).", ge=0.3, le=1.0 ) - improve_contrast: bool = Field(default=False, title="Improve Contrast") - contour_area: Optional[int] = Field(default=30, title="Contour Area") + improve_contrast: bool = Field(default=True, title="Improve Contrast") + contour_area: Optional[int] = Field(default=15, title="Contour Area") delta_alpha: float = Field(default=0.2, title="Delta Alpha") - frame_alpha: float = Field(default=0.2, title="Frame Alpha") + frame_alpha: float = Field(default=0.02, title="Frame Alpha") frame_height: Optional[int] = Field(default=50, title="Frame Height") mask: Union[str, List[str]] = Field( default="", title="Coordinates polygon for the motion mask." diff --git a/frigate/motion/cnt_motion.py b/frigate/motion/cnt_motion.py deleted file mode 100644 index cc40253c7..000000000 --- a/frigate/motion/cnt_motion.py +++ /dev/null @@ -1,120 +0,0 @@ -from typing import Tuple - -import cv2 -import imutils -import numpy as np - -from frigate.config import MotionConfig -from frigate.motion import MotionDetector - - -class CNTMotionDetector(MotionDetector): - def __init__( - self, - frame_shape: Tuple[int, int, int], - config: MotionConfig, - fps: int, - improve_contrast, - threshold, - contour_area, - ): - self.frame_shape = frame_shape - self.threshold = threshold - self.contour_area = contour_area - self.improve_contrast = improve_contrast - self.resize_factor = frame_shape[0] / config.frame_height - self.motion_frame_size = ( - config.frame_height, - config.frame_height * frame_shape[1] // frame_shape[0], - ) - resized_mask = cv2.resize( - config.mask, - dsize=(self.motion_frame_size[1], self.motion_frame_size[0]), - interpolation=cv2.INTER_LINEAR, - ) - self.blur_kernel = ( - 3, # int(0.05 * self.motion_frame_size[0]), - 3, # int(0.05 * self.motion_frame_size[0]), - ) - - self.mask = np.where(resized_mask == [0]) - # createBackgroundSubtractorCNT(int minPixelStability = 15, - # bool useHistory = true, - # int maxPixelStability = 15*60, - # bool isParallel = true); - self.bg_subtractor = cv2.bgsegm.createBackgroundSubtractorCNT( - fps, True, fps * 60, True - ) - self.save_images = False - self.frame_counter = 0 - - def detect(self, frame): - motion_boxes = [] - gray = frame[0 : self.frame_shape[0], 0 : self.frame_shape[1]] - # resize frame - resized_frame = cv2.resize( - gray, - dsize=(self.motion_frame_size[1], self.motion_frame_size[0]), - interpolation=cv2.INTER_LINEAR, - ) - if self.improve_contrast.value: - resized_frame = cv2.equalizeHist(resized_frame) - blurred_frame = cv2.GaussianBlur( - resized_frame, self.blur_kernel, cv2.BORDER_DEFAULT - ) - # mask frame - blurred_frame[self.mask] = [255] - fg = self.bg_subtractor.apply(blurred_frame) - thresh = cv2.threshold(fg, self.threshold.value, 255, cv2.THRESH_BINARY)[1] - - # dilate the thresholded image to fill in holes, then find contours - # on thresholded image - cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) - cnts = imutils.grab_contours(cnts) - - # loop over the contours - for c in cnts: - # if the contour is big enough, count it as motion - contour_area = cv2.contourArea(c) - if contour_area > self.contour_area.value: - x, y, w, h = cv2.boundingRect(c) - motion_boxes.append( - ( - int(x * self.resize_factor), - int(y * self.resize_factor), - int((x + w) * self.resize_factor), - int((y + h) * self.resize_factor), - ) - ) - - if self.save_images: - self.frame_counter += 1 - # print("--------") - # print(self.frame_counter) - thresh_boxes = cv2.cvtColor(fg, cv2.COLOR_GRAY2BGR) - for c in cnts: - contour_area = cv2.contourArea(c) - if contour_area > self.contour_area.value: - x, y, w, h = cv2.boundingRect(c) - cv2.rectangle( - thresh_boxes, - (x, y), - (x + w, y + h), - (0, 0, 255), - 2, - ) - # print("--------") - image_row_1 = cv2.hconcat( - [ - cv2.cvtColor(fg, cv2.COLOR_GRAY2BGR), - cv2.cvtColor( - self.bg_subtractor.getBackgroundImage(), cv2.COLOR_GRAY2BGR - ), - ] - ) - image_row_2 = cv2.hconcat( - [cv2.cvtColor(thresh, cv2.COLOR_GRAY2BGR), thresh_boxes] - ) - combined_image = cv2.vconcat([image_row_1, image_row_2]) - cv2.imwrite(f"debug/frames/motion-{self.frame_counter}.jpg", combined_image) - return motion_boxes