Files
2026-04-01 15:19:33 +08:00

127 lines
4.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import torch
MAX_MASKS = 8
class AutoMask:
@classmethod
def INPUT_TYPES(cls):
optional = {f"mask_{i}": ("MASK",) for i in range(1, MAX_MASKS + 1)}
optional["else"] = ("MASK",)
return {
"required": {
"image": ("IMAGE",),
"multi_match": (["Use First Match", "Raise Error"],),
"no_match": (["Output Empty Mask", "Use Else Mask", "Raise Error"],),
},
"optional": optional,
}
RETURN_TYPES = ("IMAGE", "MASK", "IMAGE")
RETURN_NAMES = ("image", "mask", "rgba")
FUNCTION = "execute"
CATEGORY = "IMGLAB"
def execute(self, image, multi_match, no_match, **kwargs):
B, img_h, img_w, C = image.shape
match_results = {}
mask_sizes = {}
matched_masks = []
for i in range(1, MAX_MASKS + 1):
mask = kwargs.get(f"mask_{i}")
if mask is None:
continue
if mask.dim() == 2:
mh, mw = mask.shape
mask = mask.unsqueeze(0).expand(B, -1, -1)
elif mask.dim() == 3:
mh, mw = mask.shape[1], mask.shape[2]
if mask.shape[0] != B:
mask = mask.expand(B, -1, -1)
else:
match_results[i] = False
continue
mask_sizes[i] = [mw, mh]
is_match = (mh == img_h and mw == img_w)
match_results[i] = is_match
if is_match:
matched_masks.append((i, mask))
# ── else mask size for UI display ────────────────────────────────
else_mask_size = None
else_mask_raw = kwargs.get("else")
if else_mask_raw is not None:
if else_mask_raw.dim() == 2:
emh, emw = else_mask_raw.shape
elif else_mask_raw.dim() == 3:
emh, emw = else_mask_raw.shape[1], else_mask_raw.shape[2]
else:
emh, emw = 0, 0
else_mask_size = [emw, emh]
# ── Multiple matches ─────────────────────────────────────────────
if len(matched_masks) > 1 and multi_match == "Raise Error":
slots = [str(s) for s, _ in matched_masks]
raise ValueError(
f"[AutoPickMask] Multiple masks match {img_w}×{img_h}: "
f"slots {', '.join(slots)}. "
f"Disconnect duplicates or switch to 'Use First Match'."
)
# ── No match ─────────────────────────────────────────────────────
if len(matched_masks) == 0:
if no_match == "Raise Error":
connected = [str(k) for k in mask_sizes]
raise ValueError(
f"[AutoPickMask] No mask matches {img_w}×{img_h}. "
f"Connected slots: {', '.join(connected) or 'none'}."
)
elif no_match == "Use Else Mask":
else_mask = else_mask_raw
if else_mask is None:
raise ValueError(
f"[AutoPickMask] No mask matches {img_w}×{img_h} and "
f"'else' is not connected. Connect an else mask or "
f"change 'no_match' to 'Output Empty Mask'."
)
if else_mask.dim() == 2:
else_mask = else_mask.unsqueeze(0).expand(B, -1, -1)
elif else_mask.dim() == 3:
if else_mask.shape[0] != B:
else_mask = else_mask.expand(B, -1, -1)
matched_mask = else_mask
else:
matched_mask = torch.zeros(
B, img_h, img_w, dtype=image.dtype, device=image.device
)
else:
matched_mask = matched_masks[0][1]
# ── Resize if needed (else mask may differ in size) ──────────────
mh, mw = matched_mask.shape[1], matched_mask.shape[2]
if mh != img_h or mw != img_w:
import torch.nn.functional as F
matched_mask = F.interpolate(
matched_mask.unsqueeze(1).float(),
size=(img_h, img_w),
mode="nearest"
).squeeze(1).to(image.dtype)
alpha = matched_mask.unsqueeze(-1)
rgba = torch.cat([image, alpha], dim=-1)
return {
"result": (image, matched_mask, rgba),
"ui": {
"match_results": [match_results],
"image_size": [[img_w, img_h]],
"mask_sizes": [mask_sizes],
"else_mask_size": [else_mask_size] if else_mask_size else [],
},
}