File size: 2,376 Bytes
246c106
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import threading
import cv2
import numpy as np
from queue import Queue, Empty
from typing import Tuple

# TODO: 
# currently, the following error is raised due to running cv2 not in the main thread:
#   QObject::killTimer: Timers cannot be stopped from another thread
#   QObject::~QObject: Timers cannot be stopped from another thread
class ImageViewer:
    """
    Example usage:
    viewer = ImageViewer(window_name="Test Viewer", refresh_rate=0.02)
    while viewer.running:
        image = np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8)
        viewer.update_image(image)
    viewer.stop()
    """
    def __init__(self, 
        window_size: Tuple[int, int] = (512, 512),
        window_name: str = "Simulator Viewer", 
        refresh_rate: float = 0.02
    ):
        self.window_name = window_name
        self.refresh_rate = refresh_rate
        self.image_queue = Queue()
        self.running = True
        self.current_image = None
        self.window_size = window_size
        
        # Start the thread
        self.viewer_thread = threading.Thread(target=self._run_viewer, daemon=True)
        self.viewer_thread.start()

    def _run_viewer(self):
        cv2.namedWindow(self.window_name)
        while self.running:
            try:
                # Get the latest image from the queue, with a small timeout to avoid blocking forever
                self.current_image = self.image_queue.get(timeout=self.refresh_rate)
            except Empty:
                # If no new image is available, continue displaying the current image
                pass
            
            if self.current_image is not None:
                # Resize image to fit window
                self.current_image = cv2.resize(self.current_image, self.window_size)
                cv2.imshow(self.window_name, self.current_image)
            
            # Break the loop if 'q' is pressed
            if cv2.waitKey(int(self.refresh_rate * 1000)) & 0xFF == ord('q'):
                self.running = False
        
        # Close window when done
        cv2.destroyWindow(self.window_name)

    def update_image(self, image: np.ndarray):
        if not isinstance(image, np.ndarray):
            raise ValueError("Image must be a numpy array.")
        self.image_queue.put(image)

    def stop(self):
        self.running = False
        self.viewer_thread.join()