withoutBG Open Weights (ONNX)

Open-source background removal and alpha matting from RGB images, exported as an ONNX bundle for ONNX Runtime. No PyTorch checkpoints are needed at inference time.

A trained router looks at each image and picks the branch suited to it:

  • Fine strands, soft detail, transparency β†’ the withoutBG matting model (Depth Anything V2 small depth + ConvNeXt-fused matting), trained and maintained by withoutBG.
  • Hard opaque objects, flat scenes, vehicles β†’ BiRefNet segmentation.

Only the selected branch runs, and its alpha is upsampled to the image's native resolution.

New in v4 (10.12.4): Smart brush. Paint a stroke over what should stay and what should go; a small stroke-edit network (2.5 MB) fixes the cutout. One stroke on an object reaches the whole object; pixels away from your strokes do not change. On our frozen validation set it cuts the error inside the edited region by 83 % on matte fixes and by 96 % on object edits.

The open-weights release ships no edge refiner; native-resolution refined edges are part of the Cloud API.

See the results

Example 1 Example 2 Example 3

Open Weights results β†’ Β· Cloud API results β†’ Β· Compare β†’

Model details

Field Value
Variant oss
Version 10.12.4 (Open Weights v4)
Pipeline routed (sidecar schema 3)
Format ONNX (opset 18), 4 graphs
Precision fp32
Max inference size 4096 Γ— 4096 (larger images are downscaled first)
Router DINOv3 ConvNeXt base @ 448Β², 6 categories
Matting branch DepthAnythingV2 vits (dav2s) @ 518Β² + ConvNeXtFusedMattingUNet @ 448Β²
BiRefNet branch BiRefNet (general) @ 1024Β²
Smart brush Stroke-edit net @ 448Β² (RGB + matte + keep + remove + depth)
Size ~1.5 GB total

Files

Always distribute all five files together:

File Contents Size
withoutbg-open-weights-backbone.onnx Shared ConvNeXt backbone β†’ router logits + features for matting ~350 MB
withoutbg-open-weights.onnx Matting branch: depth + ConvNeXt-fused matting (reuses backbone features); also outputs edit_depth for Smart brush ~104 MB
birefnet-general.onnx BiRefNet branch ~1.04 GB
withoutbg-open-weights-stroke-edit.onnx Smart brush stroke-edit net ~2.5 MB
withoutbg-open-weights.onnx.json Sidecar: files, SHA256, input sizes and resize modes, router categories β€”

Read the sidecar first. It is the authoritative source for every graph's file name, SHA256, input/output names, input size and interpolation, the router categories, and which of them route to BiRefNet (birefnet_categories).

Pipeline

All graph inputs are float32 RGB in [0, 1], NCHW, and a square stretch of the whole working image (no letterbox, no padding).

Graph Inputs Output
backbone (router) rgb_matting [1, 3, 448, 448] (bilinear, no antialias) route_logits [1, 6], f0–f3
matting (coarse) rgb_depth [1, 3, 518, 518] (bicubic, antialias), rgb_matting (same as backbone), f0–f3 coarse_alpha [1, 1, 448, 448]
BiRefNet (birefnet) rgb [1, 3, 1024, 1024] (bilinear, no antialias) alpha [1, 1, 1024, 1024]
stroke edit (stroke_edit) stroke_input [1, 7, 448, 448]: RGB, matte, keep, remove (area-resized), edit_depth edited_alpha [1, 1, 448, 448]
  1. Convert to RGB. If the image exceeds max_inference_size, downscale it to fit (keeping aspect ratio). This is the working image.
  2. Run the backbone. The category is the argmax of route_logits over categories.
  3. If the category is in birefnet_categories (hard_opaque, flat_scene, vehicle), run BiRefNet. Otherwise run the matting graph with the backbone's f0–f3.
  4. Upsample the branch alpha bilinearly (align_corners=False) to the working size and clamp to [0, 1]. Resize to the original size if step 1 downscaled.

Bilinear resizes match PyTorch F.interpolate(mode="bilinear", align_corners=False) without antialiasing. The bicubic depth input matches PyTorch with antialias=True, which PIL reproduces on float ("F" mode) channels.

Smart brush

The withoutbg Python package (1.4.0+) runs the edit for you:

from withoutbg import WithoutBG

model = WithoutBG.open_weights()
result = model.remove_background("photo.jpg")
# keep.png / remove.png: same size as the photo, white where you painted
fixed = model.edit("photo.jpg", keep="keep.png", remove="remove.png", matte=result)
fixed.save("fixed.png")

The edit runs the backbone and the matting graph's edit_depth, area-resizes RGB, previous matte and the two masks to 448Β², softens hard stroke edges with a [1, 2, 1] tent, runs the stroke-edit graph, upsamples its output bilinearly and blends it in only around the strokes and the pieces they touch. Remove strokes are clamped to 0; Keep strokes are not clamped. Reference code: withoutbg/edit.py in withoutbg-python.

Download

hf download withoutbg/withoutbg-openweights-onnx --local-dir withoutbg-open-weights

Or in Python:

from huggingface_hub import snapshot_download

bundle = snapshot_download(
    repo_id="withoutbg/withoutbg-openweights-onnx",
    allow_patterns=["*.onnx", "*.onnx.json"],
)

Usage

The reference host is RoutedPipeline in withoutbg-inference. It verifies every file's SHA256 and runs the pipeline above:

import json
from pathlib import Path
from PIL import Image
from withoutbg_openweights.edge_pipeline import RoutedPipeline

bundle = Path("withoutbg-open-weights")
sidecar = json.loads((bundle / "withoutbg-open-weights.onnx.json").read_text())
pipeline = RoutedPipeline(sidecar, bundle, providers=["CPUExecutionProvider"])

image = Image.open("input.jpg").convert("RGB")
matte, route = pipeline.estimate_alpha_with_route(image)  # route: {"category", "pipeline"}
image.putalpha(matte)
image.save("output.png")

Standalone, with only numpy, pillow and onnxruntime (run from the bundle folder):

import json
from pathlib import Path

import numpy as np
import onnxruntime as ort
from PIL import Image

bundle = Path(".")  # folder holding the bundle files
sidecar = json.loads((bundle / "withoutbg-open-weights.onnx.json").read_text())
providers = ["CPUExecutionProvider"]
sessions = {
    name: ort.InferenceSession(str(bundle / sidecar[name]["file"]), providers=providers)
    for name in ("router", "coarse", "birefnet")
}  # "stroke_edit" is only needed for Smart brush


def bilinear(x, height, width):
    """(C, H, W) -> (C, height, width); torch bilinear, align_corners=False, no antialias."""
    def axis(src, dst):
        pos = np.maximum((np.arange(dst) + 0.5) * (src / dst) - 0.5, 0.0)
        i0 = pos.astype(np.int64)
        return i0, np.minimum(i0 + 1, src - 1), (pos - i0).astype(np.float32)

    _, h, w = x.shape
    r0, r1, rl = axis(h, height)
    c0, c1, cl = axis(w, width)
    rows = x[:, r0] * (1 - rl)[:, None] + x[:, r1] * rl[:, None]
    return rows[:, :, c0] * (1 - cl) + rows[:, :, c1] * cl


def feed(rgb, spec):
    """Square-stretch native (H, W, 3) float RGB as each graph input's spec says."""
    feeds = {}
    for name, s in spec["inputs"].items():
        if s["interpolation"] == "bicubic":  # antialiased; PIL "F" mode matches torch
            x = np.stack([np.asarray(Image.fromarray(np.ascontiguousarray(rgb[..., c]))
                                     .resize((s["size"],) * 2, Image.Resampling.BICUBIC))
                          for c in range(3)])
        else:
            x = bilinear(np.ascontiguousarray(rgb.transpose(2, 0, 1)), s["size"], s["size"])
        feeds[name] = np.ascontiguousarray(x[None], dtype=np.float32)
    return feeds


image = Image.open("input.jpg").convert("RGB")
w, h = image.size
max_w, max_h = sidecar["max_inference_size"]
if w > max_w:
    w, h = max_w, int(max_w / (image.width / image.height))
if h > max_h:
    w, h = int(max_h * (image.width / image.height)), max_h
work = image.resize((w, h)) if (w, h) != image.size else image
rgb = np.asarray(work, dtype=np.float32) / 255.0

# 1. Route: shared ConvNeXt backbone -> category logits + features.
router = sidecar["router"]
router_feeds = feed(rgb, router)
logits, *feats = sessions["router"].run([router["logits_name"], *router["feature_names"]], router_feeds)
category = router["categories"][int(np.argmax(logits[0]))]

# 2. Run only the selected branch.
if category in router["birefnet_categories"]:
    spec = sidecar["birefnet"]
    alpha = sessions["birefnet"].run([spec["output_name"]], feed(rgb, spec))[0][0]
else:
    spec = sidecar["coarse"]
    feeds = {**router_feeds, **feed(rgb, {"inputs": {"rgb_depth": spec["inputs"]["rgb_depth"]}})}
    feeds.update(zip(spec["feature_names"], feats))
    alpha = sessions["coarse"].run([spec["output_name"]], feeds)[0][0]

# 3. Upsample to the working size, then to the original size.
alpha = np.clip(bilinear(alpha, h, w), 0.0, 1.0)[0]
matte = Image.fromarray(np.clip(alpha * 255.0 + 0.5, 0, 255).astype(np.uint8))
matte = matte.resize(image.size, Image.Resampling.BILINEAR)

out = image.copy()
out.putalpha(matte)
out.save("output.png")
print(category, "->", "birefnet" if category in router["birefnet_categories"] else "matting")

Runtime dependencies

python >=3.10
numpy
pillow
onnxruntime

For Hugging Face downloads, also install huggingface_hub.

More than weights

This repo hosts the ONNX weights. Same open-weights technology powers ready-made surfaces:

Surface Choose when
Python package You want to embed withoutBG in scripts, notebooks, or backends
Docker / self-host You want an HTTP API or browser UI on your own server (CPU or NVIDIA GPU)
Mac app You want a native desktop cutout tool, with an optional Local API for plugins and scripts
GIMP plugin You edit in GIMP 3 and want a private, mask-first workflow via Mac Local API or Docker
Space You want to try a browser demo
Cloud API You need maximum quality without running inference yourself

License

The withoutBG Open Weights model is distributed under the withoutBG Open Weights license. See LICENSE and withoutbg.com/open-model/license.

Built with DINOv3.

Third-party terms

This bundle includes upstream components under their respective licenses. See THIRD_PARTY_NOTICES.md.

Component Used in License
DINOv3 Router backbone, matting DINOv3 License
Depth Anything V2 (small) Matting branch depth Apache-2.0
BiRefNet (ZhengPeng7) birefnet-general.onnx MIT

Links

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Model tree for withoutbg/withoutbg-openweights-onnx

Finetunes
1 model

Space using withoutbg/withoutbg-openweights-onnx 1