feat: add FL_ImageToMask node, fix InpaintCrop force_square + auto-resize masks (v2.3.4)
- Add FL_ImageToMask node to convert IMAGE to MASK via luminance/channel extraction - Fix force_square shadowing target_size parameter, breaking resize modes - Auto-resize mask and context_mask to match image size instead of raising errors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
1fc6ea0b0d
commit
078ed5b9e6
@@ -118,6 +118,7 @@ from .nodes.image.FL_ImageOverlay import FL_ImageOverlay
|
||||
from .nodes.image.FL_ImageSelector import FL_ImageSelector
|
||||
from .nodes.image.FL_ImagePicker import FL_ImagePicker
|
||||
from .nodes.image.FL_ImageSlicer import FL_ImageSlicer
|
||||
from .nodes.image.FL_ImageToMask import FL_ImageToMask
|
||||
from .nodes.image.FL_Image_AddToBatch import FL_ImageAddToBatch
|
||||
from .nodes.image.FL_Image_Blank import FL_ImageBlank
|
||||
from .nodes.image.FL_Image_Crop import FL_ImageCrop
|
||||
@@ -390,6 +391,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"FL_RunwayImageAPI": FL_RunwayImageAPI,
|
||||
"FL_RunwayAct2": FL_RunwayAct2,
|
||||
"FL_ImageCrop": FL_ImageCrop,
|
||||
"FL_ImageToMask": FL_ImageToMask,
|
||||
"FL_WanFirstLastFrameToVideo": FL_WanFirstLastFrameToVideo,
|
||||
"FL_WanVideoContinue": FL_WanVideoContinue,
|
||||
"FL_WanVideoBlender": FL_WanVideoBlender,
|
||||
@@ -578,6 +580,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"FL_TextOverlayNode": "FL Text Overlay",
|
||||
"FL_SaveWebM": "FL Save WebM",
|
||||
"FL_ImageCrop": "FL Image Crop",
|
||||
"FL_ImageToMask": "FL Image To Mask",
|
||||
"FL_WanFirstLastFrameToVideo": "FL Wan First Frame Last Frame",
|
||||
"FL_WanVideoContinue": "FL Wan Video Continue",
|
||||
"FL_WanVideoBlender": "FL Wan Video Blender",
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import torch
|
||||
|
||||
|
||||
class FL_ImageToMask:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"channel": (["luminance", "red", "green", "blue", "alpha"], {
|
||||
"default": "luminance",
|
||||
"description": "Which channel to extract as the mask"
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MASK",)
|
||||
RETURN_NAMES = ("mask",)
|
||||
FUNCTION = "image_to_mask"
|
||||
CATEGORY = "🏵️Fill Nodes/Image"
|
||||
|
||||
def image_to_mask(self, image, channel):
|
||||
# image shape: (B, H, W, C)
|
||||
if channel == "luminance":
|
||||
# Standard luminance weights
|
||||
mask = 0.2989 * image[:, :, :, 0] + 0.5870 * image[:, :, :, 1] + 0.1140 * image[:, :, :, 2]
|
||||
elif channel == "red":
|
||||
mask = image[:, :, :, 0]
|
||||
elif channel == "green":
|
||||
mask = image[:, :, :, 1]
|
||||
elif channel == "blue":
|
||||
mask = image[:, :, :, 2]
|
||||
elif channel == "alpha":
|
||||
if image.shape[3] > 3:
|
||||
mask = image[:, :, :, 3]
|
||||
else:
|
||||
mask = torch.ones(image.shape[0], image.shape[1], image.shape[2], device=image.device)
|
||||
|
||||
return (mask,)
|
||||
@@ -218,13 +218,10 @@ class FL_InpaintCrop:
|
||||
original_mask = mask
|
||||
original_height, original_width = image.shape[1], image.shape[2]
|
||||
|
||||
# Validate mask size
|
||||
# Auto-resize mask to match image size
|
||||
if mask.shape[1] != image.shape[1] or mask.shape[2] != image.shape[2]:
|
||||
non_zero_indices = torch.nonzero(mask[0], as_tuple=True)
|
||||
if not non_zero_indices[0].size(0):
|
||||
mask = torch.zeros_like(image[:, :, :, 0])
|
||||
else:
|
||||
raise ValueError("mask size must match image size")
|
||||
mask = F.interpolate(mask.unsqueeze(1), size=(image.shape[1], image.shape[2]), mode='nearest').squeeze(1)
|
||||
print(f"[FL Inpaint Crop] Auto-resized mask to {image.shape[2]}x{image.shape[1]}")
|
||||
|
||||
# Invert mask if requested
|
||||
if invert_mask:
|
||||
@@ -258,11 +255,9 @@ class FL_InpaintCrop:
|
||||
if optional_context_mask is None:
|
||||
context_mask = mask
|
||||
elif optional_context_mask.shape[1] != image.shape[1] or optional_context_mask.shape[2] != image.shape[2]:
|
||||
non_zero_indices = torch.nonzero(optional_context_mask[0], as_tuple=True)
|
||||
if not non_zero_indices[0].size(0):
|
||||
context_mask = mask
|
||||
else:
|
||||
raise ValueError("context_mask size must match image size")
|
||||
optional_context_mask = F.interpolate(optional_context_mask.unsqueeze(1), size=(image.shape[1], image.shape[2]), mode='nearest').squeeze(1)
|
||||
print(f"[FL Inpaint Crop] Auto-resized context mask to {image.shape[2]}x{image.shape[1]}")
|
||||
context_mask = torch.clamp(optional_context_mask + mask, 0.0, 1.0)
|
||||
else:
|
||||
context_mask = torch.clamp(optional_context_mask + mask, 0.0, 1.0)
|
||||
|
||||
@@ -298,22 +293,22 @@ class FL_InpaintCrop:
|
||||
if force_square:
|
||||
x_size = x_max - x_min + 1
|
||||
y_size = y_max - y_min + 1
|
||||
target_size = max(x_size, y_size)
|
||||
square_size = max(x_size, y_size)
|
||||
|
||||
# Center the square
|
||||
x_mid = (x_min + x_max) // 2
|
||||
y_mid = (y_min + y_max) // 2
|
||||
|
||||
x_min = max(x_mid - target_size // 2, 0)
|
||||
x_max = min(x_min + target_size - 1, original_width - 1)
|
||||
y_min = max(y_mid - target_size // 2, 0)
|
||||
y_max = min(y_min + target_size - 1, original_height - 1)
|
||||
x_min = max(x_mid - square_size // 2, 0)
|
||||
x_max = min(x_min + square_size - 1, original_width - 1)
|
||||
y_min = max(y_mid - square_size // 2, 0)
|
||||
y_max = min(y_min + square_size - 1, original_height - 1)
|
||||
|
||||
# Adjust if we hit boundaries
|
||||
if x_max == original_width - 1:
|
||||
x_min = max(0, x_max - target_size + 1)
|
||||
x_min = max(0, x_max - square_size + 1)
|
||||
if y_max == original_height - 1:
|
||||
y_min = max(0, y_max - target_size + 1)
|
||||
y_min = max(0, y_max - square_size + 1)
|
||||
|
||||
print(f"[FL Inpaint Crop] After square adjustment: x[{x_min}, {x_max}] y[{y_min}, {y_max}]")
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui_fill-nodes"
|
||||
description = "Fill-Nodes is a versatile collection of custom nodes for ComfyUI that extends functionality across multiple domains. Features include advanced image processing (pixelation, slicing, masking), visual effects generation (glitch, halftone, pixel art), comprehensive file handling (PDF creation/extraction, Google Drive integration), AI model interfaces (GPT, DALL-E, Hugging Face), utility nodes for workflow enhancement, and specialized tools for video processing, captioning, and batch operations. The pack provides both practical workflow solutions and creative tools within a unified node collection."
|
||||
version = "2.3.3"
|
||||
version = "2.3.4"
|
||||
license = "LICENSE"
|
||||
dependencies = ["librosa", "sounddevice", "glitch_this", "PyOpenGL", "glfw", "scipy>=1.13.1", "requests", "aiohttp", "moviepy", "matplotlib", "reportlab", "openai", "PyPDF2", "pdf2image", "PyMuPDF", "reportlab", "PyPDF2", "ollama", "kornia", "opencv-python", "gdown", "open_clip_torch", "google-genai"]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user