Mask Generation
Transformers.js
ONNX
swin
onnxruntime
onnxruntime-web
webgpu
vision
image-segmentation
background-removal
salient-object-detection
matting
mvanet
Instructions to use MarcinEU/finegrain-box-segmenter-ONNX with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers.js
How to use MarcinEU/finegrain-box-segmenter-ONNX with Transformers.js:
// npm i @huggingface/transformers import { pipeline } from '@huggingface/transformers'; // Allocate pipeline const pipe = await pipeline('mask-generation', 'MarcinEU/finegrain-box-segmenter-ONNX');
| """ | |
| Export finegrain/finegrain-box-segmenter (MVANet, Refiners) to ONNX. | |
| The box prompt is NOT part of the network (it is a host-side crop in | |
| refiners.solutions.BoxSegmenter.run). So the exported graph is purely: | |
| input float32 [1,3,1024,1024] (RGB, ImageNet-normalized) | |
| logits float32 [1,1,1024,1024] (RAW logits; sigmoid is applied host-side) | |
| See ../.research/*.md for the full sourced analysis. | |
| Usage: | |
| python export_onnx.py # dynamo exporter, opset 18 | |
| python export_onnx.py --patch-pool # + replace adaptive_avg_pool2d with avg_pool2d | |
| python export_onnx.py --exporter legacy # TorchScript exporter, opset 17 | |
| """ | |
| import argparse | |
| import json | |
| import os | |
| import time | |
| import traceback | |
| from pathlib import Path | |
| import torch | |
| def patch_pool() -> None: | |
| """Replace adaptive_avg_pool2d in refiners MVANet `Pool` with an exact avg_pool2d. | |
| In Pool.forward, output size = (h//ratio, w//ratio) and `assert h % ratio == 0`, | |
| so each output cell covers exactly `ratio` input pixels -> avg_pool2d(kernel=ratio, | |
| stride=ratio) is numerically identical, but exports to a plain ONNX AveragePool. | |
| """ | |
| from torch.nn.functional import avg_pool2d | |
| from refiners.foundationals.swin.mvanet import utils as U | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| b, _, h, w = x.shape | |
| assert h % self.ratio == 0 and w % self.ratio == 0 | |
| r = avg_pool2d(x, kernel_size=self.ratio, stride=self.ratio) | |
| return torch.unflatten(r, 0, (b, -1)) | |
| U.Pool.forward = forward | |
| print("[patch] Pool.forward -> F.avg_pool2d(kernel=stride=ratio)") | |
| def main() -> None: | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--exporter", choices=["dynamo", "legacy"], default="dynamo") | |
| ap.add_argument("--opset", type=int, default=None) | |
| ap.add_argument("--patch-pool", action="store_true") | |
| ap.add_argument("--no-fold", action="store_true", | |
| help="legacy: do_constant_folding=False (smaller file; ORT folds at load)") | |
| ap.add_argument("--out", default="models/mvanet_box_segmenter.onnx") | |
| args = ap.parse_args() | |
| opset = args.opset if args.opset is not None else (18 if args.exporter == "dynamo" else 17) | |
| torch.manual_seed(0) | |
| torch.set_num_threads(max(1, os.cpu_count() or 4)) | |
| if args.patch_pool: | |
| patch_pool() | |
| from refiners.solutions import BoxSegmenter | |
| print("[load] BoxSegmenter(device=cpu) (downloads model.safetensors v0.1 on first run) ...") | |
| t0 = time.time() | |
| seg = BoxSegmenter(device="cpu") | |
| model = seg.model.eval().float() | |
| n_params = sum(p.numel() for p in model.parameters()) | |
| dtypes = sorted({str(p.dtype) for p in model.parameters()}) | |
| print(f"[load] ok in {time.time()-t0:.1f}s params={n_params:,} param_dtypes={dtypes}") | |
| x = torch.randn(1, 3, 1024, 1024, dtype=torch.float32) | |
| print("[sanity] running an eager forward ...") | |
| t0 = time.time() | |
| with torch.no_grad(): | |
| y = model(x) | |
| print(f"[sanity] out={tuple(y.shape)} dtype={y.dtype} " | |
| f"min={float(y.min()):.4f} max={float(y.max()):.4f} ({time.time()-t0:.1f}s)") | |
| assert tuple(y.shape) == (1, 1, 1024, 1024), f"unexpected output shape {tuple(y.shape)}" | |
| out = Path(args.out) | |
| out.parent.mkdir(parents=True, exist_ok=True) | |
| print(f"[export] exporter={args.exporter} opset={opset} -> {out}") | |
| t0 = time.time() | |
| try: | |
| if args.exporter == "dynamo": | |
| torch.onnx.export( | |
| model, (x,), str(out), | |
| dynamo=True, opset_version=opset, | |
| input_names=["input"], output_names=["logits"], | |
| external_data=False, optimize=True, verify=False, | |
| ) | |
| else: | |
| with torch.no_grad(): | |
| torch.onnx.export( | |
| model, (x,), str(out), | |
| dynamo=False, opset_version=opset, | |
| input_names=["input"], output_names=["logits"], | |
| do_constant_folding=not args.no_fold, | |
| ) | |
| except Exception: | |
| print("[export] FAILED:\n" + traceback.format_exc()) | |
| raise SystemExit(2) | |
| print(f"[export] done in {time.time()-t0:.1f}s size={out.stat().st_size/1e6:.1f} MB") | |
| # Inspect the produced model | |
| import onnx | |
| m = onnx.load(str(out)) | |
| try: | |
| onnx.checker.check_model(m) | |
| print("[check] onnx.checker: OK") | |
| except Exception as e: | |
| print(f"[check] onnx.checker WARN: {e}") | |
| ops = sorted({n.op_type for n in m.graph.node}) | |
| opsets = {f"{i.domain or 'ai.onnx'}": i.version for i in m.opset_import} | |
| ins = [(i.name, [d.dim_value or d.dim_param for d in i.type.tensor_type.shape.dim]) for i in m.graph.input] | |
| outs = [(o.name, [d.dim_value or d.dim_param for d in o.type.tensor_type.shape.dim]) for o in m.graph.output] | |
| meta = { | |
| "exporter": args.exporter, "opset": opset, "patch_pool": args.patch_pool, | |
| "params": n_params, "size_mb": round(out.stat().st_size / 1e6, 1), | |
| "opset_import": opsets, "n_nodes": len(m.graph.node), | |
| "op_types": ops, "inputs": ins, "outputs": outs, | |
| } | |
| (out.parent / (out.stem + ".meta.json")).write_text(json.dumps(meta, indent=2)) | |
| print("[inspect] inputs:", ins) | |
| print("[inspect] outputs:", outs) | |
| print("[inspect] n_nodes:", len(m.graph.node), "opset_import:", opsets) | |
| print("[inspect] op_types:", ops) | |
| print("[done] wrote", out, "and", out.parent / (out.stem + ".meta.json")) | |
| if __name__ == "__main__": | |
| main() | |