Cleanup motion mypy

This commit is contained in:
Nicolas Mowen 2026-03-25 11:10:08 -06:00
parent 80c4ce2b5d
commit 706ba0c9c7
3 changed files with 41 additions and 34 deletions

View File

@ -1,7 +1,9 @@
from typing import Any
import cv2 import cv2
import numpy as np import numpy as np
from frigate.config import MotionConfig from frigate.config.config import RuntimeMotionConfig
from frigate.motion import MotionDetector from frigate.motion import MotionDetector
from frigate.util.image import grab_cv2_contours from frigate.util.image import grab_cv2_contours
@ -9,19 +11,20 @@ from frigate.util.image import grab_cv2_contours
class FrigateMotionDetector(MotionDetector): class FrigateMotionDetector(MotionDetector):
def __init__( def __init__(
self, self,
frame_shape, frame_shape: tuple[int, ...],
config: MotionConfig, config: RuntimeMotionConfig,
fps: int, fps: int,
improve_contrast, improve_contrast: Any,
threshold, threshold: Any,
contour_area, contour_area: Any,
): ) -> None:
self.config = config self.config = config
self.frame_shape = frame_shape self.frame_shape = frame_shape
self.resize_factor = frame_shape[0] / config.frame_height frame_height = config.frame_height or frame_shape[0]
self.resize_factor = frame_shape[0] / frame_height
self.motion_frame_size = ( self.motion_frame_size = (
config.frame_height, frame_height,
config.frame_height * frame_shape[1] // frame_shape[0], frame_height * frame_shape[1] // frame_shape[0],
) )
self.avg_frame = np.zeros(self.motion_frame_size, np.float32) self.avg_frame = np.zeros(self.motion_frame_size, np.float32)
self.avg_delta = np.zeros(self.motion_frame_size, np.float32) self.avg_delta = np.zeros(self.motion_frame_size, np.float32)
@ -38,10 +41,10 @@ class FrigateMotionDetector(MotionDetector):
self.threshold = threshold self.threshold = threshold
self.contour_area = contour_area self.contour_area = contour_area
def is_calibrating(self): def is_calibrating(self) -> bool:
return False return False
def detect(self, frame): def detect(self, frame: np.ndarray) -> list:
motion_boxes = [] motion_boxes = []
gray = frame[0 : self.frame_shape[0], 0 : self.frame_shape[1]] gray = frame[0 : self.frame_shape[0], 0 : self.frame_shape[1]]
@ -99,7 +102,7 @@ class FrigateMotionDetector(MotionDetector):
# dilate the thresholded image to fill in holes, then find contours # dilate the thresholded image to fill in holes, then find contours
# on thresholded image # on thresholded image
thresh_dilated = cv2.dilate(thresh, None, iterations=2) thresh_dilated = cv2.dilate(thresh, None, iterations=2) # type: ignore[call-overload]
contours = cv2.findContours( contours = cv2.findContours(
thresh_dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE thresh_dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
) )

View File

@ -4,8 +4,11 @@ import cv2
import numpy as np import numpy as np
from scipy.ndimage import gaussian_filter from scipy.ndimage import gaussian_filter
from typing import Any, Optional
from frigate.camera import PTZMetrics from frigate.camera import PTZMetrics
from frigate.config import MotionConfig from frigate.config import MotionConfig
from frigate.config.config import RuntimeMotionConfig
from frigate.motion import MotionDetector from frigate.motion import MotionDetector
from frigate.util.image import grab_cv2_contours from frigate.util.image import grab_cv2_contours
@ -15,22 +18,23 @@ logger = logging.getLogger(__name__)
class ImprovedMotionDetector(MotionDetector): class ImprovedMotionDetector(MotionDetector):
def __init__( def __init__(
self, self,
frame_shape, frame_shape: tuple[int, ...],
config: MotionConfig, config: RuntimeMotionConfig,
fps: int, fps: int,
ptz_metrics: PTZMetrics = None, ptz_metrics: Optional[PTZMetrics] = None,
name="improved", name: str = "improved",
blur_radius=1, blur_radius: int = 1,
interpolation=cv2.INTER_NEAREST, interpolation: int = cv2.INTER_NEAREST,
contrast_frame_history=50, contrast_frame_history: int = 50,
): ) -> None:
self.name = name self.name = name
self.config = config self.config = config
self.frame_shape = frame_shape self.frame_shape = frame_shape
self.resize_factor = frame_shape[0] / config.frame_height frame_height = config.frame_height or frame_shape[0]
self.resize_factor = frame_shape[0] / frame_height
self.motion_frame_size = ( self.motion_frame_size = (
config.frame_height, frame_height,
config.frame_height * frame_shape[1] // frame_shape[0], frame_height * frame_shape[1] // frame_shape[0],
) )
self.avg_frame = np.zeros(self.motion_frame_size, np.float32) self.avg_frame = np.zeros(self.motion_frame_size, np.float32)
self.motion_frame_count = 0 self.motion_frame_count = 0
@ -44,20 +48,20 @@ class ImprovedMotionDetector(MotionDetector):
self.contrast_values[:, 1:2] = 255 self.contrast_values[:, 1:2] = 255
self.contrast_values_index = 0 self.contrast_values_index = 0
self.ptz_metrics = ptz_metrics self.ptz_metrics = ptz_metrics
self.last_stop_time = None self.last_stop_time: float | None = None
def is_calibrating(self): def is_calibrating(self) -> bool:
return self.calibrating return self.calibrating
def detect(self, frame): def detect(self, frame: np.ndarray) -> list[tuple[int, int, int, int]]:
motion_boxes = [] motion_boxes: list[tuple[int, int, int, int]] = []
if not self.config.enabled: if not self.config.enabled:
return motion_boxes return motion_boxes
# if ptz motor is moving from autotracking, quickly return # if ptz motor is moving from autotracking, quickly return
# a single box that is 80% of the frame # a single box that is 80% of the frame
if ( if self.ptz_metrics is not None and (
self.ptz_metrics.autotracker_enabled.value self.ptz_metrics.autotracker_enabled.value
and not self.ptz_metrics.motor_stopped.is_set() and not self.ptz_metrics.motor_stopped.is_set()
): ):
@ -130,19 +134,19 @@ class ImprovedMotionDetector(MotionDetector):
# dilate the thresholded image to fill in holes, then find contours # dilate the thresholded image to fill in holes, then find contours
# on thresholded image # on thresholded image
thresh_dilated = cv2.dilate(thresh, None, iterations=1) thresh_dilated = cv2.dilate(thresh, None, iterations=1) # type: ignore[call-overload]
contours = cv2.findContours( contours = cv2.findContours(
thresh_dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE thresh_dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
) )
contours = grab_cv2_contours(contours) contours = grab_cv2_contours(contours)
# loop over the contours # loop over the contours
total_contour_area = 0 total_contour_area: float = 0
for c in contours: for c in contours:
# if the contour is big enough, count it as motion # if the contour is big enough, count it as motion
contour_area = cv2.contourArea(c) contour_area = cv2.contourArea(c)
total_contour_area += contour_area total_contour_area += contour_area
if contour_area > self.config.contour_area: if contour_area > (self.config.contour_area or 0):
x, y, w, h = cv2.boundingRect(c) x, y, w, h = cv2.boundingRect(c)
motion_boxes.append( motion_boxes.append(
( (
@ -159,7 +163,7 @@ class ImprovedMotionDetector(MotionDetector):
# check if the motor has just stopped from autotracking # check if the motor has just stopped from autotracking
# if so, reassign the average to the current frame so we begin with a new baseline # if so, reassign the average to the current frame so we begin with a new baseline
if ( if self.ptz_metrics is not None and (
# ensure we only do this for cameras with autotracking enabled # ensure we only do this for cameras with autotracking enabled
self.ptz_metrics.autotracker_enabled.value self.ptz_metrics.autotracker_enabled.value
and self.ptz_metrics.motor_stopped.is_set() and self.ptz_metrics.motor_stopped.is_set()

View File

@ -47,7 +47,7 @@ ignore_errors = false
[mypy-frigate.jobs.*] [mypy-frigate.jobs.*]
ignore_errors = false ignore_errors = false
[mypy-frigate.motion] [mypy-frigate.motion.*]
ignore_errors = false ignore_errors = false
[mypy-frigate.object_detection] [mypy-frigate.object_detection]