Text and box prompts
Cut out a specific subject with SAM3 — by name, or by pointing at it.
Sam3 wraps transformers' SAM3 and
exposes its prompt-conditioned segmentation as a single alpha matte, so it drops into the same flow
as BiRefNet — plus the capability BiRefNet does not have.
MultiMatte is the trained checkpoint; AutoModel resolves the Sam3 class from its tag.
from nobg import AutoModel
model = AutoModel.from_pretrained("feyninc/multimatte")
model.process("input.jpg").save("output.png")Or start from Meta's weights
Sam3.from_origin("facebook/sam3") converts Meta's upstream checkpoint instead. Those weights are
gated under Meta's SAM License — accept it
on the Hub and log in first. nobg ships no SAM weights, and only the Apache-2.0 transformers
implementation is used in code.
Prompting by name
Because SAM3 is open-vocabulary, the second argument names what to cut out:
model.process("input.jpg", "the dog").save("dog.png")With no prompt, the processor supplies default_prompt ("the main foreground subject", taken
from config.default_prompt) — which is what makes prompt-free background removal work. A prompt
applies to every image in the call.
Prompting by box
The third argument is boxes: a visual prompt in the original image's pixel coordinates. Use it
when the thing you want is easier to point at than to name.
model.process("input.jpg", None, [[120, 80, 460, 720]]).save("cutout.png")Unlike prompt, boxes is per-image: pass [[x1, y1, x2, y2], ...] for one image, or one such
list per image for a batch. The nesting depth is how the two cases are told apart, and the boxes are
sliced alongside the images so batch_size cannot misalign them.
images = ["a.jpg", "b.jpg"]
boxes = [[[10, 10, 200, 300]], [[40, 60, 380, 500], [400, 20, 620, 260]]]
cuts = model.process(images, "the dog", boxes)Boxes and a prompt can be combined. With boxes and no prompt, SAM3 segments what the boxes point at.
The processor
process builds its processor from the model config: image size and default_prompt come straight
from it, and the CLIP tokenizer is loaded from whatever repo from_origin read the weights from,
falling back to the ungated openai/clip-vit-large-patch14 (SAM3's text tower is CLIP's). Pass
tokenizer= a repo id, a directory or an instance to override that.
When you would rather hold the processor yourself, predict is the same call with it passed in
first. MultiMatte ships its own processor_config.json and tokenizer, so loading from the repo gives
you exactly what it was trained with:
from nobg import Sam3Processor
processor = Sam3Processor.from_pretrained("feyninc/multimatte")
model.predict(processor, "input.jpg", "the dog").save("dog.png")Always preprocess through Sam3Processor
pixel_values must be exactly config.image_size square — the vision tower's rotary embeddings
are fixed-size buffers built from it — and input_ids is required, since SAM3 will not run without
a prompt. forward raises a clear error for both rather than failing deep inside attention.
Which head produces the matte
By default the matte comes from SAM3's own prompt-conditioned semantic head
(config.aggregate="semantic"). Set aggregate to "max" or "mean" to build it from the union of
per-object instance masks instead:
model = Sam3.from_origin("feyninc/multimatte", aggregate="max")
model.predict(processor, "input.jpg", score_threshold=0.5)from_origin is what takes config-field overrides; from_pretrained passes only the checkpoint's own
config. On an already-loaded model, model.config.aggregate = "max" also works — forward reads the
field on every call.
That path respects score_threshold — how many detected objects land in the matte, falling back to
the best-scoring one so the matte is never empty — but produces a noticeably softer alpha, because a
query's mask logits are calibrated for binarizing at 0.5, not for use as a matte.
Measured against FeyNobg on two photos:
aggregate | MAE | Pixels at intermediate alpha | score_threshold |
|---|---|---|---|
"semantic" (default) | 0.035 / 0.039 | 19 / 29 % | ignored |
"max" / "mean" | 0.144 / 0.150 | 71 / 79 % | respected |
The semantic head is the clear default. Reach for "max"/"mean" when you specifically want the
matte to track the detected instance set.
Detecting an absent concept
Because the matte is never empty, a prompt for something that isn't in the image still returns one.
Read presence_logits to tell the difference — SAM3's presence head is a reliable confidence signal.
On a cosplay photo: "the person" → 0.97, "the hat" → 0.85, "the dog" → 0.001.
import torch
from loadimg import load_img
image = load_img("input.jpg").convert("RGB")
inputs = processor(images=image, text="the dog", return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs)
confidence = outputs["presence_logits"].sigmoid().item()Instance-level outputs
The per-object outputs come through untouched — pred_masks, pred_boxes, pred_logits,
presence_logits, semantic_seg — so the open-vocabulary detector is still fully usable alongside
the matte:
alpha = processor.post_process_alpha_matting(
outputs, target_sizes=[(image.height, image.width)]
)[0]
processor.cutout(image, alpha).save("output.png")
instances = processor.image_processor.post_process_instance_segmentation(outputs)Trade-offs versus FeyNobg
SAM3 finds the right subject — on a test photo its matte agrees with FeyNobg at IoU 0.98 — but it is a detector, not a matting model: masks are predicted at a fraction of the input resolution and upsampled, so edges stay softer (19–29 % of pixels at intermediate alpha, versus 3 % for FeyNobg).
Use SAM3 when you need to choose what to cut out, and FeyNobg when you need hair-level edges.