creatumlibre.graphics.boolean_operations.image_boolean
1# pylint: disable=no-member 2 3 4import cv2 5import numpy as np 6 7from creatumlibre.ui.manager.image_handler import ImageHandler 8 9 10def merge( 11 from_obj: ImageHandler, to_obj: ImageHandler 12): # pylint: disable=too-many-locals 13 """Composites the 'from' image into the 'to' image using its mask and position.""" 14 overlay = from_obj.get_image() 15 mask = from_obj.get_mask() 16 act_postion = XY.from_tuple(from_obj.get_position()) 17 18 base = to_obj.get_image() 19 h, w = overlay.shape[:2] 20 if h < 1 or w < 1: 21 return 22 23 base_h, base_w = base.shape[:2] 24 25 # Begrenzung berechnen 26 posTopLeft = act_postion.max_XY(XY(0, 0)) 27 posRightBottom = posTopLeft.add(XY(w, h)).min_XY(XY(base_w, base_h)) 28 29 # Falls komplett außerhalb: abbrechen 30 if posTopLeft.x >= posRightBottom.x or posTopLeft.y >= posRightBottom.y: 31 return 32 33 # Offset im Overlay berechnen 34 overlay_1 = posTopLeft.sub(act_postion) 35 overlay_2 = overlay_1.add(posRightBottom.sub(posTopLeft)) 36 37 roi = base[posTopLeft.y : posRightBottom.y, posTopLeft.x : posRightBottom.x].astype( 38 np.float32 39 ) 40 overlay_crop = overlay[overlay_1.y : overlay_2.y, overlay_1.x : overlay_2.x].astype( 41 np.float32 42 ) 43 44 if mask is not None: 45 alpha = mask[overlay_1.y : overlay_2.y, overlay_1.x : overlay_2.x].astype( 46 np.float32 47 ) 48 if len(alpha.shape) == 2: 49 alpha = cv2.merge([alpha] * 3) 50 else: 51 alpha = np.ones_like(overlay_crop, dtype=np.float32) 52 53 blended = overlay_crop * alpha + roi * (1 - alpha) 54 base[posTopLeft.y : posRightBottom.y, posTopLeft.x : posRightBottom.x] = ( 55 blended.astype(np.uint8) 56 ) 57 58 to_obj.set_image(base) 59 60 61class XY: 62 """2D vector class with basic operations.""" 63 64 def __init__(self, x: int, y: int | None = None): 65 self.x = x 66 self.y = y 67 68 @classmethod 69 def from_tuple(cls, vector: tuple[int, int]) -> "XY": 70 return cls(vector[0], vector[1]) 71 72 def to_tuple(self) -> tuple[int, int]: 73 return (self.x, self.y) 74 75 def add(self, other: "XY") -> "XY": 76 return XY(self.x + other.x, self.y + other.y) 77 78 def sub(self, other: "XY") -> "XY": 79 return XY(self.x - other.x, self.y - other.y) 80 81 def max_XY(self, ref: "XY") -> "XY": 82 return XY(max(self.x, ref.x), max(self.y, ref.y)) 83 84 def min_XY(self, ref: "XY") -> "XY": 85 return XY(min(self.x, ref.x), min(self.y, ref.y)) 86 87 def __repr__(self): 88 return f"XY(x={self.x}, y={self.y})"
def
merge( from_obj: creatumlibre.ui.manager.image_handler.ImageHandler, to_obj: creatumlibre.ui.manager.image_handler.ImageHandler):
11def merge( 12 from_obj: ImageHandler, to_obj: ImageHandler 13): # pylint: disable=too-many-locals 14 """Composites the 'from' image into the 'to' image using its mask and position.""" 15 overlay = from_obj.get_image() 16 mask = from_obj.get_mask() 17 act_postion = XY.from_tuple(from_obj.get_position()) 18 19 base = to_obj.get_image() 20 h, w = overlay.shape[:2] 21 if h < 1 or w < 1: 22 return 23 24 base_h, base_w = base.shape[:2] 25 26 # Begrenzung berechnen 27 posTopLeft = act_postion.max_XY(XY(0, 0)) 28 posRightBottom = posTopLeft.add(XY(w, h)).min_XY(XY(base_w, base_h)) 29 30 # Falls komplett außerhalb: abbrechen 31 if posTopLeft.x >= posRightBottom.x or posTopLeft.y >= posRightBottom.y: 32 return 33 34 # Offset im Overlay berechnen 35 overlay_1 = posTopLeft.sub(act_postion) 36 overlay_2 = overlay_1.add(posRightBottom.sub(posTopLeft)) 37 38 roi = base[posTopLeft.y : posRightBottom.y, posTopLeft.x : posRightBottom.x].astype( 39 np.float32 40 ) 41 overlay_crop = overlay[overlay_1.y : overlay_2.y, overlay_1.x : overlay_2.x].astype( 42 np.float32 43 ) 44 45 if mask is not None: 46 alpha = mask[overlay_1.y : overlay_2.y, overlay_1.x : overlay_2.x].astype( 47 np.float32 48 ) 49 if len(alpha.shape) == 2: 50 alpha = cv2.merge([alpha] * 3) 51 else: 52 alpha = np.ones_like(overlay_crop, dtype=np.float32) 53 54 blended = overlay_crop * alpha + roi * (1 - alpha) 55 base[posTopLeft.y : posRightBottom.y, posTopLeft.x : posRightBottom.x] = ( 56 blended.astype(np.uint8) 57 ) 58 59 to_obj.set_image(base)
Composites the 'from' image into the 'to' image using its mask and position.
class
XY:
62class XY: 63 """2D vector class with basic operations.""" 64 65 def __init__(self, x: int, y: int | None = None): 66 self.x = x 67 self.y = y 68 69 @classmethod 70 def from_tuple(cls, vector: tuple[int, int]) -> "XY": 71 return cls(vector[0], vector[1]) 72 73 def to_tuple(self) -> tuple[int, int]: 74 return (self.x, self.y) 75 76 def add(self, other: "XY") -> "XY": 77 return XY(self.x + other.x, self.y + other.y) 78 79 def sub(self, other: "XY") -> "XY": 80 return XY(self.x - other.x, self.y - other.y) 81 82 def max_XY(self, ref: "XY") -> "XY": 83 return XY(max(self.x, ref.x), max(self.y, ref.y)) 84 85 def min_XY(self, ref: "XY") -> "XY": 86 return XY(min(self.x, ref.x), min(self.y, ref.y)) 87 88 def __repr__(self): 89 return f"XY(x={self.x}, y={self.y})"
2D vector class with basic operations.