Routed Open Weights 10.8.0: router + matting / BiRefNet, no refiner

#1
by kocabiyik - opened
LICENSE ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ withoutBG Open Weights License
2
+
3
+ Copyright 2026 withoutBG
4
+
5
+ The withoutBG Open Weights model in this repository (the ONNX graphs
6
+ withoutbg-open-weights-backbone.onnx, withoutbg-open-weights.onnx and
7
+ birefnet-general.onnx, and the sidecar withoutbg-open-weights.onnx.json) is
8
+ distributed under the withoutBG Open Weights license:
9
+
10
+ https://withoutbg.com/open-model/license
11
+
12
+ The model is built from third-party components that remain under their own
13
+ licenses, and your use must also comply with them:
14
+
15
+ - DINOv3 (Meta Platforms, Inc.): DINOv3 License
16
+ https://ai.meta.com/resources/models-and-libraries/dinov3-license/
17
+ Built with DINOv3.
18
+ - Depth Anything V2 (small): Apache License 2.0
19
+ https://www.apache.org/licenses/LICENSE-2.0
20
+ - BiRefNet (ZhengPeng7): MIT License
21
+
22
+ Full notices and license texts are in THIRD_PARTY_NOTICES.md.
23
+
24
+ THE MODEL IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
27
+
28
+ Commercial licensing: contact@withoutbg.com
README.md CHANGED
@@ -1,5 +1,7 @@
1
  ---
2
- license: apache-2.0
 
 
3
  library_name: onnxruntime
4
  tags:
5
  - background-removal
@@ -13,11 +15,18 @@ pipeline_tag: image-segmentation
13
 
14
  # withoutBG Open Weights (ONNX)
15
 
16
- Open-source background removal and alpha matting from RGB images. This repository
17
- hosts the **OSS variant** exported as a self-contained ONNX graph for ONNX Runtime.
18
 
19
- The graph includes DepthAnythingV2 and a ConvNeXt-fused matting head — no
20
- PyTorch checkpoints are needed at inference time.
 
 
 
 
 
 
 
21
 
22
  - **Try it live:** [withoutBG on Hugging Face Spaces](https://huggingface.co/spaces/withoutbg/withoutbg)
23
  - **Website:** [withoutbg.com/open-model](https://withoutbg.com/open-model?utm_source=github&utm_medium=withoutbg-hf-readme&utm_campaign=main-readme)
@@ -36,133 +45,181 @@ PyTorch checkpoints are needed at inference time.
36
  | Field | Value |
37
  |-------|-------|
38
  | Variant | `oss` |
39
- | Version | `10.0.0` |
40
- | Format | ONNX (opset 18) |
 
41
  | Precision | fp32 |
42
- | Max resolution | 448 |
43
- | ONNX input tensor | 448 × 448 (fixed letterbox) |
44
- | ONNX output tensor | 448 × 448 |
45
- | Depth | DepthAnythingV2 `vits` (`dav2s`) |
46
- | Matting | ConvNeXtFusedMattingUNet (DINOv3 ConvNeXt `base`) |
47
- | Transformer opt | disabled |
48
- | ORT offline opt | extended |
49
- | Size | ~455 MB |
50
- | SHA256 | `29930e48e9d5ecc56d6486c53c35a4c1470566c2a3359fa180b08c8d3c34ef0f` |
51
 
52
  ## Files
53
 
54
- Always distribute the ONNX file and its sidecar JSON together:
55
-
56
- - `withoutbg-open-weights.onnx` — inference graph (depth → ConvNeXt matting)
57
- - `withoutbg-open-weights.onnx.json` — sidecar metadata (I/O names, shapes, SHA256, canvas size)
58
-
59
- Read the sidecar first. It is the authoritative source for `canvas_size` (448),
60
- input/output names, precision, model version, `depth_variant`, `convnext_size`,
61
- and SHA256.
62
-
63
- ## Architecture
64
 
65
- v10 pipeline:
 
 
 
 
 
66
 
67
- - **Depth:** DepthAnythingV2 `vits` (`dav2s`)
68
- - **Matting:** ConvNeXtFusedMattingUNet — frozen DINOv3 **ConvNeXt** backbone
69
- fused into a U-Net on 4-channel RGB+depth at **448²**
70
 
71
- Consumers letterbox RGB to the fixed ONNX input tensor (`canvas_size` in the
72
- sidecar) and run a single inference session. Depth is computed inside the graph.
73
 
74
- ## Input / output contract
 
75
 
76
- > **Max resolution is 448px.** Input letterboxing and output alpha both use
77
- > `canvas_size` (448).
 
 
 
78
 
79
- The graph expects a **letterboxed** RGB tensor sized to `canvas_size` from the sidecar:
 
 
 
 
 
 
80
 
81
- | | Name | Shape | Dtype | Range |
82
- |---|------|-------|-------|-------|
83
- | Input | `rgb` | `[1, 3, 448, 448]` | float32 | `[0, 1]`, NCHW |
84
- | Output | `alpha` | `[1, 1, 448, 448]` | float32 | `[0, 1]` |
85
 
86
- Preprocessing (required):
87
-
88
- 1. Convert image to RGB.
89
- 2. Read `canvas_size` from the sidecar (448 for this export).
90
- 3. Resize longest side to `canvas_size`, preserve aspect ratio.
91
- 4. Paste at top-left on a black `canvas_size` × `canvas_size` canvas.
92
- 5. Normalize to float32 `[0, 1]`, transpose HWC → CHW, add batch dim.
93
-
94
- Postprocessing (required):
95
 
96
- 1. Crop alpha to the resized image region (top-left, before padding).
97
- 2. Resize alpha back to the original image dimensions.
98
- 3. Attach as PNG alpha channel for cutout output.
99
 
100
- ## Download
101
 
102
  ```python
103
- from huggingface_hub import hf_hub_download
104
 
105
- model_path = hf_hub_download(
106
- repo_id="withoutbg/withoutbg-openweights-onnx",
107
- filename="withoutbg-open-weights.onnx",
108
- )
109
- sidecar_path = hf_hub_download(
110
  repo_id="withoutbg/withoutbg-openweights-onnx",
111
- filename="withoutbg-open-weights.onnx.json",
112
  )
113
  ```
114
 
115
- Or with the CLI:
116
 
117
- ```bash
118
- hf download withoutbg/withoutbg-openweights-onnx \
119
- withoutbg-open-weights.onnx \
120
- withoutbg-open-weights.onnx.json
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  ```
122
 
123
- ## Usage
124
 
125
  ```python
126
- from pathlib import Path
127
  import json
 
 
128
  import numpy as np
129
  import onnxruntime as ort
130
  from PIL import Image
131
 
132
- model_path = Path("withoutbg-open-weights.onnx")
133
- sidecar = json.loads(model_path.with_suffix(model_path.suffix + ".json").read_text())
134
- canvas = sidecar.get("canvas_size", 448)
135
- input_name = sidecar.get("input_name", "rgb")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
- session = ort.InferenceSession(str(model_path), providers=["CPUExecutionProvider"])
138
 
139
  image = Image.open("input.jpg").convert("RGB")
140
- orig_w, orig_h = image.size
141
- scale = canvas / max(orig_w, orig_h)
142
- new_w = max(1, round(orig_w * scale))
143
- new_h = max(1, round(orig_h * scale))
144
-
145
- resized = image.resize((new_w, new_h), Image.Resampling.BILINEAR)
146
- padded = Image.new("RGB", (canvas, canvas), (0, 0, 0))
147
- padded.paste(resized, (0, 0))
148
-
149
- rgb = np.asarray(padded, dtype=np.float32) / 255.0
150
- rgb = np.transpose(rgb, (2, 0, 1))[None, ...]
151
-
152
- alpha_canvas = session.run(None, {input_name: rgb})[0][0, 0]
153
- alpha_crop = alpha_canvas[:new_h, :new_w]
154
- alpha_u8 = np.clip(alpha_crop * 255.0, 0, 255).astype(np.uint8)
155
- alpha = Image.fromarray(alpha_u8, "L").resize((orig_w, orig_h), Image.Resampling.BILINEAR)
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  out = image.copy()
158
- out.putalpha(alpha)
159
  out.save("output.png")
 
160
  ```
161
 
162
  ## Runtime dependencies
163
 
164
  ```text
165
- python >=3.11
166
  numpy
167
  pillow
168
  onnxruntime
@@ -185,23 +242,22 @@ This repo hosts the **ONNX weights**. Same open-weights technology powers ready-
185
 
186
  ## License
187
 
188
- Apache-2.0 — see [withoutbg.com/open-model/license](https://withoutbg.com/open-model/license?utm_source=github&utm_medium=withoutbg-hf-readme&utm_campaign=main-readme).
 
 
189
 
190
  Built with DINOv3.
191
 
192
  ## Third-party terms
193
 
194
- This distribution includes upstream components under their respective licenses.
195
  See [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
196
 
197
- ### DINOv3
198
-
199
- License: [DINOv3 License](https://ai.meta.com/resources/models-and-libraries/dinov3-license/)
200
-
201
- ### Depth Anything V2
202
-
203
- License: [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) —
204
- [Depth Anything V2](https://github.com/DepthAnything/Depth-Anything-V2)
205
 
206
  ## Links
207
 
 
1
  ---
2
+ license: other
3
+ license_name: withoutbg-open-weights
4
+ license_link: LICENSE
5
  library_name: onnxruntime
6
  tags:
7
  - background-removal
 
15
 
16
  # withoutBG Open Weights (ONNX)
17
 
18
+ Open-source background removal and alpha matting from RGB images, exported as an
19
+ ONNX bundle for ONNX Runtime. No PyTorch checkpoints are needed at inference time.
20
 
21
+ A trained **router** looks at each image and picks the branch suited to it:
22
+
23
+ - **Fine strands, soft detail, transparency** → the **withoutBG matting** model
24
+ (Depth Anything V2 small depth + ConvNeXt-fused matting), trained and maintained by withoutBG.
25
+ - **Hard opaque objects, flat scenes, vehicles** → **BiRefNet** segmentation.
26
+
27
+ Only the selected branch runs, and its alpha is upsampled to the image's native
28
+ resolution. The open-weights release ships no edge refiner; refined edges are part of the
29
+ [Cloud API](https://withoutbg.com/pro-model?utm_source=github&utm_medium=withoutbg-hf-readme&utm_campaign=main-readme).
30
 
31
  - **Try it live:** [withoutBG on Hugging Face Spaces](https://huggingface.co/spaces/withoutbg/withoutbg)
32
  - **Website:** [withoutbg.com/open-model](https://withoutbg.com/open-model?utm_source=github&utm_medium=withoutbg-hf-readme&utm_campaign=main-readme)
 
45
  | Field | Value |
46
  |-------|-------|
47
  | Variant | `oss` |
48
+ | Version | `10.8.0` |
49
+ | Pipeline | `routed` (sidecar schema 3) |
50
+ | Format | ONNX (opset 18), 3 graphs |
51
  | Precision | fp32 |
52
+ | Max inference size | 4096 × 4096 (larger images are downscaled first) |
53
+ | Router | DINOv3 ConvNeXt `base` @ 448², 6 categories |
54
+ | Matting branch | DepthAnythingV2 `vits` (`dav2s`) @ 518² + ConvNeXtFusedMattingUNet @ 448² |
55
+ | BiRefNet branch | BiRefNet (general) @ 1024² |
56
+ | Size | ~1.5 GB total |
 
 
 
 
57
 
58
  ## Files
59
 
60
+ Always distribute all four files together:
 
 
 
 
 
 
 
 
 
61
 
62
+ | File | Contents | Size |
63
+ |------|----------|------|
64
+ | `withoutbg-open-weights-backbone.onnx` | Shared ConvNeXt backbone → router logits + features for matting | ~350 MB |
65
+ | `withoutbg-open-weights.onnx` | Matting branch: depth + ConvNeXt-fused matting (reuses backbone features) | ~104 MB |
66
+ | `birefnet-general.onnx` | BiRefNet branch | ~1.04 GB |
67
+ | `withoutbg-open-weights.onnx.json` | Sidecar: files, SHA256, input sizes and resize modes, router categories | — |
68
 
69
+ Read the sidecar first. It is the authoritative source for every graph's file name,
70
+ SHA256, input/output names, input size and interpolation, the router `categories`, and
71
+ which of them route to BiRefNet (`birefnet_categories`).
72
 
73
+ ## Pipeline
 
74
 
75
+ All graph inputs are float32 RGB in `[0, 1]`, NCHW, and a **square stretch** of the
76
+ whole working image (no letterbox, no padding).
77
 
78
+ | Graph | Inputs | Output |
79
+ |-------|--------|--------|
80
+ | backbone (`router`) | `rgb_matting` `[1, 3, 448, 448]` (bilinear, no antialias) | `route_logits` `[1, 6]`, `f0`–`f3` |
81
+ | matting (`coarse`) | `rgb_depth` `[1, 3, 518, 518]` (bicubic, antialias), `rgb_matting` (same as backbone), `f0`–`f3` | `coarse_alpha` `[1, 1, 448, 448]` |
82
+ | BiRefNet (`birefnet`) | `rgb` `[1, 3, 1024, 1024]` (bilinear, no antialias) | `alpha` `[1, 1, 1024, 1024]` |
83
 
84
+ 1. Convert to RGB. If the image exceeds `max_inference_size`, downscale it to fit
85
+ (keeping aspect ratio). This is the working image.
86
+ 2. Run the backbone. The category is the argmax of `route_logits` over `categories`.
87
+ 3. If the category is in `birefnet_categories` (`hard_opaque`, `flat_scene`, `vehicle`),
88
+ run BiRefNet. Otherwise run the matting graph with the backbone's `f0`–`f3`.
89
+ 4. Upsample the branch alpha bilinearly (`align_corners=False`) to the working size and
90
+ clamp to `[0, 1]`. Resize to the original size if step 1 downscaled.
91
 
92
+ Bilinear resizes match PyTorch `F.interpolate(mode="bilinear", align_corners=False)`
93
+ without antialiasing. The bicubic depth input matches PyTorch with `antialias=True`, which
94
+ PIL reproduces on float (`"F"` mode) channels.
 
95
 
96
+ ## Download
 
 
 
 
 
 
 
 
97
 
98
+ ```bash
99
+ hf download withoutbg/withoutbg-openweights-onnx --local-dir withoutbg-open-weights
100
+ ```
101
 
102
+ Or in Python:
103
 
104
  ```python
105
+ from huggingface_hub import snapshot_download
106
 
107
+ bundle = snapshot_download(
 
 
 
 
108
  repo_id="withoutbg/withoutbg-openweights-onnx",
109
+ allow_patterns=["*.onnx", "*.onnx.json"],
110
  )
111
  ```
112
 
113
+ ## Usage
114
 
115
+ The reference host is `RoutedPipeline` in
116
+ [withoutbg-inference](https://github.com/withoutbg/withoutbg-inference). It verifies
117
+ every file's SHA256 and runs the pipeline above:
118
+
119
+ ```python
120
+ import json
121
+ from pathlib import Path
122
+ from PIL import Image
123
+ from withoutbg_openweights.edge_pipeline import RoutedPipeline
124
+
125
+ bundle = Path("withoutbg-open-weights")
126
+ sidecar = json.loads((bundle / "withoutbg-open-weights.onnx.json").read_text())
127
+ pipeline = RoutedPipeline(sidecar, bundle, providers=["CPUExecutionProvider"])
128
+
129
+ image = Image.open("input.jpg").convert("RGB")
130
+ matte, route = pipeline.estimate_alpha_with_route(image) # route: {"category", "pipeline"}
131
+ image.putalpha(matte)
132
+ image.save("output.png")
133
  ```
134
 
135
+ Standalone, with only `numpy`, `pillow` and `onnxruntime` (run from the bundle folder):
136
 
137
  ```python
 
138
  import json
139
+ from pathlib import Path
140
+
141
  import numpy as np
142
  import onnxruntime as ort
143
  from PIL import Image
144
 
145
+ bundle = Path(".") # folder holding the four files
146
+ sidecar = json.loads((bundle / "withoutbg-open-weights.onnx.json").read_text())
147
+ providers = ["CPUExecutionProvider"]
148
+ sessions = {
149
+ name: ort.InferenceSession(str(bundle / sidecar[name]["file"]), providers=providers)
150
+ for name in ("router", "coarse", "birefnet")
151
+ }
152
+
153
+
154
+ def bilinear(x, height, width):
155
+ """(C, H, W) -> (C, height, width); torch bilinear, align_corners=False, no antialias."""
156
+ def axis(src, dst):
157
+ pos = np.maximum((np.arange(dst) + 0.5) * (src / dst) - 0.5, 0.0)
158
+ i0 = pos.astype(np.int64)
159
+ return i0, np.minimum(i0 + 1, src - 1), (pos - i0).astype(np.float32)
160
+
161
+ _, h, w = x.shape
162
+ r0, r1, rl = axis(h, height)
163
+ c0, c1, cl = axis(w, width)
164
+ rows = x[:, r0] * (1 - rl)[:, None] + x[:, r1] * rl[:, None]
165
+ return rows[:, :, c0] * (1 - cl) + rows[:, :, c1] * cl
166
+
167
+
168
+ def feed(rgb, spec):
169
+ """Square-stretch native (H, W, 3) float RGB as each graph input's spec says."""
170
+ feeds = {}
171
+ for name, s in spec["inputs"].items():
172
+ if s["interpolation"] == "bicubic": # antialiased; PIL "F" mode matches torch
173
+ x = np.stack([np.asarray(Image.fromarray(np.ascontiguousarray(rgb[..., c]))
174
+ .resize((s["size"],) * 2, Image.Resampling.BICUBIC))
175
+ for c in range(3)])
176
+ else:
177
+ x = bilinear(np.ascontiguousarray(rgb.transpose(2, 0, 1)), s["size"], s["size"])
178
+ feeds[name] = np.ascontiguousarray(x[None], dtype=np.float32)
179
+ return feeds
180
 
 
181
 
182
  image = Image.open("input.jpg").convert("RGB")
183
+ w, h = image.size
184
+ max_w, max_h = sidecar["max_inference_size"]
185
+ if w > max_w:
186
+ w, h = max_w, int(max_w / (image.width / image.height))
187
+ if h > max_h:
188
+ w, h = int(max_h * (image.width / image.height)), max_h
189
+ work = image.resize((w, h)) if (w, h) != image.size else image
190
+ rgb = np.asarray(work, dtype=np.float32) / 255.0
191
+
192
+ # 1. Route: shared ConvNeXt backbone -> category logits + features.
193
+ router = sidecar["router"]
194
+ router_feeds = feed(rgb, router)
195
+ logits, *feats = sessions["router"].run([router["logits_name"], *router["feature_names"]], router_feeds)
196
+ category = router["categories"][int(np.argmax(logits[0]))]
197
+
198
+ # 2. Run only the selected branch.
199
+ if category in router["birefnet_categories"]:
200
+ spec = sidecar["birefnet"]
201
+ alpha = sessions["birefnet"].run([spec["output_name"]], feed(rgb, spec))[0][0]
202
+ else:
203
+ spec = sidecar["coarse"]
204
+ feeds = {**router_feeds, **feed(rgb, {"inputs": {"rgb_depth": spec["inputs"]["rgb_depth"]}})}
205
+ feeds.update(zip(spec["feature_names"], feats))
206
+ alpha = sessions["coarse"].run([spec["output_name"]], feeds)[0][0]
207
+
208
+ # 3. Upsample to the working size, then to the original size.
209
+ alpha = np.clip(bilinear(alpha, h, w), 0.0, 1.0)[0]
210
+ matte = Image.fromarray(np.clip(alpha * 255.0 + 0.5, 0, 255).astype(np.uint8))
211
+ matte = matte.resize(image.size, Image.Resampling.BILINEAR)
212
 
213
  out = image.copy()
214
+ out.putalpha(matte)
215
  out.save("output.png")
216
+ print(category, "->", "birefnet" if category in router["birefnet_categories"] else "matting")
217
  ```
218
 
219
  ## Runtime dependencies
220
 
221
  ```text
222
+ python >=3.10
223
  numpy
224
  pillow
225
  onnxruntime
 
242
 
243
  ## License
244
 
245
+ The withoutBG Open Weights model is distributed under the withoutBG Open Weights license.
246
+ See [LICENSE](LICENSE) and
247
+ [withoutbg.com/open-model/license](https://withoutbg.com/open-model/license?utm_source=github&utm_medium=withoutbg-hf-readme&utm_campaign=main-readme).
248
 
249
  Built with DINOv3.
250
 
251
  ## Third-party terms
252
 
253
+ This bundle includes upstream components under their respective licenses.
254
  See [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
255
 
256
+ | Component | Used in | License |
257
+ |-----------|---------|---------|
258
+ | [DINOv3](https://github.com/facebookresearch/dinov3) | Router backbone, matting | [DINOv3 License](https://ai.meta.com/resources/models-and-libraries/dinov3-license/) |
259
+ | [Depth Anything V2](https://github.com/DepthAnything/Depth-Anything-V2) (small) | Matting branch depth | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
260
+ | [BiRefNet](https://github.com/ZhengPeng7/BiRefNet) (ZhengPeng7) | `birefnet-general.onnx` | MIT |
 
 
 
261
 
262
  ## Links
263
 
THIRD_PARTY_NOTICES.md ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Third-Party Notices
2
+
3
+ ## withoutBG Open Weights Model (10.8.0, routed)
4
+
5
+ License: withoutBG Open Weights license — https://withoutbg.com/open-model/license
6
+
7
+ This bundle contains three ONNX graphs built from the following upstream components: DINOv3 (router backbone and matting), Depth Anything V2 small (matting depth) and BiRefNet (`birefnet-general.onnx`).
8
+
9
+ **Built with DINOv3**
10
+
11
+ ## DINOv3
12
+
13
+ License: DINOv3 License
14
+
15
+ - https://ai.meta.com/resources/models-and-libraries/dinov3-license/
16
+ - https://github.com/facebookresearch/dinov3/blob/main/LICENSE.md
17
+
18
+ ## Depth Anything V2
19
+
20
+ License: Apache-2.0
21
+
22
+ - https://github.com/DepthAnything/Depth-Anything-V2
23
+ - https://www.apache.org/licenses/LICENSE-2.0
24
+
25
+ ## BiRefNet
26
+
27
+ The `birefnet-general.onnx` graph in this bundle is BiRefNet by ZhengPeng7, exported to ONNX.
28
+ Source: https://github.com/ZhengPeng7/BiRefNet
29
+
30
+ MIT License
31
+
32
+ Copyright (c) 2024 ZhengPeng
33
+
34
+ Permission is hereby granted, free of charge, to any person obtaining a copy
35
+ of this software and associated documentation files (the "Software"), to deal
36
+ in the Software without restriction, including without limitation the rights
37
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
38
+ copies of the Software, and to permit persons to whom the Software is
39
+ furnished to do so, subject to the following conditions:
40
+
41
+ The above copyright notice and this permission notice shall be included in all
42
+ copies or substantial portions of the Software.
43
+
44
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
47
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
49
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
50
+ SOFTWARE.
birefnet-general.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:28c988df0383b7c9925139b7aa012c33ce750e8552bd75a628346096acb4c0bb
3
+ size 1038495470
withoutbg-onnx.md DELETED
@@ -1,259 +0,0 @@
1
- # withoutBG ONNX Open Model Distribution Brief
2
-
3
- This file gives AI agents enough context to package, serve, and distribute the
4
- withoutBG ONNX open model from another project, such as a Docker image or model
5
- registry release.
6
-
7
- Primary source files in the training repo (`model-pro`):
8
-
9
- - `scripts/export_wbgnet_onnx.py` - exports the optimized ONNX artifact and
10
- writes the sidecar metadata JSON.
11
- - `scripts/serve_wbgnet.py` - reference ONNX Runtime CLI that handles image
12
- preprocessing, inference, postprocessing, output composition, and benchmarking.
13
- - `src/depth_variants.py` / `src/model_matting.py` - depth + ConvNeXt checkpoint
14
- naming.
15
-
16
- ## What To Distribute
17
-
18
- Distribute the final ONNX file and its sidecar JSON together:
19
-
20
- ```text
21
- withoutbg-open-weights.onnx
22
- withoutbg-open-weights.onnx.json
23
- ```
24
-
25
- The ONNX graph is self-contained for inference. It includes DepthAnythingV2 and
26
- the ConvNeXt-fused matting head. The serving container does not need PyTorch
27
- checkpoints at runtime.
28
-
29
- Do not distribute local training checkpoints unless the release explicitly calls
30
- for source-weight distribution.
31
-
32
- ## Model Identity
33
-
34
- - Product: `withoutBG`
35
- - Variant: `oss`
36
- - Version: `10.0.0`
37
- - Target: ONNX / Hugging Face / Docker
38
- - License: Apache-2.0 via `https://withoutbg.com/open-model/license`
39
- - Third-party upstream: DINOv3 terms at
40
- `https://ai.meta.com/resources/models-and-libraries/dinov3-license/`
41
- - Main use: background removal / alpha matting from RGB images
42
-
43
- v10 pipeline:
44
-
45
- - Depth: DepthAnythingV2 `vits` (`dav2s`)
46
- - Matting: ConvNeXtFusedMattingUNet — DINOv3 **ConvNeXt** `base` fused into a
47
- U-Net on 4-channel RGB+depth at **448²**
48
-
49
- ## ONNX Contract
50
-
51
- The exported graph has a fixed shape contract:
52
-
53
- ```text
54
- input_name: rgb
55
- input_shape: [1, 3, 448, 448]
56
- input_dtype: float32 by default, float16 only when exported with --io-fp16
57
- input_layout: NCHW
58
- input_range: [0, 1]
59
-
60
- output_name: alpha
61
- output_shape: [1, 1, 448, 448]
62
- output_dtype: float32
63
- output_range: [0, 1]
64
- ```
65
-
66
- The graph does not accept arbitrary image sizes. The consumer must letterbox each
67
- image to the fixed canvas before inference, then remove the padded region and
68
- resize the alpha matte back to the original image size.
69
-
70
- Read the sidecar JSON first when present. It records the authoritative
71
- `canvas_size` (448), `matting_input_size`, `depth_variant`, `convnext_size`,
72
- input and output names, precision, model version, SHA256, and optional
73
- benchmark/parity values.
74
-
75
- ## Export Command
76
-
77
- Run Python through `uv` in the training repo:
78
-
79
- ```bash
80
- uv sync
81
- uv run python scripts/export_wbgnet_onnx.py \
82
- --variant oss \
83
- --convnext base \
84
- --precision fp32 \
85
- --no-transformer-opt \
86
- --ort-opt-level extended \
87
- --output compiled_models/withoutbg-open-weights.onnx
88
- ```
89
-
90
- Typical knobs:
91
-
92
- - `--precision fp32|fp16|int8|int4`
93
- - `--canvas-size 448` (default)
94
- - `--convnext {small,base}` (default `base`)
95
- - `--opset-version 18`
96
- - `--validate` defaults on and compares ONNX output against patched PyTorch
97
- - `--bench` runs a small CPU benchmark and writes stats into the sidecar
98
- - `--ort-offline-opt` defaults on
99
- - `--ort-opt-level extended` is portable; `all` can be faster but may bake in
100
- host CPU-specific layout choices
101
- - `--transformer-opt` can improve DepthAnything ViT latency but is off by
102
- default because it may produce provider-specific ops
103
-
104
- Required export-time checkpoints under `--checkpoint-dir`:
105
-
106
- ```text
107
- depth_anything_v2_vits.pth
108
- matting-dav2s-base_*_best.pth
109
- dinov3_convnext_base_pretrain_lvd1689m-801f2ba9.pth
110
- ```
111
-
112
- Runtime distribution only needs the `.onnx` and `.onnx.json`.
113
-
114
- ## Reference Serving Behavior
115
-
116
- Preprocessing:
117
-
118
- 1. Convert input image to RGB.
119
- 2. Preserve aspect ratio by resizing the longest side to `canvas_size`.
120
- 3. Paste the resized image at top-left of a black `canvas_size x canvas_size`
121
- RGB canvas.
122
- 4. Convert to `float32`, divide by `255.0`, transpose HWC to CHW, add batch
123
- dimension.
124
- 5. Run ONNX Runtime with `{input_name: tensor}`.
125
-
126
- Postprocessing:
127
-
128
- 1. Read `alpha` from `[1, 1, canvas, canvas]`.
129
- 2. Crop to the resized image area before padding (`alpha[:new_h, :new_w]`).
130
- 3. Resize cropped alpha back to the original image width and height.
131
- 4. For cutout output, attach alpha as the PNG alpha channel.
132
- 5. For matte output, save alpha as grayscale PNG.
133
- 6. For composite output, blend RGB over a solid background:
134
- `out = rgb * alpha + bg * (1 - alpha)`.
135
-
136
- Supported reference providers:
137
-
138
- ```text
139
- cpu -> CPUExecutionProvider
140
- cuda -> CUDAExecutionProvider
141
- coreml -> CoreMLExecutionProvider
142
- ```
143
-
144
- Provider order matters. For a CPU Docker image, use only `CPUExecutionProvider`
145
- unless the image explicitly installs GPU ONNX Runtime.
146
-
147
- ## Minimal Runtime Dependencies
148
-
149
- For a CPU Docker image, runtime code needs:
150
-
151
- ```text
152
- python >=3.11
153
- numpy
154
- pillow
155
- onnxruntime
156
- ```
157
-
158
- The full exporter also needs PyTorch, ONNX tooling, `onnxsim`,
159
- `onnxconverter-common`, and repo source modules. Keep exporter dependencies out
160
- of the runtime image unless the image is meant to export models too.
161
-
162
- ## Suggested Docker Layout
163
-
164
- Use a small runtime image and copy only the serving code and model artifacts:
165
-
166
- ```dockerfile
167
- FROM python:3.11-slim
168
-
169
- WORKDIR /app
170
-
171
- RUN pip install --no-cache-dir numpy pillow onnxruntime
172
-
173
- COPY withoutbg-open-weights.onnx /app/model.onnx
174
- COPY withoutbg-open-weights.onnx.json /app/model.onnx.json
175
- COPY space/inference.py /app/inference.py
176
-
177
- ENTRYPOINT ["python", "-c", "print('wire your serve entrypoint here')"]
178
- ```
179
-
180
- For an API server image, keep the same preprocessing and postprocessing contract.
181
- Load one `onnxruntime.InferenceSession` at process startup, warm it with a zero
182
- tensor, and reuse it across requests.
183
-
184
- ## Validation Checklist
185
-
186
- Before publishing a Docker image or model release:
187
-
188
- - Confirm both files exist: `.onnx` and `.onnx.json`.
189
- - Confirm sidecar `sha256` matches the ONNX artifact.
190
- - Run a smoke inference on at least one real image.
191
- - Inspect a cutout PNG and a matte PNG visually.
192
- - Check that output alpha has original image resolution, not `448 x 448`.
193
- - Verify the selected ORT provider is the expected one.
194
- - Include license and DINOv3 third-party terms in release metadata.
195
- - Avoid `--ort-opt-level all` for broadly distributed CPU images unless the
196
- artifact is intentionally tied to the build host architecture.
197
-
198
- ## Known Export Approximations
199
-
200
- The exporter applies ONNX-only patches:
201
-
202
- - Antialiased resize is disabled where this PyTorch/ONNX stack cannot lower it.
203
- - Some fixed-shape arithmetic is folded for the 448 canvas.
204
-
205
- These approximations are expected. The exporter validates parity against the
206
- patched PyTorch model.
207
-
208
- ## Failure Modes Agents Should Watch For
209
-
210
- - Missing sidecar: service still works with defaults, but distribution should
211
- include the sidecar because it records precision, names, SHA256, and version.
212
- - Wrong tensor layout: input must be NCHW, not NHWC.
213
- - Wrong normalization: input must be float in `[0, 1]`, not uint8 or `[-1, 1]`.
214
- - Forgetting unletterbox: padded canvas alpha will produce wrong output sizes
215
- and black-padding artifacts.
216
- - `fp16` IO mismatch: only feed float16 when sidecar says `io_fp16: true`.
217
- - Provider fallback: ONNX Runtime may silently choose CPU if CUDA/CoreML is
218
- unavailable; log `session.get_providers()[0]`.
219
- - CPU-specific offline optimization: `--ort-opt-level all` can make a model less
220
- portable across CPU architectures.
221
-
222
- ## Minimal Python Inference Skeleton
223
-
224
- ```python
225
- from pathlib import Path
226
- import json
227
- import numpy as np
228
- import onnxruntime as ort
229
- from PIL import Image
230
-
231
- model_path = Path("model.onnx")
232
- sidecar = json.loads(model_path.with_suffix(model_path.suffix + ".json").read_text())
233
- canvas = sidecar.get("canvas_size", 448)
234
- input_name = sidecar.get("input_name", "rgb")
235
-
236
- session = ort.InferenceSession(str(model_path), providers=["CPUExecutionProvider"])
237
-
238
- image = Image.open("input.jpg").convert("RGB")
239
- orig_w, orig_h = image.size
240
- scale = canvas / max(orig_w, orig_h)
241
- new_w = max(1, round(orig_w * scale))
242
- new_h = max(1, round(orig_h * scale))
243
-
244
- resized = image.resize((new_w, new_h), Image.Resampling.BILINEAR)
245
- padded = Image.new("RGB", (canvas, canvas), (0, 0, 0))
246
- padded.paste(resized, (0, 0))
247
-
248
- rgb = np.asarray(padded, dtype=np.float32) / 255.0
249
- rgb = np.transpose(rgb, (2, 0, 1))[None, ...]
250
-
251
- alpha_canvas = session.run(None, {input_name: rgb})[0][0, 0]
252
- alpha_crop = alpha_canvas[:new_h, :new_w]
253
- alpha_u8 = np.clip(alpha_crop * 255.0, 0, 255).astype(np.uint8)
254
- alpha = Image.fromarray(alpha_u8, "L").resize((orig_w, orig_h), Image.Resampling.BILINEAR)
255
-
256
- out = image.copy()
257
- out.putalpha(alpha)
258
- out.save("output.png")
259
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
withoutbg-open-weights-backbone.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a64ba43cc25557f568ad6f35ed1139049dd43f5568385fa4f9848e0b4c7123ff
3
+ size 350403090
withoutbg-open-weights.onnx CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:29930e48e9d5ecc56d6486c53c35a4c1470566c2a3359fa180b08c8d3c34ef0f
3
- size 454497726
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a22bc936a7be65f500f44955129bdde09a16d1ab3745a43224128638981cff32
3
+ size 104110104
withoutbg-open-weights.onnx.json CHANGED
@@ -1,35 +1,97 @@
1
  {
2
- "canvas_size": 448,
3
- "matting_input_size": 448,
 
 
 
 
 
 
 
 
 
4
  "opset_version": 18,
5
  "precision": "fp32",
6
- "requested_precision": "fp32",
7
- "io_fp16": false,
8
- "transformer_opt": false,
9
- "ort_offline_opt": true,
10
- "ort_opt_level": "extended",
11
- "variant": "oss",
12
- "depth_variant": "dav2s",
13
- "convnext_size": "base",
14
- "refiner": false,
15
- "model_version": "10.0.0",
16
- "input_name": "rgb",
17
- "output_name": "alpha",
18
- "input_dtype": "float32",
19
- "input_shape": [
20
- 1,
21
- 3,
22
- 448,
23
- 448
24
- ],
25
- "output_shape": [
26
- 1,
27
- 1,
28
- 448,
29
- 448
30
  ],
31
- "size_mb": 454.5,
32
- "sha256": "29930e48e9d5ecc56d6486c53c35a4c1470566c2a3359fa180b08c8d3c34ef0f",
33
- "mae": 3.6705664285818784e-08,
34
- "max_abs": 3.0040740966796875e-05
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  }
 
1
  {
2
+ "schema_version": 3,
3
+ "pipeline": "routed",
4
+ "variant": "oss",
5
+ "model_version": "10.8.0",
6
+ "components": {
7
+ "depth": "depth_anything_v2_vits.pth",
8
+ "convnext": "dinov3_convnext_base_pretrain_lvd1689m-801f2ba9.pth",
9
+ "matting": "matting-dav2s-base_10.0.0_best_trunk.pth",
10
+ "router": "router_10.1.0_best.pth",
11
+ "birefnet": "BiRefNet"
12
+ },
13
  "opset_version": 18,
14
  "precision": "fp32",
15
+ "max_inference_size": [
16
+ 4096,
17
+ 4096
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  ],
19
+ "router": {
20
+ "file": "withoutbg-open-weights-backbone.onnx",
21
+ "sha256": "a64ba43cc25557f568ad6f35ed1139049dd43f5568385fa4f9848e0b4c7123ff",
22
+ "inputs": {
23
+ "rgb_matting": {
24
+ "size": 448,
25
+ "resize": "stretch",
26
+ "interpolation": "bilinear",
27
+ "antialias": false
28
+ }
29
+ },
30
+ "logits_name": "route_logits",
31
+ "feature_names": [
32
+ "f0",
33
+ "f1",
34
+ "f2",
35
+ "f3"
36
+ ],
37
+ "categories": [
38
+ "fine_strand",
39
+ "soft_detail",
40
+ "transparency",
41
+ "hard_opaque",
42
+ "flat_scene",
43
+ "vehicle"
44
+ ],
45
+ "birefnet_categories": [
46
+ "hard_opaque",
47
+ "flat_scene",
48
+ "vehicle"
49
+ ],
50
+ "mae": 1.819754515963723e-06,
51
+ "max_abs": 0.00189208984375
52
+ },
53
+ "coarse": {
54
+ "file": "withoutbg-open-weights.onnx",
55
+ "sha256": "a22bc936a7be65f500f44955129bdde09a16d1ab3745a43224128638981cff32",
56
+ "inputs": {
57
+ "rgb_depth": {
58
+ "size": 518,
59
+ "resize": "stretch",
60
+ "interpolation": "bicubic",
61
+ "antialias": true
62
+ },
63
+ "rgb_matting": {
64
+ "size": 448,
65
+ "resize": "stretch",
66
+ "interpolation": "bilinear",
67
+ "antialias": false
68
+ }
69
+ },
70
+ "feature_names": [
71
+ "f0",
72
+ "f1",
73
+ "f2",
74
+ "f3"
75
+ ],
76
+ "output_name": "coarse_alpha",
77
+ "output_size": 448,
78
+ "mae": 5.89670491990546e-07,
79
+ "max_abs": 1.683831214904785e-05
80
+ },
81
+ "birefnet": {
82
+ "file": "birefnet-general.onnx",
83
+ "sha256": "28c988df0383b7c9925139b7aa012c33ce750e8552bd75a628346096acb4c0bb",
84
+ "inputs": {
85
+ "rgb": {
86
+ "size": 1024,
87
+ "resize": "stretch",
88
+ "interpolation": "bilinear",
89
+ "antialias": false
90
+ }
91
+ },
92
+ "output_name": "alpha",
93
+ "output_size": 1024,
94
+ "mae": 2.580805968932509e-08,
95
+ "max_abs": 1.3899352779844776e-07
96
+ }
97
  }