diff --git a/README.md b/README.md index dccd874..ee7b34d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 🔗 Comfyui : Bjornulf_custom_nodes v0.13 🔗 +# 🔗 Comfyui : Bjornulf_custom_nodes v0.14 🔗 # Dependencies @@ -22,6 +22,7 @@ - **v0.11**: Add a new node : Text with random Seed - Generate a random seed, along with text. - **v0.12**: Combine images : Add option to move vertically and horizontally. (from -50% to 150%) - **v0.13**: Add a new node: Load image with transparency (alpha) - Load an image with transparency. +- **v0.14**: Add a new node: Cut image from a mask # 📝 Nodes descriptions @@ -233,4 +234,10 @@ FLUX : Here is an example of 4 images without Random Seed node on the left, and **Description:** Load an image with transparency. -The default `Load Image` node will not load the transparency. \ No newline at end of file +The default `Load Image` node will not load the transparency. + +## 30 - 🖼✂ Cut image with a mask +![Cut image](screenshots/image_mask_cut.png) + +**Description:** +Cut an image from a mask. \ No newline at end of file diff --git a/__init__.py b/__init__.py index bb1f55f..4e7b1a2 100644 --- a/__init__.py +++ b/__init__.py @@ -34,6 +34,7 @@ from .random_line_from_input import RandomLineFromInput from .loop_lines import LoopAllLines from .random_seed_with_text import TextToStringAndSeed from .load_image_alpha import LoadImageWithTransparency +from .image_mask_cutter import ImageMaskCutter # from .check_black_image import CheckBlackImage # from .clear_vram import ClearVRAM @@ -42,6 +43,7 @@ from .load_image_alpha import LoadImageWithTransparency NODE_CLASS_MAPPINGS = { # "Bjornulf_CustomStringType": CustomStringType, "Bjornulf_ollamaLoader": ollamaLoader, + "Bjornulf_ImageMaskCutter": ImageMaskCutter, "Bjornulf_LoadImageWithTransparency": LoadImageWithTransparency, "Bjornulf_LoopAllLines": LoopAllLines, "Bjornulf_TextToStringAndSeed": TextToStringAndSeed, @@ -84,6 +86,7 @@ NODE_CLASS_MAPPINGS = { NODE_DISPLAY_NAME_MAPPINGS = { # "Bjornulf_CustomStringType": "!!! CUSTOM STRING TYPE !!!", "Bjornulf_ollamaLoader": "🦙 Ollama (Description)", + "Bjornulf_ImageMaskCutter": "🖼✂ Cut Image with Mask", "Bjornulf_LoadImageWithTransparency": "🖼 Load Image with Transparency ▢", "Bjornulf_GreenScreenToTransparency": "🟩➜▢ Green Screen to Transparency", # "Bjornulf_CheckBlackImage": "🔲 Check Black Image (Empty mask)", diff --git a/combine_background_overlay.py b/combine_background_overlay.py index 0a8ca94..6ad2188 100644 --- a/combine_background_overlay.py +++ b/combine_background_overlay.py @@ -8,7 +8,8 @@ class CombineBackgroundOverlay: return { "required": { "background": ("IMAGE",), - "overlay_alpha": ("IMAGE",), + "overlay": ("IMAGE",), + "mask": ("MASK",), "horizontal_position": ("FLOAT", {"default": 50, "min": -50, "max": 150, "step": 0.1}), "vertical_position": ("FLOAT", {"default": 50, "min": -50, "max": 150, "step": 0.1}), }, @@ -18,7 +19,7 @@ class CombineBackgroundOverlay: FUNCTION = "combine_background_overlay" CATEGORY = "Bjornulf" - def combine_background_overlay(self, background, overlay_alpha, horizontal_position, vertical_position): + def combine_background_overlay(self, background, overlay, mask, horizontal_position, vertical_position): # Convert background from torch tensor to numpy array bg = background[0].numpy() bg = (bg * 255).astype(np.uint8) @@ -26,17 +27,23 @@ class CombineBackgroundOverlay: results = [] - for overlay in overlay_alpha: + for ov, m in zip(overlay, mask): # Convert overlay from torch tensor to numpy array - ov = overlay.numpy() + ov = ov.numpy() ov = (ov * 255).astype(np.uint8) + # Convert mask from torch tensor to numpy array + m = m.numpy() + m = (m * 255).astype(np.uint8) + # Create PIL Image for overlay - if ov.shape[2] == 4: - ov_img = Image.fromarray(ov, 'RGBA') - else: - ov_img = Image.fromarray(ov, 'RGB') - ov_img = ov_img.convert('RGBA') + ov_img = Image.fromarray(ov, 'RGB') + + # Create alpha channel from mask + alpha = Image.fromarray(m, 'L') + + # Combine RGB overlay with alpha mask + ov_img.putalpha(alpha) # Calculate horizontal position x = int((horizontal_position / 100) * (bg_img.width - ov_img.width)) @@ -56,9 +63,8 @@ class CombineBackgroundOverlay: # Convert back to numpy array and then to torch tensor result_np = np.array(result) - # If the result is RGBA, convert to RGB - if result_np.shape[2] == 4: - result_np = result_np[:,:,:3] + # Convert RGBA to RGB + result_np = result_np[:,:,:3] result_tensor = torch.from_numpy(result_np).float() / 255.0 diff --git a/image_mask_cutter.py b/image_mask_cutter.py new file mode 100644 index 0000000..4c6bea3 --- /dev/null +++ b/image_mask_cutter.py @@ -0,0 +1,46 @@ +import torch + +class ImageMaskCutter: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "image": ("IMAGE",), + "mask": ("MASK",), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "cut_image" + + CATEGORY = "Bjornulf" + + def cut_image(self, image, mask): + print(f"Image shape: {image.shape}") + print(f"Mask shape: {mask.shape}") + + # Check if image channels are in the last dimension + if image.shape[-1] == 3 or image.shape[-1] == 4: + # Move channels to second dimension + image = image.permute(0, 3, 1, 2) + + # Ensure image and mask have compatible dimensions + if image.shape[2:] != mask.shape[1:]: + raise ValueError(f"Image and mask must have compatible dimensions. Got image shape {image.shape} and mask shape {mask.shape}") + + # Convert mask to float and ensure it's in the range [0, 1] + mask = mask.float() + mask = torch.clamp(mask, 0, 1) + + # If image is RGB, convert to RGBA + if image.shape[1] == 3: + alpha = torch.ones((image.shape[0], 1, image.shape[2], image.shape[3]), device=image.device) + image = torch.cat([image, alpha], dim=1) + + # Use the mask as the alpha channel + image[:, 3:4, :, :] = mask.unsqueeze(1) + + # Move channels back to the last dimension + cut_image = image.permute(0, 2, 3, 1) + + return (cut_image,) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index bac1bcc..e8b3873 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "bjornulf_custom_nodes" description = "Nodes: Ollama, Green Screen to Transparency, Save image for Bjornulf LobeChat, Text with random Seed, Random line from input, Combine images (Background+Overlay alpha), Image to grayscale (black & white), Remove image Transparency (alpha), Resize Image, ..." -version = "0.13" +version = "0.14" license = {file = "LICENSE"} [project.urls] diff --git a/screenshots/image_mask_cut.png b/screenshots/image_mask_cut.png new file mode 100644 index 0000000..8032308 Binary files /dev/null and b/screenshots/image_mask_cut.png differ