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.
- bgremover_onnx-2.0.0/.gitignore +50 -0
- bgremover_onnx-2.0.0/LICENSE +21 -0
- bgremover_onnx-2.0.0/PKG-INFO +290 -0
- bgremover_onnx-2.0.0/README.md +248 -0
- bgremover_onnx-2.0.0/pyproject.toml +80 -0
- bgremover_onnx-2.0.0/src/bgremover/__init__.py +60 -0
- bgremover_onnx-2.0.0/src/bgremover/cli.py +290 -0
- bgremover_onnx-2.0.0/src/bgremover/download.py +184 -0
- bgremover_onnx-2.0.0/src/bgremover/images.py +148 -0
- bgremover_onnx-2.0.0/src/bgremover/models.py +157 -0
- bgremover_onnx-2.0.0/src/bgremover/processing.py +160 -0
- bgremover_onnx-2.0.0/src/bgremover/server/__init__.py +16 -0
- bgremover_onnx-2.0.0/src/bgremover/server/app.py +343 -0
- bgremover_onnx-2.0.0/src/bgremover/server/config.py +114 -0
- bgremover_onnx-2.0.0/src/bgremover/server/fetch.py +169 -0
- bgremover_onnx-2.0.0/src/bgremover/session.py +196 -0
- bgremover_onnx-2.0.0/tests/conftest.py +95 -0
- bgremover_onnx-2.0.0/tests/test_architecture.py +136 -0
- bgremover_onnx-2.0.0/tests/test_cli.py +121 -0
- bgremover_onnx-2.0.0/tests/test_download.py +137 -0
- bgremover_onnx-2.0.0/tests/test_images.py +111 -0
- bgremover_onnx-2.0.0/tests/test_models.py +89 -0
- bgremover_onnx-2.0.0/tests/test_processing.py +129 -0
- bgremover_onnx-2.0.0/tests/test_real_model.py +82 -0
- bgremover_onnx-2.0.0/tests/test_server.py +293 -0
- bgremover_onnx-2.0.0/tests/test_session.py +173 -0
|
@@ -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
|
+
[](https://pypi.org/project/bgremover-onnx/)
|
|
46
|
+
[](https://pypi.org/project/bgremover-onnx/)
|
|
47
|
+
[](https://github.com/asimmakhmudov/bgremover/actions/workflows/ci.yml)
|
|
48
|
+
[](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
|
+
[](https://pypi.org/project/bgremover-onnx/)
|
|
4
|
+
[](https://pypi.org/project/bgremover-onnx/)
|
|
5
|
+
[](https://github.com/asimmakhmudov/bgremover/actions/workflows/ci.yml)
|
|
6
|
+
[](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"]
|