Comfy-Org/ComfyUI · error · ValueError

TripoSplatPreprocessImage: mask is empty (no foreground pixe

Error message

TripoSplatPreprocessImage: mask is empty (no foreground pixels).

What it means

TripoSplatPreprocessImage crops an RGBA image around its alpha foreground. After optional erosion (a min-filter over the alpha channel) it finds nonzero alpha pixels; if none remain, the mask is empty and there is nothing to crop, so it raises. The erosion step can shrink a thin or weak alpha matte to nothing.

Source

Thrown at comfy_extras/nodes_triposplat.py:44

    # Match original preprocessing:
    # resize min side to `size` -> erode alpha -> alpha bbox -> 1.2x square crop -> resize -> composite on black.
    rgb = image[..., :3].clamp(0, 1).movedim(-1, 0)        # (3, H, W)
    alpha = mask.clamp(0, 1)[None]                         # (1, H, W)
    rgba = torch.cat([rgb, alpha], 0)[None]                # (1, 4, H, W)

    h, w = rgba.shape[-2:]
    s = size / min(w, h)
    rgba = comfy.utils.common_upscale(rgba, max(1, round(w * s)), max(1, round(h * s)), "lanczos", "disabled").clamp(0, 1)

    a = rgba[:, 3:4]
    if erode_radius > 0:
        # min filter over a (2r+1) window == morphological erosion of the alpha matte.
        a = -F.max_pool2d(-a, 2 * erode_radius + 1, stride=1, padding=erode_radius)
        rgba = torch.cat([rgba[:, :3], a], 1)

    ys, xs = torch.nonzero(a[0, 0] > 0, as_tuple=True)
    if xs.numel() == 0:
        raise ValueError("TripoSplatPreprocessImage: mask is empty (no foreground pixels).")
    x0, x1 = int(xs.min()), int(xs.max())
    y0, y1 = int(ys.min()), int(ys.max())
    cx, cy = (x0 + x1) / 2, (y0 + y1) / 2
    half = max(x1 - x0, y1 - y0) / 2 * 1.2
    left, upper, right, lower = int(cx - half), int(cy - half), int(cx + half), int(cy + half)

    H, W = rgba.shape[-2:]
    crop = rgba.new_zeros((1, 4, lower - upper, right - left))  # out-of-bounds stays 0, matching PIL.crop
    sx0, sy0, sx1, sy1 = max(left, 0), max(upper, 0), min(right, W), min(lower, H)
    if sx1 > sx0 and sy1 > sy0:
        crop[:, :, sy0 - upper:sy1 - upper, sx0 - left:sx1 - left] = rgba[:, :, sy0:sy1, sx0:sx1]

    crop = comfy.utils.common_upscale(crop, size, size, "lanczos", "disabled").clamp(0, 1)
    out = (crop[:, :3] * crop[:, 3:4])[0].movedim(0, -1)   # composite over black == rgb * alpha
    return out.unsqueeze(0)  # (1, 1024, 1024, 3)


class TripoSplatPreprocessImage(IO.ComfyNode):

View on GitHub (pinned to 1c6d8d45b3)

Solutions

  1. Verify the input image actually has foreground alpha > 0 (inspect the alpha channel statistics)
  2. Reduce erode_radius so erosion does not consume the whole matte
  3. Re-export the image with a correct alpha channel from the background-removal tool

Example fix

# before: erosion wipes out a thin matte
pre = TripoSplatPreprocessImage.execute(rgba, size=512, erode_radius=15)

# after: smaller erosion radius
pre = TripoSplatPreprocessImage.execute(rgba, size=512, erode_radius=2)
Defensive patterns

Strategy: validation

Validate before calling

alpha = rgba[:, 3:4]
# simulate the erosion the node applies
eroded = -torch.nn.functional.max_pool2d(-alpha, 2 * erode_radius + 1, 1, erode_radius)
if (eroded[0, 0] > 0).sum() == 0:
    erode_radius = 0  # or reject the image

Type guard

def has_foreground(rgba: torch.Tensor) -> bool:
    return bool((rgba[:, 3] > 0).any())

Prevention

When it happens

Trigger: Input RGBA image whose alpha channel is all zeros, or alpha that becomes all zeros after erode_radius erosion — e.g. a faint silhouette with alpha values of 1-2 out of 255, or a fine hair/thin-object matte eroded by a radius larger than the feature width.

Common situations: Background-removed images saved without alpha (fully transparent); PNG with black alpha channel; overly large erode_radius for delicate mattes; images where segmentation produced an empty mask.

Related errors


AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14). Data as JSON: /api/errors/3f17c286e209716f. Report an issue: GitHub.