You need to agree to share your contact information to access this model

This repository is publicly accessible, but you have to accept the conditions to access its files and content.

Log in or Sign Up to review the conditions and access this model content.

SAM3 β€” Split Encoder / Decoder (ONNX)

Text-prompted, open-vocabulary image segmentation. Give it an image and a text concept (e.g. "person", "red car") and it returns instance masks, boxes and scores for that concept.

This is the two-stage split of SAM3:

File Role Runs
sam3_encoder.onnx Vision backbone: image β†’ 3 feature tensors once per image
sam3_decoder.onnx Detection head: features + text β†’ masks/boxes/scores once per prompt

Because the backbone is separated out, you encode the image once and then decode many prompts cheaply against the cached features β€” instead of re-running the whole model for every prompt.

Precision: the ONNX graph stores weights in fp32 (with a few int8-quantized tensors) and has fp32 inputs/outputs (feed a normal float32 image, read float32 logits/boxes/masks). It is meant to run at fp16 on GPU β€” build/enable fp16 in your runtime (e.g. TensorRT). The latency and VRAM figures below are for that fp16 GPU deployment.


Inputs & outputs

Encoder β€” sam3_encoder.onnx

Tensor Shape dtype Notes
image (in) [1, 3, 924, 924] float32 RGB, CHW, normalized (x/255 - 0.5) / 0.5
/Add_179_output_0 (out) [1, 4356, 256] float32 feature β€” pass straight to decoder
/fused_229/Conv_output_0 (out) [1, 256, 264, 264] float32 feature β€” pass straight to decoder
/fused_232/Conv_output_0 (out) [1, 256, 132, 132] float32 feature β€” pass straight to decoder

Decoder β€” sam3_decoder.onnx

Tensor Shape dtype Notes
3 feature tensors (in) (above) float32 fed by name from the encoder outputs
tokenized_text (in) [1, 32] int64 CLIP-style input_ids, zero-padded to 32
pred_masks (out) [1, 200, 264, 264] float32 per-query mask logits
pred_logits (out) [1, 200, 1] float32 per-query class logit
pred_boxes (out) [1, 200, 4] float32 per-query box, normalized cxcywh
presence_logit_dec (out) [1, 1] float32 global concept-presence logit

The encoder's output tensor names are identical to the decoder's feature input names, so features pass through by name β€” no remapping needed.


How to use

Requires: onnxruntime-gpu (or onnxruntime), opencv-python, numpy, transformers. The tokenizer comes from facebook/sam3.

import cv2
import numpy as np
import onnxruntime as ort
from transformers import AutoTokenizer

INPUT_SIZE = 924
TEXT_LEN = 32
SCORE_THRESHOLD = 0.25

# ── load once ────────────────────────────────────────────────────────────────
providers = ["CUDAExecutionProvider", "CPUExecutionProvider"]
encoder = ort.InferenceSession("sam3_encoder.onnx", providers=providers)
decoder = ort.InferenceSession("sam3_decoder.onnx", providers=providers)

tokenizer = AutoTokenizer.from_pretrained("facebook/sam3")
tokenizer.pad_token_id = 0  # CLIP-style zero padding

enc_out_names = [o.name for o in encoder.get_outputs()]      # the 3 feature tensors
dec_in_names  = [i.name for i in decoder.get_inputs()]
feature_names = [n for n in dec_in_names if n != "tokenized_text"]


# ── preprocessing ─────────────────────────────────────────────────────────────
def preprocess_image(frame_rgb):
    """HxWx3 uint8 RGB -> [1,3,924,924] float32."""
    resized = cv2.resize(frame_rgb, (INPUT_SIZE, INPUT_SIZE), interpolation=cv2.INTER_LINEAR)
    img = resized.astype(np.float32) / 255.0
    img = (img - 0.5) / 0.5
    img = img.transpose(2, 0, 1)[np.newaxis]
    return np.ascontiguousarray(img)

def tokenize(prompt):
    ids = tokenizer(prompt, padding="max_length", max_length=TEXT_LEN,
                    truncation=True, return_tensors="np")["input_ids"]
    return ids.astype(np.int64)


# ── postprocessing ────────────────────────────────────────────────────────────
def sigmoid(x):
    return 1.0 / (1.0 + np.exp(-np.clip(x, -50, 50)))

def cxcywh_to_xyxy(b):
    cx, cy, w, h = b[..., 0], b[..., 1], b[..., 2], b[..., 3]
    return np.stack([cx - 0.5 * w, cy - 0.5 * h, cx + 0.5 * w, cy + 0.5 * h], axis=-1)

def postprocess(out, orig_hw):
    H, W = orig_hw
    scores = sigmoid(out["pred_logits"][0, :, 0]) * sigmoid(out["presence_logit_dec"][0])
    keep = scores > SCORE_THRESHOLD
    boxes = cxcywh_to_xyxy(out["pred_boxes"][0][keep]) * np.array([W, H, W, H], np.float32)
    dets = []
    for score, box, m in zip(scores[keep], boxes, out["pred_masks"][0][keep]):
        mask = sigmoid(cv2.resize(m, (W, H), interpolation=cv2.INTER_LINEAR)) > 0.5
        dets.append({"score": float(score), "box_xyxy": box, "mask": mask})
    return dets


# ── inference: encode ONCE, decode MANY prompts ───────────────────────────────
frame_bgr = cv2.imread("image.jpg")
frame_rgb = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB)
orig_hw = frame_rgb.shape[:2]

# Stage 1 β€” backbone (once)
feats = encoder.run(enc_out_names, {"image": preprocess_image(frame_rgb)})
feat_feed = dict(zip(enc_out_names, feats))

# Stage 2 β€” decode each prompt against the cached features
for prompt in ["person", "car", "dog", "traffic light"]:
    feed = {n: feat_feed[n] for n in feature_names}
    feed["tokenized_text"] = tokenize(prompt)
    outs = decoder.run(None, feed)
    out = dict(zip([o.name for o in decoder.get_outputs()], outs))
    dets = postprocess(out, orig_hw)
    print(f"{prompt}: {len(dets)} detection(s)")

Scoring recap: score = sigmoid(pred_logits) * sigmoid(presence_logit_dec), keep score > 0.25 (tune to taste). Boxes are cxcywh normalized β†’ convert to xyxy and scale by [W, H, W, H]. Masks are logits at 264Γ—264 β†’ resize to source size, sigmoid(...) > 0.5.


Benchmarks

  • NVIDIA RTX 4070 Ti β€” DeepStream (TensorRT, fp16): ~60 ms end-to-end for 1 image + 4 prompts (one encoder pass + four decoder passes).

  • VRAM: ~1800 MB with both encoder and decoder sessions loaded.

Because the encoder runs once and decoders are cheap, added prompts cost far less than a full model pass each β€” the per-prompt cost is dominated by the small decoder, not the backbone.

To reproduce on your own hardware, run the encoder once per image and the decoder per prompt (as in the example above), and time each stage separately (encoder vs. decoder).


Notes

  • Precision: fp32 weights (a few int8-quantized tensors) with fp32 I/O; intended to run at fp16 on GPU. Enable fp16 in your runtime (e.g. TensorRT) for the benchmarked latency/VRAM. Running the raw fp32 graph (e.g. plain ONNX Runtime) is correct but slower and uses more memory.
  • ONNX opset 17 (IR version 8) β€” use a matching onnxruntime (β‰₯ 1.14).
  • Fixed input resolution 924Γ—924 β€” the position embeddings are baked to this grid; resizing the input is not supported.
  • Single execution context per session: don't run one session from multiple threads at once; serialize calls or use one session per thread.

License & attribution

Derived from facebook/sam3. Use of these weights is subject to the SAM3 license β€” review and accept it before use.

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