mirror of
https://github.com/justUmen/Bjornulf_custom_nodes.git
synced 2026-03-21 20:52:11 -03:00
new node : cut image from mask
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
# 🔗 Comfyui : Bjornulf_custom_nodes v0.13 🔗
|
# 🔗 Comfyui : Bjornulf_custom_nodes v0.14 🔗
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
- **v0.11**: Add a new node : Text with random Seed - Generate a random seed, along with text.
|
- **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.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.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
|
# 📝 Nodes descriptions
|
||||||
|
|
||||||
@@ -234,3 +235,9 @@ FLUX : Here is an example of 4 images without Random Seed node on the left, and
|
|||||||
**Description:**
|
**Description:**
|
||||||
Load an image with transparency.
|
Load an image with transparency.
|
||||||
The default `Load Image` node will not load the transparency.
|
The default `Load Image` node will not load the transparency.
|
||||||
|
|
||||||
|
## 30 - 🖼✂ Cut image with a mask
|
||||||
|

|
||||||
|
|
||||||
|
**Description:**
|
||||||
|
Cut an image from a mask.
|
||||||
@@ -34,6 +34,7 @@ from .random_line_from_input import RandomLineFromInput
|
|||||||
from .loop_lines import LoopAllLines
|
from .loop_lines import LoopAllLines
|
||||||
from .random_seed_with_text import TextToStringAndSeed
|
from .random_seed_with_text import TextToStringAndSeed
|
||||||
from .load_image_alpha import LoadImageWithTransparency
|
from .load_image_alpha import LoadImageWithTransparency
|
||||||
|
from .image_mask_cutter import ImageMaskCutter
|
||||||
# from .check_black_image import CheckBlackImage
|
# from .check_black_image import CheckBlackImage
|
||||||
# from .clear_vram import ClearVRAM
|
# from .clear_vram import ClearVRAM
|
||||||
|
|
||||||
@@ -42,6 +43,7 @@ from .load_image_alpha import LoadImageWithTransparency
|
|||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
# "Bjornulf_CustomStringType": CustomStringType,
|
# "Bjornulf_CustomStringType": CustomStringType,
|
||||||
"Bjornulf_ollamaLoader": ollamaLoader,
|
"Bjornulf_ollamaLoader": ollamaLoader,
|
||||||
|
"Bjornulf_ImageMaskCutter": ImageMaskCutter,
|
||||||
"Bjornulf_LoadImageWithTransparency": LoadImageWithTransparency,
|
"Bjornulf_LoadImageWithTransparency": LoadImageWithTransparency,
|
||||||
"Bjornulf_LoopAllLines": LoopAllLines,
|
"Bjornulf_LoopAllLines": LoopAllLines,
|
||||||
"Bjornulf_TextToStringAndSeed": TextToStringAndSeed,
|
"Bjornulf_TextToStringAndSeed": TextToStringAndSeed,
|
||||||
@@ -84,6 +86,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
# "Bjornulf_CustomStringType": "!!! CUSTOM STRING TYPE !!!",
|
# "Bjornulf_CustomStringType": "!!! CUSTOM STRING TYPE !!!",
|
||||||
"Bjornulf_ollamaLoader": "🦙 Ollama (Description)",
|
"Bjornulf_ollamaLoader": "🦙 Ollama (Description)",
|
||||||
|
"Bjornulf_ImageMaskCutter": "🖼✂ Cut Image with Mask",
|
||||||
"Bjornulf_LoadImageWithTransparency": "🖼 Load Image with Transparency ▢",
|
"Bjornulf_LoadImageWithTransparency": "🖼 Load Image with Transparency ▢",
|
||||||
"Bjornulf_GreenScreenToTransparency": "🟩➜▢ Green Screen to Transparency",
|
"Bjornulf_GreenScreenToTransparency": "🟩➜▢ Green Screen to Transparency",
|
||||||
# "Bjornulf_CheckBlackImage": "🔲 Check Black Image (Empty mask)",
|
# "Bjornulf_CheckBlackImage": "🔲 Check Black Image (Empty mask)",
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ class CombineBackgroundOverlay:
|
|||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"background": ("IMAGE",),
|
"background": ("IMAGE",),
|
||||||
"overlay_alpha": ("IMAGE",),
|
"overlay": ("IMAGE",),
|
||||||
|
"mask": ("MASK",),
|
||||||
"horizontal_position": ("FLOAT", {"default": 50, "min": -50, "max": 150, "step": 0.1}),
|
"horizontal_position": ("FLOAT", {"default": 50, "min": -50, "max": 150, "step": 0.1}),
|
||||||
"vertical_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"
|
FUNCTION = "combine_background_overlay"
|
||||||
CATEGORY = "Bjornulf"
|
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
|
# Convert background from torch tensor to numpy array
|
||||||
bg = background[0].numpy()
|
bg = background[0].numpy()
|
||||||
bg = (bg * 255).astype(np.uint8)
|
bg = (bg * 255).astype(np.uint8)
|
||||||
@@ -26,17 +27,23 @@ class CombineBackgroundOverlay:
|
|||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
for overlay in overlay_alpha:
|
for ov, m in zip(overlay, mask):
|
||||||
# Convert overlay from torch tensor to numpy array
|
# Convert overlay from torch tensor to numpy array
|
||||||
ov = overlay.numpy()
|
ov = ov.numpy()
|
||||||
ov = (ov * 255).astype(np.uint8)
|
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
|
# 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 = Image.fromarray(ov, 'RGB')
|
||||||
ov_img = ov_img.convert('RGBA')
|
|
||||||
|
# Create alpha channel from mask
|
||||||
|
alpha = Image.fromarray(m, 'L')
|
||||||
|
|
||||||
|
# Combine RGB overlay with alpha mask
|
||||||
|
ov_img.putalpha(alpha)
|
||||||
|
|
||||||
# Calculate horizontal position
|
# Calculate horizontal position
|
||||||
x = int((horizontal_position / 100) * (bg_img.width - ov_img.width))
|
x = int((horizontal_position / 100) * (bg_img.width - ov_img.width))
|
||||||
@@ -56,8 +63,7 @@ class CombineBackgroundOverlay:
|
|||||||
# Convert back to numpy array and then to torch tensor
|
# Convert back to numpy array and then to torch tensor
|
||||||
result_np = np.array(result)
|
result_np = np.array(result)
|
||||||
|
|
||||||
# If the result is RGBA, convert to RGB
|
# Convert RGBA to RGB
|
||||||
if result_np.shape[2] == 4:
|
|
||||||
result_np = result_np[:,:,:3]
|
result_np = result_np[:,:,:3]
|
||||||
|
|
||||||
result_tensor = torch.from_numpy(result_np).float() / 255.0
|
result_tensor = torch.from_numpy(result_np).float() / 255.0
|
||||||
|
|||||||
46
image_mask_cutter.py
Normal file
46
image_mask_cutter.py
Normal file
@@ -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,)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "bjornulf_custom_nodes"
|
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, ..."
|
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"}
|
license = {file = "LICENSE"}
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
|
|||||||
BIN
screenshots/image_mask_cut.png
Normal file
BIN
screenshots/image_mask_cut.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 305 KiB |
Reference in New Issue
Block a user