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:
Machine Delusions
2026-03-08 00:17:18 -08:00
co-authored by Claude Opus 4.6
parent 1fc6ea0b0d
commit 078ed5b9e6
4 changed files with 56 additions and 19 deletions
+3
View File
@@ -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",
+39
View File
@@ -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,)
+13 -18
View File
@@ -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
View File
@@ -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"]