mlboydaisuke's picture
SAM2.1 Hiera-Tiny LiteRT (CompiledModel GPU), corr 1.0
b07559d verified
|
Raw
History Blame Contribute Delete
3.74 kB
---
license: apache-2.0
library_name: litert
pipeline_tag: mask-generation
tags:
- litert
- sam2
- segment-anything
- image-segmentation
- on-device
- gpu
base_model: facebook/sam2.1-hiera-tiny
---
# SAM 2.1 Hiera-Tiny β€” LiteRT (CompiledModel GPU)
[SAM 2.1](https://ai.meta.com/sam2/) (Segment Anything 2, Meta) Hiera-Tiny converted to
**LiteRT** and running fully on the **GPU** via the `CompiledModel` API (ML Drift). Tap a
point on an image and it returns a segmentation mask β€” the image encoder runs once per image,
the mask decoder runs per point.
Both graphs are **fully GPU-accelerated** on the Pixel 8a (Mali / ML Drift) and on Apple
silicon (Metal), and the output is **bit-exact (corr 1.0)** vs the original PyTorch SAM 2.1.
## Files
| File | Size (fp16) | Input | Output | Runtime |
|---|---|---|---|---|
| `sam2_encoder.tflite` | 80 MB | `[1, 3, 1024, 1024]` NCHW | flat `[1, 4194304]` (`image_embed \| fpn0 \| fpn1`) | CompiledModel GPU |
| `sam2_decoder.tflite` | 17 MB | flat `[1, 4194816]` (`image_embed \| sparse \| fpn0 \| fpn1`) | masks `[1, 3, 256, 256]` | CompiledModel GPU |
| `sam2_prompt.bin` | 3 KB | β€” | prompt-encoder constants for the Kotlin point encoder | β€” |
Preprocessing: resize to 1024Γ—1024, ImageNet mean `[0.485, 0.456, 0.406]` / std
`[0.229, 0.224, 0.225]`, NCHW.
## GPU compatibility
The Hiera image encoder is made GPU-clean with three numerically-identical rewrites (done at
conversion time; the SAM 2 mask decoder converts unchanged):
1. **Bake the windowed positional embedding** (constant for a fixed 1024Β² input) β€” removes the
bicubic `interpolate` (GATHER_ND) and the tiled window embed (BROADCAST_TO).
2. **4-D window partition / unpartition** β€” the 6-D `view`+`permute` becomes split-H β†’ transpose
β†’ split-W (ML Drift rejects > 4-D tensors).
3. **4-D multi-scale attention** β€” the 5-D fused `qkv` reshape becomes a channel-wise q/k/v slice.
## Usage (Kotlin, LiteRT CompiledModel)
```kotlin
import com.google.ai.edge.litert.Accelerator
import com.google.ai.edge.litert.CompiledModel
val encoder = CompiledModel.create(
context.assets, "sam2_encoder.tflite", CompiledModel.Options(Accelerator.GPU), null)
val decoder = CompiledModel.create(
context.assets, "sam2_decoder.tflite", CompiledModel.Options(Accelerator.GPU), null)
// Encode once per image (input = normalized NCHW floats).
val encIn = encoder.createInputBuffers()
encIn[0].writeFloat(inputFloats) // 3 * 1024 * 1024
val flat = encoder.run(encIn)[0].readFloat() // [image_embed | fpn0 | fpn1]
// Build the flat decoder input [image_embed | sparse | fpn0 | fpn1] (sparse = point encoding
// from sam2_prompt.bin), then run the decoder per tap.
val decIn = decoder.createInputBuffers()
decIn[0].writeFloat(flatDecoderInput)
val masks = decoder.run(decIn)[0].readFloat() // (3, 256, 256) logits; mask > 0 = foreground
```
## Usage (Python, verify the graph)
```python
from ai_edge_litert.interpreter import Interpreter
import numpy as np
enc = Interpreter(model_path="sam2_encoder.tflite"); enc.allocate_tensors()
enc.set_tensor(enc.get_input_details()[0]["index"], pixels_nchw.astype(np.float32)) # [1,3,1024,1024]
enc.invoke()
flat = enc.get_tensor(enc.get_output_details()[0]["index"]).flatten() # image_embed | fpn0 | fpn1
```
## Conversion
Converted with `litert-torch` from the Hugging Face `transformers` SAM 2 model. The full
conversion script (and Android sample app) is in
[LiteRT-Models](https://github.com/john-rocky/LiteRT-Models) β†’ `sam2/`.
## License & credits
Apache-2.0, following the original [SAM 2](https://github.com/facebookresearch/sam2) (Meta,
Apache-2.0). Conversion by [@john-rocky](https://github.com/john-rocky).