Mask Generation
LiteRT
LiteRT
sam2
segment-anything
mask-decoder
interactive-segmentation
on-device
gpu
Instructions to use litert-community/SAM2.1-Hiera-Tiny-Mask-Decoder with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LiteRT
How to use litert-community/SAM2.1-Hiera-Tiny-Mask-Decoder with LiteRT:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- sam2
How to use litert-community/SAM2.1-Hiera-Tiny-Mask-Decoder with sam2:
# Use SAM2 with images import torch from sam2.sam2_image_predictor import SAM2ImagePredictor predictor = SAM2ImagePredictor.from_pretrained(litert-community/SAM2.1-Hiera-Tiny-Mask-Decoder) with torch.inference_mode(), torch.autocast("cuda", dtype=torch.bfloat16): predictor.set_image(<your_image>) masks, _, _ = predictor.predict(<input_prompts>)# Use SAM2 with videos import torch from sam2.sam2_video_predictor import SAM2VideoPredictor predictor = SAM2VideoPredictor.from_pretrained(litert-community/SAM2.1-Hiera-Tiny-Mask-Decoder) with torch.inference_mode(), torch.autocast("cuda", dtype=torch.bfloat16): state = predictor.init_state(<your_video>) # add new prompts and instantly get the output on the same frame frame_idx, object_ids, masks = predictor.add_new_points(state, <your_prompts>): # propagate the prompts to get masklets throughout the video for frame_idx, object_ids, masks in predictor.propagate_in_video(state): ... - Notebooks
- Google Colab
- Kaggle
Document the rank-3 attention GPU miscompute and the v2 fix
Browse files
README.md
CHANGED
|
@@ -34,16 +34,25 @@ turns a **point prompt** into segmentation masks **per tap** (a few ms each) β
|
|
| 34 |
| Inputs | `image_embeddings [1,256,64,64]`, `sparse_prompt [1,2,256]`, `feat_s1 [1,64,128,128]`, `feat_s0 [1,32,256,256]` |
|
| 35 |
| Outputs | `pred_masks [1,3,256,256]` (logits, 3 multimask candidates), `iou_scores [1,3]` |
|
| 36 |
| Precision / size | FP16, **17 MB** |
|
| 37 |
-
|
|
|
|
|
| 38 |
| Op set | `banned ops = NONE`, `>4-D tensors = 0` (BATCH_MATMUL Γ15, SOFTMAX Γ7, GELU Γ2, CONV_2D Γ2) |
|
| 39 |
|
| 40 |
-
> **β Residency β correctness
|
| 41 |
-
>
|
| 42 |
-
>
|
| 43 |
-
>
|
| 44 |
-
>
|
| 45 |
-
>
|
| 46 |
-
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
|
| 48 |
## Pipeline (how the inputs are produced)
|
| 49 |
|
|
@@ -112,9 +121,9 @@ card). Mask boundaries are carried by the near-exact high-resolution features, s
|
|
| 112 |
// once per image - encoder on GPU (decoder-ready v2 variant from the companion repo)
|
| 113 |
val enc = CompiledModel.create(context.assets, "sam2_tiny_image_encoder_v2_fp16.tflite",
|
| 114 |
CompiledModel.Options(Accelerator.GPU), null)
|
| 115 |
-
// per tap - decoder on
|
| 116 |
-
val dec = CompiledModel.create(context.assets, "
|
| 117 |
-
CompiledModel.Options(Accelerator.
|
| 118 |
// dec inputs: 0 image_embeddings[1,256,64,64], 1 sparse[1,2,256],
|
| 119 |
// 2 feat_s1[1,64,128,128], 3 feat_s0[1,32,256,256]
|
| 120 |
// dec outputs: pred_masks[1,3,256,256] logits, iou_scores[1,3] -> argmax(iou), threshold 0
|
|
@@ -143,8 +152,8 @@ coord = 2 * np.pi * ((2 * (np.array([px, py], np.float32) + 0.5) / 1024 - 1) @ p
|
|
| 143 |
tok0 = np.concatenate([np.sin(coord), np.cos(coord)]) + pe1
|
| 144 |
sparse = np.stack([tok0, nap])[None].astype(np.float32) # [1,2,256]
|
| 145 |
|
| 146 |
-
# 3) decode masks
|
| 147 |
-
dec = Interpreter(model_path="
|
| 148 |
feed = {(1,2,256): sparse}; feed.update(eo) # match inputs by shape
|
| 149 |
for d in dec.get_input_details(): dec.set_tensor(d["index"], feed[tuple(d["shape"])])
|
| 150 |
dec.invoke()
|
|
|
|
| 34 |
| Inputs | `image_embeddings [1,256,64,64]`, `sparse_prompt [1,2,256]`, `feat_s1 [1,64,128,128]`, `feat_s0 [1,32,256,256]` |
|
| 35 |
| Outputs | `pred_masks [1,3,256,256]` (logits, 3 multimask candidates), `iou_scores [1,3]` |
|
| 36 |
| Precision / size | FP16, **17 MB** |
|
| 37 |
+
| File | **`sam2_tiny_mask_decoder_v2_fp16.tflite`** (recommended). `sam2_tiny_mask_decoder_fp16.tflite` is the earlier build, kept for reference β see the note below. |
|
| 38 |
+
| Device | Pixel 8a β **fully GPU** (LITERT_CL), correct masks, ~7 ms/tap |
|
| 39 |
| Op set | `banned ops = NONE`, `>4-D tensors = 0` (BATCH_MATMUL Γ15, SOFTMAX Γ7, GELU Γ2, CONV_2D Γ2) |
|
| 40 |
|
| 41 |
+
> **β Residency β correctness β and why v2 exists.** The first build (`sam2_tiny_mask_decoder_fp16.tflite`)
|
| 42 |
+
> fully delegated to the GPU (358/358 LITERT_CL nodes, `banned ops = NONE`, `>4-D = 0`, desktop parity
|
| 43 |
+
> corr 1.0) yet returned **silently wrong masks on the Pixel 8a GPU** (corr 0.265 vs CPU; a face tap at
|
| 44 |
+
> IoU β 0.62 on CPU collapsed to β 0.10 with the mask on the background).
|
| 45 |
+
>
|
| 46 |
+
> The cause was found by device A/B bisection: its attention was written with the **batch dim collapsed**
|
| 47 |
+
> (q/k/v shaped `[heads, N, d]`, rank 3). The GPU delegate mis-computes that form. It is **not** an fp16
|
| 48 |
+
> problem (forcing fp32 GPU compute still gives corr 0.473) and **not** LayerNorm (plain and
|
| 49 |
+
> overflow-safe LN give the same wrong result). The mask head's rank-2 matmul is innocent.
|
| 50 |
+
>
|
| 51 |
+
> **`v2` keeps the leading batch dim (rank-4 SDPA, `[1, heads, N, d]`).** Host numerics are identical
|
| 52 |
+
> (eager cos 0.999999); on the Pixel 8a GPU it restores **corr 0.9998 / binary-IoU 0.999** vs CPU and is
|
| 53 |
+
> **~20 % faster** (6.8 ms vs 8.5 ms). Inputs and outputs are unchanged, so v2 is a drop-in replacement.
|
| 54 |
+
> Note the companion encoder's rank-3 SDPA *is* GPU-correct β a healthy sibling graph proves nothing;
|
| 55 |
+
> only a numeric GPU-vs-CPU check on device catches this.
|
| 56 |
|
| 57 |
## Pipeline (how the inputs are produced)
|
| 58 |
|
|
|
|
| 121 |
// once per image - encoder on GPU (decoder-ready v2 variant from the companion repo)
|
| 122 |
val enc = CompiledModel.create(context.assets, "sam2_tiny_image_encoder_v2_fp16.tflite",
|
| 123 |
CompiledModel.Options(Accelerator.GPU), null)
|
| 124 |
+
// per tap - decoder on GPU (v2: rank-4 attention, GPU-correct; see the note above)
|
| 125 |
+
val dec = CompiledModel.create(context.assets, "sam2_tiny_mask_decoder_v2_fp16.tflite",
|
| 126 |
+
CompiledModel.Options(Accelerator.GPU), null)
|
| 127 |
// dec inputs: 0 image_embeddings[1,256,64,64], 1 sparse[1,2,256],
|
| 128 |
// 2 feat_s1[1,64,128,128], 3 feat_s0[1,32,256,256]
|
| 129 |
// dec outputs: pred_masks[1,3,256,256] logits, iou_scores[1,3] -> argmax(iou), threshold 0
|
|
|
|
| 152 |
tok0 = np.concatenate([np.sin(coord), np.cos(coord)]) + pe1
|
| 153 |
sparse = np.stack([tok0, nap])[None].astype(np.float32) # [1,2,256]
|
| 154 |
|
| 155 |
+
# 3) decode masks
|
| 156 |
+
dec = Interpreter(model_path="sam2_tiny_mask_decoder_v2_fp16.tflite"); dec.allocate_tensors()
|
| 157 |
feed = {(1,2,256): sparse}; feed.update(eo) # match inputs by shape
|
| 158 |
for d in dec.get_input_details(): dec.set_tensor(d["index"], feed[tuple(d["shape"])])
|
| 159 |
dec.invoke()
|