bgremover-onnx 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,50 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ .eggs/
11
+ pip-wheel-metadata/
12
+
13
+ # Virtual environments
14
+ .venv/
15
+ venv/
16
+ env/
17
+
18
+ # Testing and tooling caches
19
+ .pytest_cache/
20
+ .ruff_cache/
21
+ .mypy_cache/
22
+ .coverage
23
+ .coverage.*
24
+ htmlcov/
25
+ coverage.xml
26
+
27
+ # Model weights are downloaded into a cache, never committed
28
+ *.onnx
29
+ models/
30
+
31
+ # Sample and scratch outputs
32
+ *_nobg.png
33
+ outputs/
34
+ uploads/
35
+ temp/
36
+
37
+ # Environment
38
+ .env
39
+ .env.*
40
+
41
+ # OS
42
+ .DS_Store
43
+ Thumbs.db
44
+
45
+ # Editors
46
+ .vscode/
47
+ .idea/
48
+ *.swp
49
+ *.swo
50
+ *~
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Asim Mahmudov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,290 @@
1
+ Metadata-Version: 2.5
2
+ Name: bgremover-onnx
3
+ Version: 2.0.0
4
+ Summary: Remove image backgrounds with ONNX segmentation models. Library, CLI and HTTP API.
5
+ Project-URL: Homepage, https://github.com/asimmakhmudov/bgremover
6
+ Project-URL: Issues, https://github.com/asimmakhmudov/bgremover/issues
7
+ Project-URL: Documentation, https://github.com/asimmakhmudov/bgremover#readme
8
+ Project-URL: Source, https://github.com/asimmakhmudov/bgremover
9
+ Author: Asim Mahmudov
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: background-removal,birefnet,isnet,onnx,segmentation,u2net
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Multimedia :: Graphics
23
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
24
+ Requires-Python: >=3.10
25
+ Requires-Dist: numpy>=1.24
26
+ Requires-Dist: onnxruntime>=1.16
27
+ Requires-Dist: pillow>=10.0
28
+ Provides-Extra: api
29
+ Requires-Dist: fastapi>=0.110; extra == 'api'
30
+ Requires-Dist: python-multipart>=0.0.9; extra == 'api'
31
+ Requires-Dist: uvicorn>=0.27; extra == 'api'
32
+ Provides-Extra: dev
33
+ Requires-Dist: build>=1.2; extra == 'dev'
34
+ Requires-Dist: fastapi>=0.110; extra == 'dev'
35
+ Requires-Dist: httpx>=0.27; extra == 'dev'
36
+ Requires-Dist: pytest>=8.0; extra == 'dev'
37
+ Requires-Dist: python-multipart>=0.0.9; extra == 'dev'
38
+ Requires-Dist: ruff>=0.5; extra == 'dev'
39
+ Requires-Dist: twine>=5.0; extra == 'dev'
40
+ Requires-Dist: uvicorn>=0.27; extra == 'dev'
41
+ Description-Content-Type: text/markdown
42
+
43
+ # bgremover
44
+
45
+ [![PyPI](https://img.shields.io/pypi/v/bgremover-onnx.svg)](https://pypi.org/project/bgremover-onnx/)
46
+ [![Python](https://img.shields.io/pypi/pyversions/bgremover-onnx.svg)](https://pypi.org/project/bgremover-onnx/)
47
+ [![CI](https://github.com/asimmakhmudov/bgremover/actions/workflows/ci.yml/badge.svg)](https://github.com/asimmakhmudov/bgremover/actions/workflows/ci.yml)
48
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/asimmakhmudov/bgremover/blob/master/LICENSE)
49
+
50
+ Remove image backgrounds with pretrained ONNX segmentation models — as a Python
51
+ library, a command line tool, or an HTTP API.
52
+
53
+ It runs U²-Net, IS-Net and BiRefNet with its own inference code: numpy and
54
+ Pillow for the image math, onnxruntime for the model. There is no `rembg`
55
+ dependency. (The weight files are hosted on rembg's releases page, which is the
56
+ only thing taken from that project.)
57
+
58
+ ```python
59
+ from bgremover import remove
60
+
61
+ with open("photo.jpg", "rb") as fh:
62
+ png = remove(fh.read())
63
+ ```
64
+
65
+ ## Install
66
+
67
+ With pip:
68
+
69
+ ```bash
70
+ pip install bgremover-onnx # library + CLI
71
+ pip install "bgremover-onnx[api]" # also the HTTP API
72
+ ```
73
+
74
+ With [uv](https://docs.astral.sh/uv/):
75
+
76
+ ```bash
77
+ uv add bgremover-onnx # add to a uv project
78
+ uv add "bgremover-onnx[api]"
79
+ uv pip install bgremover-onnx # into the active environment
80
+ uvx --from bgremover-onnx bgremover remove photo.jpg # run the CLI without installing it
81
+ uvx --from "bgremover-onnx[api]" bgremover serve # run the API without installing it
82
+ ```
83
+
84
+ The package is published on PyPI as `bgremover-onnx`; you still import it as
85
+ `bgremover` and run the `bgremover` command.
86
+
87
+ Python 3.10 or newer. Model weights are downloaded on first use and cached in
88
+ `~/.cache/bgremover` (override with `BGREMOVER_HOME`).
89
+
90
+ ## Project structure
91
+
92
+ ```
93
+ bgremover/
94
+ ├── src/bgremover/
95
+ │ ├── __init__.py Public API, resolved lazily (remove, Remover, MODELS, ...)
96
+ │ ├── models.py Model registry: URL, SHA-256, input size, normalisation
97
+ │ ├── download.py Download, hash-check and cache weights (stdlib only)
98
+ │ ├── session.py Remover: owns the ONNX session, runs the pipeline
99
+ │ ├── processing.py Pure image math: preprocess, postprocess, refine, cutout, trim
100
+ │ ├── images.py Decode any input (bytes / path / PIL / numpy), encode output
101
+ │ ├── cli.py `bgremover` command: remove, models, download, serve
102
+ │ └── server/ Optional HTTP API (installed with the [api] extra)
103
+ │ ├── app.py FastAPI app, routes, error handling, bounded worker pool
104
+ │ ├── config.py Settings read from BGREMOVER_* environment variables
105
+ │ └── fetch.py SSRF-safe image fetching for the URL route
106
+ ├── tests/ pytest suite (fake ONNX session; real models with -m model)
107
+ ├── .github/workflows/
108
+ │ ├── ci.yml Lint, test on 3.10-3.13, build the package, smoke-test Docker
109
+ │ └── publish.yml Publish to PyPI when a GitHub release is published
110
+ ├── Dockerfile Two stages: bake weights in, then a slim non-root runtime
111
+ └── pyproject.toml Package metadata, extras, ruff and pytest config
112
+ ```
113
+
114
+ How a request flows:
115
+
116
+ ```
117
+ input (bytes / path / PIL / numpy)
118
+ -> images.load_image decode to an RGB PIL image
119
+ -> processing.preprocess resize to the model's input, normalise, NCHW tensor
120
+ -> onnxruntime run the segmentation model (session.Remover)
121
+ -> processing.postprocess scale the mask back to the image size
122
+ -> processing.refine_mask optional: harden + feather the edge
123
+ -> processing.cutout / trim / apply_background
124
+ -> images.encode_image same type out as came in
125
+ ```
126
+
127
+ The core library never imports the `server` package, and `processing.py` does no
128
+ I/O, so each layer can be tested on its own.
129
+
130
+ ## Library
131
+
132
+ `remove()` returns the same type you give it: bytes in, bytes out; a PIL image
133
+ in, a PIL image out; a numpy array in, a numpy array out; a path in, a PIL image
134
+ out.
135
+
136
+ ```python
137
+ from bgremover import remove, Remover
138
+
139
+ png_bytes = remove(jpeg_bytes) # -> bytes (PNG)
140
+ image = remove("photo.jpg") # -> PIL.Image (RGBA)
141
+ array = remove(numpy_rgb) # -> numpy array (RGBA)
142
+
143
+ # Reuse one model across many images - the session is built once.
144
+ remover = Remover("isnet-general-use")
145
+ for path in paths:
146
+ remover.remove(path).save(path.with_suffix(".png"))
147
+ ```
148
+
149
+ Options for `remove()` / `Remover.remove()`:
150
+
151
+ | Option | Default | Meaning |
152
+ | --- | --- | --- |
153
+ | `only_mask` | `False` | Return the grayscale mask instead of the cutout |
154
+ | `refine` | `False` | Harden the confident regions and feather the edge |
155
+ | `foreground_threshold` | `240` | With `refine`: at or above this is fully opaque |
156
+ | `background_threshold` | `15` | With `refine`: at or below this is fully transparent |
157
+ | `erode_size` | `0` | With `refine`: pull the edge in by N pixels |
158
+ | `blur_radius` | `1.0` | With `refine`: feather radius |
159
+ | `background` | `None` | Composite onto a colour (`"white"`, `"#ff00ff"`, a tuple) |
160
+ | `trim_border` | `False` | Crop fully transparent borders away |
161
+ | `output_format` | `"PNG"` | Encoding used when the input was bytes |
162
+ | `quality` | `None` | JPEG/WEBP quality when the input was bytes |
163
+
164
+ `Remover` is lazy and thread-safe: constructing one touches neither the network
165
+ nor the disk, and the ONNX session is built on first use and then shared.
166
+
167
+ ## CLI
168
+
169
+ ```bash
170
+ bgremover remove photo.jpg # -> photo_nobg.png
171
+ bgremover remove photo.jpg -o out.png --refine --trim
172
+ bgremover remove *.jpg -o cutouts/ -m isnet-general-use
173
+ bgremover remove photo.jpg --bg white -o flat.jpg
174
+ bgremover remove photo.jpg --only-mask -o mask.png
175
+ cat photo.jpg | bgremover remove - > out.png
176
+
177
+ bgremover models # list models, show what is cached
178
+ bgremover download u2net isnet-general-use # pre-fetch weights
179
+ bgremover download --all
180
+ bgremover download --check u2net # re-hash a cached file
181
+ bgremover serve --port 3001 # run the HTTP API
182
+ ```
183
+
184
+ ## HTTP API
185
+
186
+ ```bash
187
+ pip install "bgremover-onnx[api]"
188
+ bgremover serve --port 3001 # docs at http://localhost:3001/docs
189
+ ```
190
+
191
+ | Route | What it does |
192
+ | --- | --- |
193
+ | `POST /remove-background` | Multipart upload, field name `image` |
194
+ | `POST /remove-background-url` | JSON body with `imageUrl` (or `image_url`) |
195
+ | `GET /models` | Models this server will serve |
196
+ | `GET /health` | Health check |
197
+ | `GET /` | API description |
198
+
199
+ ```bash
200
+ curl -X POST -F "image=@photo.jpg" \
201
+ http://localhost:3001/remove-background --output out.png
202
+
203
+ curl -X POST -H "Content-Type: application/json" \
204
+ -d '{"imageUrl": "https://example.com/photo.jpg"}' \
205
+ http://localhost:3001/remove-background-url --output out.png
206
+ ```
207
+
208
+ Both routes accept the same options as the library: `model`, `only_mask`,
209
+ `refine`, `background`, `trim`, `format`, `quality` — as form fields on the
210
+ upload route, as JSON keys on the URL route.
211
+
212
+ Errors are always `{"error": "..."}` with a 4xx or 5xx status.
213
+
214
+ ### Server environment variables
215
+
216
+ | Variable | Default | Meaning |
217
+ | --- | --- | --- |
218
+ | `BGREMOVER_MAX_UPLOAD_BYTES` | `10485760` | Upload / fetch size limit (10 MB) |
219
+ | `BGREMOVER_MODELS` | the six models ≤ 180 MB | Comma-separated allow-list |
220
+ | `BGREMOVER_DEFAULT_MODEL` | `u2net` | Model used when a request names none |
221
+ | `BGREMOVER_MAX_CONCURRENCY` | `2` | Inferences allowed to run at once |
222
+ | `BGREMOVER_FETCH_TIMEOUT` | `15` | Seconds allowed for fetching a remote image |
223
+ | `BGREMOVER_MAX_REDIRECTS` | `3` | Redirect hops, each re-checked |
224
+ | `BGREMOVER_ALLOW_PRIVATE_URLS` | `false` | Let URL fetches reach private addresses |
225
+ | `BGREMOVER_CORS_ORIGINS` | `*` | Comma-separated CORS origins |
226
+ | `BGREMOVER_PRELOAD` | `false` | Load the default model at startup |
227
+
228
+ Library-wide: `BGREMOVER_HOME` (cache directory),
229
+ `BGREMOVER_MODEL_BASE_URL` (weights mirror), `BGREMOVER_PROVIDERS`
230
+ (onnxruntime execution providers), `BGREMOVER_NUM_THREADS`.
231
+
232
+ The URL route is deliberately strict: only `http`/`https`, and the host must
233
+ resolve to a public address — on every redirect hop too, so a public URL cannot
234
+ bounce a request into `169.254.169.254` or your private network.
235
+
236
+ ## Models
237
+
238
+ | Name | Size | Input | License | Notes |
239
+ | --- | --- | --- | --- | --- |
240
+ | `u2net` | 168 MB | 320² | Apache-2.0 | General purpose. The default. |
241
+ | `u2netp` | 4 MB | 320² | Apache-2.0 | Small and fast, lower quality |
242
+ | `u2net_human_seg` | 168 MB | 320² | Apache-2.0 | Fine-tuned for people |
243
+ | `silueta` | 42 MB | 320² | Apache-2.0 | u2net pruned; near-u2net quality |
244
+ | `isnet-general-use` | 170 MB | 1024² | Apache-2.0 | Sharper edges, slower |
245
+ | `isnet-anime` | 168 MB | 1024² | Apache-2.0 | Anime and illustration |
246
+ | `birefnet-general-lite` | 214 MB | 1024² | MIT | Best quality that fits in ~8 GB RAM |
247
+ | `birefnet-general` | 928 MB | 1024² | MIT | Highest quality; needs a GPU or 16 GB+ RAM |
248
+
249
+ Every entry carries a SHA-256 that is checked on download; a mismatch is
250
+ discarded rather than cached.
251
+
252
+ The two BiRefNet models are **not** in the server's default allow-list: they are
253
+ large and memory-hungry. Add them with `BGREMOVER_MODELS` only on a machine that
254
+ can hold them.
255
+
256
+ ## Docker
257
+
258
+ ```bash
259
+ docker build -t bgremover . # bakes in u2net
260
+ docker build --build-arg MODELS=u2netp,silueta -t bgremover .
261
+ docker run -p 3001:3001 bgremover
262
+ ```
263
+
264
+ The weights are downloaded in a separate build stage and copied into the final
265
+ image, so a container does not fetch a model on its first request. Set
266
+ `BGREMOVER_MODELS` at run time to match what you baked in.
267
+
268
+ ## Development
269
+
270
+ ```bash
271
+ git clone https://github.com/asimmakhmudov/bgremover.git
272
+ cd bgremover
273
+
274
+ # with uv
275
+ uv venv && uv pip install -e ".[dev]"
276
+
277
+ # or with pip
278
+ python -m venv .venv && source .venv/bin/activate && pip install -e ".[dev]"
279
+
280
+ pytest -q # fast: a fake ONNX session, no network
281
+ pytest -q -m model # also runs real weights (downloads ~5 MB)
282
+ ruff check src tests && ruff format --check src tests
283
+ ```
284
+
285
+ Contributions are welcome. Please open an issue first for larger changes, and
286
+ keep `pytest -q` and `ruff` green.
287
+
288
+ ## License
289
+
290
+ MIT. The model weights carry their own licenses, listed in the table above.
@@ -0,0 +1,248 @@
1
+ # bgremover
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/bgremover-onnx.svg)](https://pypi.org/project/bgremover-onnx/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/bgremover-onnx.svg)](https://pypi.org/project/bgremover-onnx/)
5
+ [![CI](https://github.com/asimmakhmudov/bgremover/actions/workflows/ci.yml/badge.svg)](https://github.com/asimmakhmudov/bgremover/actions/workflows/ci.yml)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/asimmakhmudov/bgremover/blob/master/LICENSE)
7
+
8
+ Remove image backgrounds with pretrained ONNX segmentation models — as a Python
9
+ library, a command line tool, or an HTTP API.
10
+
11
+ It runs U²-Net, IS-Net and BiRefNet with its own inference code: numpy and
12
+ Pillow for the image math, onnxruntime for the model. There is no `rembg`
13
+ dependency. (The weight files are hosted on rembg's releases page, which is the
14
+ only thing taken from that project.)
15
+
16
+ ```python
17
+ from bgremover import remove
18
+
19
+ with open("photo.jpg", "rb") as fh:
20
+ png = remove(fh.read())
21
+ ```
22
+
23
+ ## Install
24
+
25
+ With pip:
26
+
27
+ ```bash
28
+ pip install bgremover-onnx # library + CLI
29
+ pip install "bgremover-onnx[api]" # also the HTTP API
30
+ ```
31
+
32
+ With [uv](https://docs.astral.sh/uv/):
33
+
34
+ ```bash
35
+ uv add bgremover-onnx # add to a uv project
36
+ uv add "bgremover-onnx[api]"
37
+ uv pip install bgremover-onnx # into the active environment
38
+ uvx --from bgremover-onnx bgremover remove photo.jpg # run the CLI without installing it
39
+ uvx --from "bgremover-onnx[api]" bgremover serve # run the API without installing it
40
+ ```
41
+
42
+ The package is published on PyPI as `bgremover-onnx`; you still import it as
43
+ `bgremover` and run the `bgremover` command.
44
+
45
+ Python 3.10 or newer. Model weights are downloaded on first use and cached in
46
+ `~/.cache/bgremover` (override with `BGREMOVER_HOME`).
47
+
48
+ ## Project structure
49
+
50
+ ```
51
+ bgremover/
52
+ ├── src/bgremover/
53
+ │ ├── __init__.py Public API, resolved lazily (remove, Remover, MODELS, ...)
54
+ │ ├── models.py Model registry: URL, SHA-256, input size, normalisation
55
+ │ ├── download.py Download, hash-check and cache weights (stdlib only)
56
+ │ ├── session.py Remover: owns the ONNX session, runs the pipeline
57
+ │ ├── processing.py Pure image math: preprocess, postprocess, refine, cutout, trim
58
+ │ ├── images.py Decode any input (bytes / path / PIL / numpy), encode output
59
+ │ ├── cli.py `bgremover` command: remove, models, download, serve
60
+ │ └── server/ Optional HTTP API (installed with the [api] extra)
61
+ │ ├── app.py FastAPI app, routes, error handling, bounded worker pool
62
+ │ ├── config.py Settings read from BGREMOVER_* environment variables
63
+ │ └── fetch.py SSRF-safe image fetching for the URL route
64
+ ├── tests/ pytest suite (fake ONNX session; real models with -m model)
65
+ ├── .github/workflows/
66
+ │ ├── ci.yml Lint, test on 3.10-3.13, build the package, smoke-test Docker
67
+ │ └── publish.yml Publish to PyPI when a GitHub release is published
68
+ ├── Dockerfile Two stages: bake weights in, then a slim non-root runtime
69
+ └── pyproject.toml Package metadata, extras, ruff and pytest config
70
+ ```
71
+
72
+ How a request flows:
73
+
74
+ ```
75
+ input (bytes / path / PIL / numpy)
76
+ -> images.load_image decode to an RGB PIL image
77
+ -> processing.preprocess resize to the model's input, normalise, NCHW tensor
78
+ -> onnxruntime run the segmentation model (session.Remover)
79
+ -> processing.postprocess scale the mask back to the image size
80
+ -> processing.refine_mask optional: harden + feather the edge
81
+ -> processing.cutout / trim / apply_background
82
+ -> images.encode_image same type out as came in
83
+ ```
84
+
85
+ The core library never imports the `server` package, and `processing.py` does no
86
+ I/O, so each layer can be tested on its own.
87
+
88
+ ## Library
89
+
90
+ `remove()` returns the same type you give it: bytes in, bytes out; a PIL image
91
+ in, a PIL image out; a numpy array in, a numpy array out; a path in, a PIL image
92
+ out.
93
+
94
+ ```python
95
+ from bgremover import remove, Remover
96
+
97
+ png_bytes = remove(jpeg_bytes) # -> bytes (PNG)
98
+ image = remove("photo.jpg") # -> PIL.Image (RGBA)
99
+ array = remove(numpy_rgb) # -> numpy array (RGBA)
100
+
101
+ # Reuse one model across many images - the session is built once.
102
+ remover = Remover("isnet-general-use")
103
+ for path in paths:
104
+ remover.remove(path).save(path.with_suffix(".png"))
105
+ ```
106
+
107
+ Options for `remove()` / `Remover.remove()`:
108
+
109
+ | Option | Default | Meaning |
110
+ | --- | --- | --- |
111
+ | `only_mask` | `False` | Return the grayscale mask instead of the cutout |
112
+ | `refine` | `False` | Harden the confident regions and feather the edge |
113
+ | `foreground_threshold` | `240` | With `refine`: at or above this is fully opaque |
114
+ | `background_threshold` | `15` | With `refine`: at or below this is fully transparent |
115
+ | `erode_size` | `0` | With `refine`: pull the edge in by N pixels |
116
+ | `blur_radius` | `1.0` | With `refine`: feather radius |
117
+ | `background` | `None` | Composite onto a colour (`"white"`, `"#ff00ff"`, a tuple) |
118
+ | `trim_border` | `False` | Crop fully transparent borders away |
119
+ | `output_format` | `"PNG"` | Encoding used when the input was bytes |
120
+ | `quality` | `None` | JPEG/WEBP quality when the input was bytes |
121
+
122
+ `Remover` is lazy and thread-safe: constructing one touches neither the network
123
+ nor the disk, and the ONNX session is built on first use and then shared.
124
+
125
+ ## CLI
126
+
127
+ ```bash
128
+ bgremover remove photo.jpg # -> photo_nobg.png
129
+ bgremover remove photo.jpg -o out.png --refine --trim
130
+ bgremover remove *.jpg -o cutouts/ -m isnet-general-use
131
+ bgremover remove photo.jpg --bg white -o flat.jpg
132
+ bgremover remove photo.jpg --only-mask -o mask.png
133
+ cat photo.jpg | bgremover remove - > out.png
134
+
135
+ bgremover models # list models, show what is cached
136
+ bgremover download u2net isnet-general-use # pre-fetch weights
137
+ bgremover download --all
138
+ bgremover download --check u2net # re-hash a cached file
139
+ bgremover serve --port 3001 # run the HTTP API
140
+ ```
141
+
142
+ ## HTTP API
143
+
144
+ ```bash
145
+ pip install "bgremover-onnx[api]"
146
+ bgremover serve --port 3001 # docs at http://localhost:3001/docs
147
+ ```
148
+
149
+ | Route | What it does |
150
+ | --- | --- |
151
+ | `POST /remove-background` | Multipart upload, field name `image` |
152
+ | `POST /remove-background-url` | JSON body with `imageUrl` (or `image_url`) |
153
+ | `GET /models` | Models this server will serve |
154
+ | `GET /health` | Health check |
155
+ | `GET /` | API description |
156
+
157
+ ```bash
158
+ curl -X POST -F "image=@photo.jpg" \
159
+ http://localhost:3001/remove-background --output out.png
160
+
161
+ curl -X POST -H "Content-Type: application/json" \
162
+ -d '{"imageUrl": "https://example.com/photo.jpg"}' \
163
+ http://localhost:3001/remove-background-url --output out.png
164
+ ```
165
+
166
+ Both routes accept the same options as the library: `model`, `only_mask`,
167
+ `refine`, `background`, `trim`, `format`, `quality` — as form fields on the
168
+ upload route, as JSON keys on the URL route.
169
+
170
+ Errors are always `{"error": "..."}` with a 4xx or 5xx status.
171
+
172
+ ### Server environment variables
173
+
174
+ | Variable | Default | Meaning |
175
+ | --- | --- | --- |
176
+ | `BGREMOVER_MAX_UPLOAD_BYTES` | `10485760` | Upload / fetch size limit (10 MB) |
177
+ | `BGREMOVER_MODELS` | the six models ≤ 180 MB | Comma-separated allow-list |
178
+ | `BGREMOVER_DEFAULT_MODEL` | `u2net` | Model used when a request names none |
179
+ | `BGREMOVER_MAX_CONCURRENCY` | `2` | Inferences allowed to run at once |
180
+ | `BGREMOVER_FETCH_TIMEOUT` | `15` | Seconds allowed for fetching a remote image |
181
+ | `BGREMOVER_MAX_REDIRECTS` | `3` | Redirect hops, each re-checked |
182
+ | `BGREMOVER_ALLOW_PRIVATE_URLS` | `false` | Let URL fetches reach private addresses |
183
+ | `BGREMOVER_CORS_ORIGINS` | `*` | Comma-separated CORS origins |
184
+ | `BGREMOVER_PRELOAD` | `false` | Load the default model at startup |
185
+
186
+ Library-wide: `BGREMOVER_HOME` (cache directory),
187
+ `BGREMOVER_MODEL_BASE_URL` (weights mirror), `BGREMOVER_PROVIDERS`
188
+ (onnxruntime execution providers), `BGREMOVER_NUM_THREADS`.
189
+
190
+ The URL route is deliberately strict: only `http`/`https`, and the host must
191
+ resolve to a public address — on every redirect hop too, so a public URL cannot
192
+ bounce a request into `169.254.169.254` or your private network.
193
+
194
+ ## Models
195
+
196
+ | Name | Size | Input | License | Notes |
197
+ | --- | --- | --- | --- | --- |
198
+ | `u2net` | 168 MB | 320² | Apache-2.0 | General purpose. The default. |
199
+ | `u2netp` | 4 MB | 320² | Apache-2.0 | Small and fast, lower quality |
200
+ | `u2net_human_seg` | 168 MB | 320² | Apache-2.0 | Fine-tuned for people |
201
+ | `silueta` | 42 MB | 320² | Apache-2.0 | u2net pruned; near-u2net quality |
202
+ | `isnet-general-use` | 170 MB | 1024² | Apache-2.0 | Sharper edges, slower |
203
+ | `isnet-anime` | 168 MB | 1024² | Apache-2.0 | Anime and illustration |
204
+ | `birefnet-general-lite` | 214 MB | 1024² | MIT | Best quality that fits in ~8 GB RAM |
205
+ | `birefnet-general` | 928 MB | 1024² | MIT | Highest quality; needs a GPU or 16 GB+ RAM |
206
+
207
+ Every entry carries a SHA-256 that is checked on download; a mismatch is
208
+ discarded rather than cached.
209
+
210
+ The two BiRefNet models are **not** in the server's default allow-list: they are
211
+ large and memory-hungry. Add them with `BGREMOVER_MODELS` only on a machine that
212
+ can hold them.
213
+
214
+ ## Docker
215
+
216
+ ```bash
217
+ docker build -t bgremover . # bakes in u2net
218
+ docker build --build-arg MODELS=u2netp,silueta -t bgremover .
219
+ docker run -p 3001:3001 bgremover
220
+ ```
221
+
222
+ The weights are downloaded in a separate build stage and copied into the final
223
+ image, so a container does not fetch a model on its first request. Set
224
+ `BGREMOVER_MODELS` at run time to match what you baked in.
225
+
226
+ ## Development
227
+
228
+ ```bash
229
+ git clone https://github.com/asimmakhmudov/bgremover.git
230
+ cd bgremover
231
+
232
+ # with uv
233
+ uv venv && uv pip install -e ".[dev]"
234
+
235
+ # or with pip
236
+ python -m venv .venv && source .venv/bin/activate && pip install -e ".[dev]"
237
+
238
+ pytest -q # fast: a fake ONNX session, no network
239
+ pytest -q -m model # also runs real weights (downloads ~5 MB)
240
+ ruff check src tests && ruff format --check src tests
241
+ ```
242
+
243
+ Contributions are welcome. Please open an issue first for larger changes, and
244
+ keep `pytest -q` and `ruff` green.
245
+
246
+ ## License
247
+
248
+ MIT. The model weights carry their own licenses, listed in the table above.
@@ -0,0 +1,80 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "bgremover-onnx"
7
+ dynamic = ["version"]
8
+ description = "Remove image backgrounds with ONNX segmentation models. Library, CLI and HTTP API."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Asim Mahmudov" }]
13
+ keywords = ["background-removal", "segmentation", "onnx", "u2net", "isnet", "birefnet"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Topic :: Multimedia :: Graphics",
25
+ "Topic :: Scientific/Engineering :: Image Processing",
26
+ ]
27
+ dependencies = [
28
+ "numpy>=1.24",
29
+ "pillow>=10.0",
30
+ "onnxruntime>=1.16",
31
+ ]
32
+
33
+ [project.optional-dependencies]
34
+ api = [
35
+ "fastapi>=0.110",
36
+ "uvicorn>=0.27",
37
+ "python-multipart>=0.0.9",
38
+ ]
39
+ dev = [
40
+ "bgremover-onnx[api]",
41
+ "pytest>=8.0",
42
+ "httpx>=0.27",
43
+ "ruff>=0.5",
44
+ "build>=1.2",
45
+ "twine>=5.0",
46
+ ]
47
+
48
+ [project.urls]
49
+ Homepage = "https://github.com/asimmakhmudov/bgremover"
50
+ Issues = "https://github.com/asimmakhmudov/bgremover/issues"
51
+ Documentation = "https://github.com/asimmakhmudov/bgremover#readme"
52
+ Source = "https://github.com/asimmakhmudov/bgremover"
53
+
54
+ [project.scripts]
55
+ bgremover = "bgremover.cli:main"
56
+
57
+ [tool.hatch.version]
58
+ path = "src/bgremover/__init__.py"
59
+
60
+ [tool.hatch.build.targets.wheel]
61
+ packages = ["src/bgremover"]
62
+
63
+ [tool.hatch.build.targets.sdist]
64
+ include = ["src/bgremover", "tests", "README.md", "LICENSE", "pyproject.toml"]
65
+
66
+ [tool.ruff]
67
+ line-length = 100
68
+ target-version = "py310"
69
+
70
+ [tool.ruff.lint]
71
+ select = ["E", "F", "I", "UP", "B"]
72
+
73
+ [tool.pytest.ini_options]
74
+ testpaths = ["tests"]
75
+ # Model tests download real weights, so they are opt-in: `pytest -m model`.
76
+ addopts = "-m 'not model'"
77
+ markers = [
78
+ "model: downloads and runs a real ONNX model (needs network)",
79
+ ]
80
+ filterwarnings = ["error::DeprecationWarning"]