diff --git a/frigate/output.py b/frigate/output.py index 3bb4e515f..09c385dcf 100644 --- a/frigate/output.py +++ b/frigate/output.py @@ -33,7 +33,7 @@ from frigate.util.image import ( logger = logging.getLogger(__name__) -def get_standard_aspect_ratio(width, height) -> tuple[int, int]: +def get_standard_aspect_ratio(width: int, height: int) -> tuple[int, int]: """Ensure that only standard aspect ratios are used.""" known_aspects = [ (16, 9), @@ -52,6 +52,22 @@ def get_standard_aspect_ratio(width, height) -> tuple[int, int]: return known_aspects[known_aspects_ratios.index(closest)] +def get_canvas_shape(width: int, height: int) -> tuple[int, int]: + """Get birdseye canvas shape.""" + canvas_width = width + canvas_height = height + a_w, a_h = get_standard_aspect_ratio(width, height) + + if round(a_w / a_h, 2) != round(width / height, 2): + canvas_width = width + canvas_height = (canvas_width / a_w) * a_h + logger.warning( + f"The birdseye resolution is a non-standard aspect ratio, forcing birdseye resolution to {canvas_width} x {canvas_height}" + ) + + return (canvas_width, canvas_height) + + class Canvas: def __init__(self, canvas_width: int, canvas_height: int) -> None: gcd = math.gcd(canvas_width, canvas_height) @@ -226,8 +242,7 @@ class BirdsEyeFrameManager: self.config = config self.mode = config.birdseye.mode self.frame_manager = frame_manager - width = config.birdseye.width - height = config.birdseye.height + width, height = get_canvas_shape(config.birdseye.width, config.birdseye.height) self.frame_shape = (height, width) self.yuv_shape = (height * 3 // 2, width) self.frame = np.ndarray(self.yuv_shape, dtype=np.uint8) diff --git a/frigate/test/test_birdseye.py b/frigate/test/test_birdseye.py new file mode 100644 index 000000000..8c24b48ec --- /dev/null +++ b/frigate/test/test_birdseye.py @@ -0,0 +1,47 @@ +"""Test camera user and password cleanup.""" + +import unittest + +from frigate.output import get_canvas_shape + + +class TestBirdseye(unittest.TestCase): + def test_16x9(self): + """Test 16x9 aspect ratio works as expected for birdseye.""" + width = 1280 + height = 720 + canvas_width, canvas_height = get_canvas_shape(width, height) + assert canvas_width == width + assert canvas_height == height + + def test_4x3(self): + """Test 4x3 aspect ratio works as expected for birdseye.""" + width = 1280 + height = 960 + canvas_width, canvas_height = get_canvas_shape(width, height) + assert canvas_width == width + assert canvas_height == height + + def test_32x9(self): + """Test 32x9 aspect ratio works as expected for birdseye.""" + width = 2560 + height = 720 + canvas_width, canvas_height = get_canvas_shape(width, height) + assert canvas_width == width + assert canvas_height == height + + def test_9x16(self): + """Test 9x16 aspect ratio works as expected for birdseye.""" + width = 720 + height = 1280 + canvas_width, canvas_height = get_canvas_shape(width, height) + assert canvas_width == width + assert canvas_height == height + + def test_non_16x9(self): + """Test non 16x9 aspect ratio fails for birdseye.""" + width = 1280 + height = 840 + canvas_width, canvas_height = get_canvas_shape(width, height) + assert canvas_width == width # width will be the same + assert canvas_height != height