|
3 | 3 | import cv2 |
4 | 4 | import time |
5 | 5 | from typing import Optional |
| 6 | +from dataclasses import dataclass |
6 | 7 | import numpy as np |
7 | 8 | from multiprocessing import get_logger |
8 | 9 |
|
9 | 10 | from marimapper.camera import Camera |
10 | 11 | from marimapper.timeout_controller import TimeoutController |
11 | 12 | from marimapper.led import Point2D, LED2D |
12 | | -from marimapper.utils import position_window |
| 13 | +from marimapper.utils import window_config |
13 | 14 |
|
14 | 15 |
|
15 | 16 | logger = get_logger() |
16 | 17 |
|
17 | 18 |
|
| 19 | +@dataclass |
| 20 | +class _Window: |
| 21 | + name: str = "MariMapper - Detector" |
| 22 | + camera_native_aspect_ratio: float = -1 |
| 23 | + initial_height: int = -1 |
| 24 | + |
| 25 | +_win = _Window() |
| 26 | + |
| 27 | + |
18 | 28 | def contour_brightness(image: np.ndarray, contour: np.ndarray) -> int: |
19 | 29 | """Calculate the sum of all pixels within a contour.""" |
20 | 30 | mask = np.zeros(image.shape, dtype=np.uint8) |
@@ -89,25 +99,34 @@ def draw_led_detections(image: cv2.Mat, led_detection: Optional[Point2D]) -> np. |
89 | 99 | return render_image |
90 | 100 |
|
91 | 101 |
|
92 | | -def show_image(image: np.ndarray) -> None: |
93 | | - window_name = "MariMapper - Detector" |
| 102 | +# If this is our first render and cv2.imshow(), make sure the window is created |
| 103 | +def _init_win_if_needed(image: np.ndarray) -> None: |
| 104 | + if _win.initial_height <= 0: |
| 105 | + cam_h, cam_w = image.shape[:2] |
| 106 | + cam_aspect_ratio = cam_w / cam_h |
94 | 107 |
|
95 | | - x, y, _, target_height = position_window(window_name, 320, 0, 960, 540) |
| 108 | + x, y, _, target_win_height = window_config(_win.name, 320, 0, 960, 540) |
| 109 | + target_win_width = int(target_win_height * cam_aspect_ratio) |
| 110 | + cv2.namedWindow(_win.name, cv2.WINDOW_NORMAL) |
| 111 | + cv2.resizeWindow(_win.name, target_win_width, target_win_height) |
| 112 | + cv2.moveWindow(_win.name, x, y) |
| 113 | + |
| 114 | + _win.initial_height = target_win_height |
| 115 | + _win.camera_native_aspect_ratio = cam_aspect_ratio |
96 | 116 |
|
97 | | - native_h, native_w = image.shape[:2] |
98 | | - aspect_ratio = native_w / native_h |
99 | 117 |
|
100 | | - target_width = int(target_height * aspect_ratio) |
101 | 118 |
|
102 | | - if not getattr(show_image, "setup_done", False): |
103 | | - cv2.namedWindow(window_name, cv2.WINDOW_NORMAL) |
104 | | - cv2.resizeWindow(window_name, target_width, target_height) |
105 | | - cv2.moveWindow(window_name, x, y) |
106 | | - show_image.setup_done = True |
| 119 | +def show_image(image: np.ndarray) -> None: |
| 120 | + _init_win_if_needed(image) |
107 | 121 |
|
108 | | - display_image = cv2.resize(image, (target_width, target_height)) |
| 122 | + # Resizing actually seems to perform better than not (more responsive too) |
| 123 | + # We only need to resize the display image (not window), and can use the initial height, |
| 124 | + # user movements are still respected |
| 125 | + image_height = _win.initial_height |
| 126 | + image_width = int(image_height * _win.camera_native_aspect_ratio) |
| 127 | + display_image = cv2.resize(image, (image_width, image_height)) |
109 | 128 |
|
110 | | - cv2.imshow(window_name, display_image) |
| 129 | + cv2.imshow(_win.name, display_image) |
111 | 130 | key = cv2.waitKey(1) |
112 | 131 |
|
113 | 132 | if key == 27: # esc |
|
0 commit comments