creatumlibre.ui.manager.image_handler

  1# pylint: disable=no-member
  2
  3import cv2
  4import numpy as np
  5from PyQt6.QtGui import QImage, QPixmap
  6
  7from creatumlibre.graphics.selection.region_manager import RegionManager
  8from creatumlibre.ui.mode.ui_input_mode import TransformMode
  9
 10
 11class ImageHandler:
 12    """Handles a single image object: pixel data, selection, and position."""
 13
 14    def __init__(self, image_array: np.ndarray, position=(0, 0), is_promoted=False):
 15        self.original_image = image_array  # BGR format
 16        self.position = position  # Absolute position in scene (e.g., top-left)
 17        self.position_before_drag = position  # reference to add dx,dy while dragging
 18        self.is_promoted = is_promoted
 19        self.is_selected = False
 20        self.region_manager = RegionManager()
 21        self.region_manager.initialize_mask(self.original_image.shape)
 22
 23    def copy(self):
 24        new = ImageHandler(self.original_image.copy())
 25        new.position = self.position  # Assuming it's immutable (like a tuple)
 26        new.is_promoted = False
 27        new.is_selected = False
 28        new.region_manager = self.region_manager.copy()
 29        return new
 30
 31    def get_image(self) -> np.ndarray:
 32        """Returns the raw image array."""
 33        return self.original_image
 34
 35    def contains_point(self, click_position: tuple[int, int]) -> bool:
 36        """hit test in object coordinates"""
 37        x, y = click_position
 38        px, py = self.get_position()
 39        h, w = self.original_image.shape[:2]
 40        return px <= x <= px + w and py <= y <= py + h
 41
 42    def get_pixmap(self) -> QPixmap:
 43        """Converts the image array (BGR) to a QPixmap for UI display."""
 44        image = self.get_image()
 45        rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
 46        height, width, channels = rgb_image.shape
 47        bytes_per_line = channels * width
 48
 49        q_image = QImage(
 50            rgb_image.data, width, height, bytes_per_line, QImage.Format.Format_RGB888
 51        )
 52        return QPixmap.fromImage(q_image)
 53
 54    def set_image(self, image):
 55        self.original_image = image
 56
 57    def set_position(self, position: tuple):
 58        """set global position"""
 59        self.position = position
 60
 61    def get_position(self) -> tuple:
 62        """Returns the (x, y) position in global scene space."""
 63        return self.position
 64
 65    def get_mask(self) -> np.ndarray:
 66        """Returns the current alpha mask (0-255)."""
 67        return self.region_manager.mask
 68
 69    def extract_selection_as_new_image(self) -> "ImageHandler | None":
 70        """Extracts the currently selected region as a new ImageHandler instance."""
 71        selection_mask = self.region_manager.get_mask()
 72        if selection_mask is None:
 73            return None
 74
 75        x, y, w, h = self.region_manager.get_bounding_rect()
 76
 77        # Clip region safely from image
 78        selected_region = self.original_image[y : y + h, x : x + w].copy()
 79
 80        # Optional: clip selection mask too
 81        # cropped_mask = selection_mask[y:y+h, x:x+w].copy()
 82
 83        new_object = ImageHandler(
 84            image_array=selected_region, position=(x, y), is_promoted=True
 85        )
 86
 87        return new_object
 88
 89    def draw_selection_frame(
 90        self, transform_mode: TransformMode, zoom_factor: float = 1.0
 91    ):
 92        """Draws a selection frame with optional handles on this image."""
 93        img = self.original_image
 94        h, w = img.shape[:2]
 95        if h < 4 or w < 4:
 96            return
 97        color = (255, 0, 255)  # magenta
 98        thickness = int(1 / zoom_factor)
 99
100        # Draw border
101        cv2.rectangle(img, (0, 0), (w - 1, h - 1), color, thickness)
102        print(transform_mode)
103
104        # Add 9-resize handles, rotate pivot, etc.
105        # For example, draw corner point:
106        # cv2.circle(img, (0, 0), radius=4, color=color, thickness=-1)
class ImageHandler:
 12class ImageHandler:
 13    """Handles a single image object: pixel data, selection, and position."""
 14
 15    def __init__(self, image_array: np.ndarray, position=(0, 0), is_promoted=False):
 16        self.original_image = image_array  # BGR format
 17        self.position = position  # Absolute position in scene (e.g., top-left)
 18        self.position_before_drag = position  # reference to add dx,dy while dragging
 19        self.is_promoted = is_promoted
 20        self.is_selected = False
 21        self.region_manager = RegionManager()
 22        self.region_manager.initialize_mask(self.original_image.shape)
 23
 24    def copy(self):
 25        new = ImageHandler(self.original_image.copy())
 26        new.position = self.position  # Assuming it's immutable (like a tuple)
 27        new.is_promoted = False
 28        new.is_selected = False
 29        new.region_manager = self.region_manager.copy()
 30        return new
 31
 32    def get_image(self) -> np.ndarray:
 33        """Returns the raw image array."""
 34        return self.original_image
 35
 36    def contains_point(self, click_position: tuple[int, int]) -> bool:
 37        """hit test in object coordinates"""
 38        x, y = click_position
 39        px, py = self.get_position()
 40        h, w = self.original_image.shape[:2]
 41        return px <= x <= px + w and py <= y <= py + h
 42
 43    def get_pixmap(self) -> QPixmap:
 44        """Converts the image array (BGR) to a QPixmap for UI display."""
 45        image = self.get_image()
 46        rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
 47        height, width, channels = rgb_image.shape
 48        bytes_per_line = channels * width
 49
 50        q_image = QImage(
 51            rgb_image.data, width, height, bytes_per_line, QImage.Format.Format_RGB888
 52        )
 53        return QPixmap.fromImage(q_image)
 54
 55    def set_image(self, image):
 56        self.original_image = image
 57
 58    def set_position(self, position: tuple):
 59        """set global position"""
 60        self.position = position
 61
 62    def get_position(self) -> tuple:
 63        """Returns the (x, y) position in global scene space."""
 64        return self.position
 65
 66    def get_mask(self) -> np.ndarray:
 67        """Returns the current alpha mask (0-255)."""
 68        return self.region_manager.mask
 69
 70    def extract_selection_as_new_image(self) -> "ImageHandler | None":
 71        """Extracts the currently selected region as a new ImageHandler instance."""
 72        selection_mask = self.region_manager.get_mask()
 73        if selection_mask is None:
 74            return None
 75
 76        x, y, w, h = self.region_manager.get_bounding_rect()
 77
 78        # Clip region safely from image
 79        selected_region = self.original_image[y : y + h, x : x + w].copy()
 80
 81        # Optional: clip selection mask too
 82        # cropped_mask = selection_mask[y:y+h, x:x+w].copy()
 83
 84        new_object = ImageHandler(
 85            image_array=selected_region, position=(x, y), is_promoted=True
 86        )
 87
 88        return new_object
 89
 90    def draw_selection_frame(
 91        self, transform_mode: TransformMode, zoom_factor: float = 1.0
 92    ):
 93        """Draws a selection frame with optional handles on this image."""
 94        img = self.original_image
 95        h, w = img.shape[:2]
 96        if h < 4 or w < 4:
 97            return
 98        color = (255, 0, 255)  # magenta
 99        thickness = int(1 / zoom_factor)
100
101        # Draw border
102        cv2.rectangle(img, (0, 0), (w - 1, h - 1), color, thickness)
103        print(transform_mode)
104
105        # Add 9-resize handles, rotate pivot, etc.
106        # For example, draw corner point:
107        # cv2.circle(img, (0, 0), radius=4, color=color, thickness=-1)

Handles a single image object: pixel data, selection, and position.

ImageHandler(image_array: numpy.ndarray, position=(0, 0), is_promoted=False)
15    def __init__(self, image_array: np.ndarray, position=(0, 0), is_promoted=False):
16        self.original_image = image_array  # BGR format
17        self.position = position  # Absolute position in scene (e.g., top-left)
18        self.position_before_drag = position  # reference to add dx,dy while dragging
19        self.is_promoted = is_promoted
20        self.is_selected = False
21        self.region_manager = RegionManager()
22        self.region_manager.initialize_mask(self.original_image.shape)
original_image
position
position_before_drag
is_promoted
is_selected
region_manager
def copy(self):
24    def copy(self):
25        new = ImageHandler(self.original_image.copy())
26        new.position = self.position  # Assuming it's immutable (like a tuple)
27        new.is_promoted = False
28        new.is_selected = False
29        new.region_manager = self.region_manager.copy()
30        return new
def get_image(self) -> numpy.ndarray:
32    def get_image(self) -> np.ndarray:
33        """Returns the raw image array."""
34        return self.original_image

Returns the raw image array.

def contains_point(self, click_position: tuple[int, int]) -> bool:
36    def contains_point(self, click_position: tuple[int, int]) -> bool:
37        """hit test in object coordinates"""
38        x, y = click_position
39        px, py = self.get_position()
40        h, w = self.original_image.shape[:2]
41        return px <= x <= px + w and py <= y <= py + h

hit test in object coordinates

def get_pixmap(self) -> PyQt6.QtGui.QPixmap:
43    def get_pixmap(self) -> QPixmap:
44        """Converts the image array (BGR) to a QPixmap for UI display."""
45        image = self.get_image()
46        rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
47        height, width, channels = rgb_image.shape
48        bytes_per_line = channels * width
49
50        q_image = QImage(
51            rgb_image.data, width, height, bytes_per_line, QImage.Format.Format_RGB888
52        )
53        return QPixmap.fromImage(q_image)

Converts the image array (BGR) to a QPixmap for UI display.

def set_image(self, image):
55    def set_image(self, image):
56        self.original_image = image
def set_position(self, position: tuple):
58    def set_position(self, position: tuple):
59        """set global position"""
60        self.position = position

set global position

def get_position(self) -> tuple:
62    def get_position(self) -> tuple:
63        """Returns the (x, y) position in global scene space."""
64        return self.position

Returns the (x, y) position in global scene space.

def get_mask(self) -> numpy.ndarray:
66    def get_mask(self) -> np.ndarray:
67        """Returns the current alpha mask (0-255)."""
68        return self.region_manager.mask

Returns the current alpha mask (0-255).

def extract_selection_as_new_image(self) -> ImageHandler | None:
70    def extract_selection_as_new_image(self) -> "ImageHandler | None":
71        """Extracts the currently selected region as a new ImageHandler instance."""
72        selection_mask = self.region_manager.get_mask()
73        if selection_mask is None:
74            return None
75
76        x, y, w, h = self.region_manager.get_bounding_rect()
77
78        # Clip region safely from image
79        selected_region = self.original_image[y : y + h, x : x + w].copy()
80
81        # Optional: clip selection mask too
82        # cropped_mask = selection_mask[y:y+h, x:x+w].copy()
83
84        new_object = ImageHandler(
85            image_array=selected_region, position=(x, y), is_promoted=True
86        )
87
88        return new_object

Extracts the currently selected region as a new ImageHandler instance.

def draw_selection_frame( self, transform_mode: creatumlibre.ui.mode.ui_input_mode.TransformMode, zoom_factor: float = 1.0):
 90    def draw_selection_frame(
 91        self, transform_mode: TransformMode, zoom_factor: float = 1.0
 92    ):
 93        """Draws a selection frame with optional handles on this image."""
 94        img = self.original_image
 95        h, w = img.shape[:2]
 96        if h < 4 or w < 4:
 97            return
 98        color = (255, 0, 255)  # magenta
 99        thickness = int(1 / zoom_factor)
100
101        # Draw border
102        cv2.rectangle(img, (0, 0), (w - 1, h - 1), color, thickness)
103        print(transform_mode)
104
105        # Add 9-resize handles, rotate pivot, etc.
106        # For example, draw corner point:
107        # cv2.circle(img, (0, 0), radius=4, color=color, thickness=-1)

Draws a selection frame with optional handles on this image.