snipbg 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.
- snipbg-2.0.0/.gitignore +50 -0
- snipbg-2.0.0/LICENSE +21 -0
- snipbg-2.0.0/PKG-INFO +322 -0
- snipbg-2.0.0/README.md +280 -0
- snipbg-2.0.0/pyproject.toml +83 -0
- snipbg-2.0.0/src/snipbg/__init__.py +60 -0
- snipbg-2.0.0/src/snipbg/cli.py +290 -0
- snipbg-2.0.0/src/snipbg/download.py +184 -0
- snipbg-2.0.0/src/snipbg/images.py +148 -0
- snipbg-2.0.0/src/snipbg/models.py +157 -0
- snipbg-2.0.0/src/snipbg/processing.py +160 -0
- snipbg-2.0.0/src/snipbg/server/__init__.py +16 -0
- snipbg-2.0.0/src/snipbg/server/app.py +343 -0
- snipbg-2.0.0/src/snipbg/server/config.py +114 -0
- snipbg-2.0.0/src/snipbg/server/fetch.py +169 -0
- snipbg-2.0.0/src/snipbg/session.py +196 -0
- snipbg-2.0.0/tests/conftest.py +95 -0
- snipbg-2.0.0/tests/test_architecture.py +136 -0
- snipbg-2.0.0/tests/test_cli.py +121 -0
- snipbg-2.0.0/tests/test_download.py +137 -0
- snipbg-2.0.0/tests/test_images.py +111 -0
- snipbg-2.0.0/tests/test_models.py +89 -0
- snipbg-2.0.0/tests/test_processing.py +129 -0
- snipbg-2.0.0/tests/test_real_model.py +82 -0
- snipbg-2.0.0/tests/test_server.py +293 -0
- snipbg-2.0.0/tests/test_session.py +173 -0
snipbg-2.0.0/.gitignore
ADDED
|
@@ -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
|
+
*~
|
snipbg-2.0.0/LICENSE
ADDED
|
@@ -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.
|
snipbg-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: snipbg
|
|
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/snipbg
|
|
6
|
+
Project-URL: Issues, https://github.com/asimmakhmudov/snipbg/issues
|
|
7
|
+
Project-URL: Documentation, https://github.com/asimmakhmudov/snipbg#readme
|
|
8
|
+
Project-URL: Source, https://github.com/asimmakhmudov/snipbg
|
|
9
|
+
Author: Asim Mahmudov
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: background-removal,background-remover,birefnet,cutout,isnet,onnx,remove-background,segmentation,transparent,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
|
+
# snipbg
|
|
44
|
+
|
|
45
|
+
[](https://pypi.org/project/snipbg/)
|
|
46
|
+
[](https://pypi.org/project/snipbg/)
|
|
47
|
+
[](https://github.com/asimmakhmudov/snipbg/actions/workflows/ci.yml)
|
|
48
|
+
[](https://github.com/asimmakhmudov/snipbg/blob/master/LICENSE)
|
|
49
|
+
|
|
50
|
+
**Remove image backgrounds locally** with pretrained ONNX segmentation models
|
|
51
|
+
(U²-Net, IS-Net, BiRefNet). Use it as a Python library, a command line tool, or
|
|
52
|
+
a self-hosted HTTP API. One name everywhere: `pip install snipbg`,
|
|
53
|
+
`import snipbg`, run `snipbg`.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install snipbg
|
|
57
|
+
snipbg remove photo.jpg # -> photo_nobg.png
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from snipbg import remove
|
|
62
|
+
|
|
63
|
+
with open("photo.jpg", "rb") as fh:
|
|
64
|
+
png = remove(fh.read())
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- **Small dependency set**: numpy, Pillow and onnxruntime. No `rembg`, no PyTorch.
|
|
68
|
+
- **Runs offline** after the first download. Weights are SHA-256 checked and cached.
|
|
69
|
+
- **Same type in, same type out**: bytes, file paths, PIL images and numpy arrays.
|
|
70
|
+
- **API you can deploy**: FastAPI server with an SSRF-safe URL route, a bounded
|
|
71
|
+
worker pool and a Docker image with the weights baked in.
|
|
72
|
+
|
|
73
|
+
## Contents
|
|
74
|
+
|
|
75
|
+
- [Install](#install)
|
|
76
|
+
- [Command line](#command-line)
|
|
77
|
+
- [Python library](#python-library)
|
|
78
|
+
- [HTTP API](#http-api)
|
|
79
|
+
- [Models](#models)
|
|
80
|
+
- [Docker](#docker)
|
|
81
|
+
- [Configuration](#configuration)
|
|
82
|
+
- [How it works](#how-it-works)
|
|
83
|
+
- [Development](#development)
|
|
84
|
+
- [License](#license)
|
|
85
|
+
|
|
86
|
+
## Install
|
|
87
|
+
|
|
88
|
+
Python 3.10 or newer.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install snipbg # library + CLI
|
|
92
|
+
pip install "snipbg[api]" # also the HTTP API
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
With [uv](https://docs.astral.sh/uv/):
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
uv add snipbg # add to a uv project
|
|
99
|
+
uv add "snipbg[api]"
|
|
100
|
+
uvx snipbg remove photo.jpg # run the CLI without installing it
|
|
101
|
+
uvx --from "snipbg[api]" snipbg serve # run the API without installing it
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Model weights are downloaded the first time a model is used and cached in
|
|
105
|
+
`~/.cache/snipbg`. Set `SNIPBG_HOME` to cache them somewhere else.
|
|
106
|
+
|
|
107
|
+
## Command line
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
snipbg remove photo.jpg # -> photo_nobg.png
|
|
111
|
+
snipbg remove photo.jpg -o out.png --refine --trim
|
|
112
|
+
snipbg remove *.jpg -o cutouts/ -m isnet-general-use
|
|
113
|
+
snipbg remove photo.jpg --bg white -o flat.jpg # flat background instead of transparency
|
|
114
|
+
snipbg remove photo.jpg --only-mask -o mask.png
|
|
115
|
+
cat photo.jpg | snipbg remove - > out.png # stdin -> stdout
|
|
116
|
+
|
|
117
|
+
snipbg models # list models, show what is cached
|
|
118
|
+
snipbg download u2net isnet-general-use # pre-fetch weights
|
|
119
|
+
snipbg download --all
|
|
120
|
+
snipbg download --check u2net # re-hash a cached file
|
|
121
|
+
snipbg serve --port 3001 # run the HTTP API
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`snipbg remove --help` lists every option. They are the same options as the
|
|
125
|
+
library: `--refine`, `--erode`, `--blur`, `--foreground-threshold`,
|
|
126
|
+
`--background-threshold`, `--bg`, `--trim`, `--format` and `--quality`.
|
|
127
|
+
|
|
128
|
+
## Python library
|
|
129
|
+
|
|
130
|
+
`remove()` returns the type it is given:
|
|
131
|
+
|
|
132
|
+
| Input | Output |
|
|
133
|
+
| --- | --- |
|
|
134
|
+
| `bytes` | `bytes` (PNG unless `output_format` says otherwise) |
|
|
135
|
+
| file path | `PIL.Image` (RGBA) |
|
|
136
|
+
| `PIL.Image` | `PIL.Image` (RGBA) |
|
|
137
|
+
| numpy array | numpy array (RGBA) |
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from snipbg import remove, Remover
|
|
141
|
+
|
|
142
|
+
png_bytes = remove(jpeg_bytes)
|
|
143
|
+
image = remove("photo.jpg")
|
|
144
|
+
array = remove(numpy_rgb)
|
|
145
|
+
|
|
146
|
+
# Batch work: build the model session once and reuse it.
|
|
147
|
+
remover = Remover("isnet-general-use")
|
|
148
|
+
for path in paths:
|
|
149
|
+
remover.remove(path, refine=True).save(path.with_suffix(".png"))
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Options for `remove()` and `Remover.remove()`:
|
|
153
|
+
|
|
154
|
+
| Option | Default | Meaning |
|
|
155
|
+
| --- | --- | --- |
|
|
156
|
+
| `only_mask` | `False` | Return the grayscale mask instead of the cutout |
|
|
157
|
+
| `refine` | `False` | Harden the confident regions and feather the edge |
|
|
158
|
+
| `foreground_threshold` | `240` | With `refine`: at or above this is fully opaque |
|
|
159
|
+
| `background_threshold` | `15` | With `refine`: at or below this is fully transparent |
|
|
160
|
+
| `erode_size` | `0` | With `refine`: pull the edge in by N pixels |
|
|
161
|
+
| `blur_radius` | `1.0` | With `refine`: feather radius |
|
|
162
|
+
| `background` | `None` | Composite onto a colour (`"white"`, `"#ff00ff"`, a tuple) |
|
|
163
|
+
| `trim_border` | `False` | Crop fully transparent borders away |
|
|
164
|
+
| `output_format` | `"PNG"` | Encoding used when the input was bytes |
|
|
165
|
+
| `quality` | `None` | JPEG/WEBP quality when the input was bytes |
|
|
166
|
+
|
|
167
|
+
`Remover` is lazy and thread-safe. Creating one does not touch the network or
|
|
168
|
+
the disk. The ONNX session is built on first use and then shared across threads.
|
|
169
|
+
|
|
170
|
+
## HTTP API
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
pip install "snipbg[api]"
|
|
174
|
+
snipbg serve --port 3001 # interactive docs at http://localhost:3001/docs
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
| Route | What it does |
|
|
178
|
+
| --- | --- |
|
|
179
|
+
| `POST /remove-background` | Multipart upload, field name `image` |
|
|
180
|
+
| `POST /remove-background-url` | JSON body with `imageUrl` (or `image_url`) |
|
|
181
|
+
| `GET /models` | Models this server will serve |
|
|
182
|
+
| `GET /health` | Health check |
|
|
183
|
+
| `GET /` | API description |
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
curl -X POST -F "image=@photo.jpg" \
|
|
187
|
+
http://localhost:3001/remove-background --output out.png
|
|
188
|
+
|
|
189
|
+
curl -X POST -H "Content-Type: application/json" \
|
|
190
|
+
-d '{"imageUrl": "https://example.com/photo.jpg"}' \
|
|
191
|
+
http://localhost:3001/remove-background-url --output out.png
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Both routes take the library's options: `model`, `only_mask`, `refine`,
|
|
195
|
+
`background`, `trim`, `format`, `quality`. Send them as form fields on the
|
|
196
|
+
upload route and as JSON keys on the URL route. Errors are always
|
|
197
|
+
`{"error": "..."}` with a 4xx or 5xx status.
|
|
198
|
+
|
|
199
|
+
The URL route accepts only `http` and `https`, and the host must resolve to a
|
|
200
|
+
public address. Each redirect hop is checked again, so a public URL cannot
|
|
201
|
+
redirect the server to `169.254.169.254` or another private address.
|
|
202
|
+
|
|
203
|
+
## Models
|
|
204
|
+
|
|
205
|
+
| Name | Size | Input | License | Notes |
|
|
206
|
+
| --- | --- | --- | --- | --- |
|
|
207
|
+
| `u2net` | 168 MB | 320² | Apache-2.0 | General purpose. The default. |
|
|
208
|
+
| `u2netp` | 4 MB | 320² | Apache-2.0 | Small and fast, lower quality |
|
|
209
|
+
| `u2net_human_seg` | 168 MB | 320² | Apache-2.0 | Fine-tuned for people |
|
|
210
|
+
| `silueta` | 42 MB | 320² | Apache-2.0 | u2net pruned; near-u2net quality |
|
|
211
|
+
| `isnet-general-use` | 170 MB | 1024² | Apache-2.0 | Sharper edges, slower |
|
|
212
|
+
| `isnet-anime` | 168 MB | 1024² | Apache-2.0 | Anime and illustration |
|
|
213
|
+
| `birefnet-general-lite` | 214 MB | 1024² | MIT | Best quality that fits in ~8 GB RAM |
|
|
214
|
+
| `birefnet-general` | 928 MB | 1024² | MIT | Highest quality; needs a GPU or 16 GB+ RAM |
|
|
215
|
+
|
|
216
|
+
**Picking one:** start with `u2net`. Use `u2netp` when speed or size matters
|
|
217
|
+
most, `isnet-general-use` for cleaner edges, and `birefnet-general-lite` when
|
|
218
|
+
quality matters more than speed.
|
|
219
|
+
|
|
220
|
+
Every download is checked against the model's SHA-256. A file that does not
|
|
221
|
+
match is thrown away instead of being cached. The weights are hosted on
|
|
222
|
+
[rembg](https://github.com/danielgatis/rembg)'s releases page. snipbg does its
|
|
223
|
+
own preprocessing and postprocessing and does not depend on rembg.
|
|
224
|
+
|
|
225
|
+
## Docker
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
docker build -t snipbg . # bakes in u2net
|
|
229
|
+
docker build --build-arg MODELS=u2netp,silueta -t snipbg .
|
|
230
|
+
docker run -p 3001:3001 snipbg
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The weights are downloaded in a separate build stage and copied into a slim
|
|
234
|
+
runtime image that runs as a non-root user, so the first request does not
|
|
235
|
+
trigger a download. If you bake in models other than the default, set
|
|
236
|
+
`SNIPBG_MODELS` at run time to the same list.
|
|
237
|
+
|
|
238
|
+
## Configuration
|
|
239
|
+
|
|
240
|
+
All settings are environment variables.
|
|
241
|
+
|
|
242
|
+
**Library and CLI**
|
|
243
|
+
|
|
244
|
+
| Variable | Default | Meaning |
|
|
245
|
+
| --- | --- | --- |
|
|
246
|
+
| `SNIPBG_HOME` | `~/.cache/snipbg` | Where weights are cached |
|
|
247
|
+
| `SNIPBG_MODEL_BASE_URL` | rembg releases | Mirror to download weights from |
|
|
248
|
+
| `SNIPBG_PROVIDERS` | onnxruntime default | Comma-separated execution providers |
|
|
249
|
+
| `SNIPBG_NUM_THREADS` | onnxruntime default | Intra-op thread count |
|
|
250
|
+
|
|
251
|
+
**HTTP server**
|
|
252
|
+
|
|
253
|
+
| Variable | Default | Meaning |
|
|
254
|
+
| --- | --- | --- |
|
|
255
|
+
| `SNIPBG_MODELS` | the six models ≤ 180 MB | Comma-separated allow-list |
|
|
256
|
+
| `SNIPBG_DEFAULT_MODEL` | `u2net` | Model used when a request names none |
|
|
257
|
+
| `SNIPBG_MAX_UPLOAD_BYTES` | `10485760` | Upload and fetch size limit (10 MB) |
|
|
258
|
+
| `SNIPBG_MAX_CONCURRENCY` | `2` | Inferences allowed to run at once |
|
|
259
|
+
| `SNIPBG_FETCH_TIMEOUT` | `15` | Seconds allowed for fetching a remote image |
|
|
260
|
+
| `SNIPBG_MAX_REDIRECTS` | `3` | Redirect hops, each one checked again |
|
|
261
|
+
| `SNIPBG_ALLOW_PRIVATE_URLS` | `false` | Let URL fetches reach private addresses |
|
|
262
|
+
| `SNIPBG_CORS_ORIGINS` | `*` | Comma-separated CORS origins |
|
|
263
|
+
| `SNIPBG_PRELOAD` | `false` | Load the default model at startup |
|
|
264
|
+
|
|
265
|
+
The two BiRefNet models are left out of the default `SNIPBG_MODELS` list
|
|
266
|
+
because they are large and use a lot of memory. Add them only on a machine
|
|
267
|
+
with enough RAM.
|
|
268
|
+
|
|
269
|
+
## How it works
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
input (bytes / path / PIL / numpy)
|
|
273
|
+
-> images.load_image decode to an RGB PIL image
|
|
274
|
+
-> processing.preprocess resize to the model's input, normalise, NCHW tensor
|
|
275
|
+
-> onnxruntime run the segmentation model (session.Remover)
|
|
276
|
+
-> processing.postprocess scale the mask back to the image size
|
|
277
|
+
-> processing.refine_mask optional: harden + feather the edge
|
|
278
|
+
-> processing.cutout / trim / apply_background
|
|
279
|
+
-> images.encode_image same type out as came in
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
src/snipbg/
|
|
284
|
+
├── __init__.py Public API, resolved lazily (remove, Remover, MODELS, ...)
|
|
285
|
+
├── models.py Model registry: URL, SHA-256, input size, normalisation
|
|
286
|
+
├── download.py Download, hash-check and cache weights (stdlib only)
|
|
287
|
+
├── session.py Remover: owns the ONNX session, runs the pipeline
|
|
288
|
+
├── processing.py Pure image math: preprocess, postprocess, refine, cutout, trim
|
|
289
|
+
├── images.py Decode any input, encode the output
|
|
290
|
+
├── cli.py `snipbg` command: remove, models, download, serve
|
|
291
|
+
└── server/ Optional HTTP API (the [api] extra)
|
|
292
|
+
├── app.py FastAPI app, routes, error handling, bounded worker pool
|
|
293
|
+
├── config.py Settings read from SNIPBG_* environment variables
|
|
294
|
+
└── fetch.py SSRF-safe image fetching for the URL route
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
The core library never imports `server`, and `processing.py` does no I/O, so
|
|
298
|
+
each layer can be tested on its own.
|
|
299
|
+
|
|
300
|
+
## Development
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
git clone https://github.com/asimmakhmudov/snipbg.git
|
|
304
|
+
cd snipbg
|
|
305
|
+
uv venv && uv pip install -e ".[dev]" # or: python -m venv .venv && pip install -e ".[dev]"
|
|
306
|
+
|
|
307
|
+
pytest -q # fast: a fake ONNX session, no network
|
|
308
|
+
pytest -q -m model # also runs real weights (downloads ~5 MB)
|
|
309
|
+
ruff check src tests && ruff format --check src tests
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
CI lints, runs the tests on Python 3.10–3.13, builds the package and
|
|
313
|
+
smoke-tests the Docker image. Publishing a GitHub release uploads the package
|
|
314
|
+
to PyPI.
|
|
315
|
+
|
|
316
|
+
Contributions are welcome. Please open an issue before starting a larger
|
|
317
|
+
change, and make sure `pytest -q` and `ruff` pass.
|
|
318
|
+
|
|
319
|
+
## License
|
|
320
|
+
|
|
321
|
+
MIT. The model weights have their own licenses, listed in the [Models](#models)
|
|
322
|
+
table.
|
snipbg-2.0.0/README.md
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# snipbg
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/snipbg/)
|
|
4
|
+
[](https://pypi.org/project/snipbg/)
|
|
5
|
+
[](https://github.com/asimmakhmudov/snipbg/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/asimmakhmudov/snipbg/blob/master/LICENSE)
|
|
7
|
+
|
|
8
|
+
**Remove image backgrounds locally** with pretrained ONNX segmentation models
|
|
9
|
+
(U²-Net, IS-Net, BiRefNet). Use it as a Python library, a command line tool, or
|
|
10
|
+
a self-hosted HTTP API. One name everywhere: `pip install snipbg`,
|
|
11
|
+
`import snipbg`, run `snipbg`.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install snipbg
|
|
15
|
+
snipbg remove photo.jpg # -> photo_nobg.png
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from snipbg import remove
|
|
20
|
+
|
|
21
|
+
with open("photo.jpg", "rb") as fh:
|
|
22
|
+
png = remove(fh.read())
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- **Small dependency set**: numpy, Pillow and onnxruntime. No `rembg`, no PyTorch.
|
|
26
|
+
- **Runs offline** after the first download. Weights are SHA-256 checked and cached.
|
|
27
|
+
- **Same type in, same type out**: bytes, file paths, PIL images and numpy arrays.
|
|
28
|
+
- **API you can deploy**: FastAPI server with an SSRF-safe URL route, a bounded
|
|
29
|
+
worker pool and a Docker image with the weights baked in.
|
|
30
|
+
|
|
31
|
+
## Contents
|
|
32
|
+
|
|
33
|
+
- [Install](#install)
|
|
34
|
+
- [Command line](#command-line)
|
|
35
|
+
- [Python library](#python-library)
|
|
36
|
+
- [HTTP API](#http-api)
|
|
37
|
+
- [Models](#models)
|
|
38
|
+
- [Docker](#docker)
|
|
39
|
+
- [Configuration](#configuration)
|
|
40
|
+
- [How it works](#how-it-works)
|
|
41
|
+
- [Development](#development)
|
|
42
|
+
- [License](#license)
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
Python 3.10 or newer.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install snipbg # library + CLI
|
|
50
|
+
pip install "snipbg[api]" # also the HTTP API
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
With [uv](https://docs.astral.sh/uv/):
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
uv add snipbg # add to a uv project
|
|
57
|
+
uv add "snipbg[api]"
|
|
58
|
+
uvx snipbg remove photo.jpg # run the CLI without installing it
|
|
59
|
+
uvx --from "snipbg[api]" snipbg serve # run the API without installing it
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Model weights are downloaded the first time a model is used and cached in
|
|
63
|
+
`~/.cache/snipbg`. Set `SNIPBG_HOME` to cache them somewhere else.
|
|
64
|
+
|
|
65
|
+
## Command line
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
snipbg remove photo.jpg # -> photo_nobg.png
|
|
69
|
+
snipbg remove photo.jpg -o out.png --refine --trim
|
|
70
|
+
snipbg remove *.jpg -o cutouts/ -m isnet-general-use
|
|
71
|
+
snipbg remove photo.jpg --bg white -o flat.jpg # flat background instead of transparency
|
|
72
|
+
snipbg remove photo.jpg --only-mask -o mask.png
|
|
73
|
+
cat photo.jpg | snipbg remove - > out.png # stdin -> stdout
|
|
74
|
+
|
|
75
|
+
snipbg models # list models, show what is cached
|
|
76
|
+
snipbg download u2net isnet-general-use # pre-fetch weights
|
|
77
|
+
snipbg download --all
|
|
78
|
+
snipbg download --check u2net # re-hash a cached file
|
|
79
|
+
snipbg serve --port 3001 # run the HTTP API
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`snipbg remove --help` lists every option. They are the same options as the
|
|
83
|
+
library: `--refine`, `--erode`, `--blur`, `--foreground-threshold`,
|
|
84
|
+
`--background-threshold`, `--bg`, `--trim`, `--format` and `--quality`.
|
|
85
|
+
|
|
86
|
+
## Python library
|
|
87
|
+
|
|
88
|
+
`remove()` returns the type it is given:
|
|
89
|
+
|
|
90
|
+
| Input | Output |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| `bytes` | `bytes` (PNG unless `output_format` says otherwise) |
|
|
93
|
+
| file path | `PIL.Image` (RGBA) |
|
|
94
|
+
| `PIL.Image` | `PIL.Image` (RGBA) |
|
|
95
|
+
| numpy array | numpy array (RGBA) |
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from snipbg import remove, Remover
|
|
99
|
+
|
|
100
|
+
png_bytes = remove(jpeg_bytes)
|
|
101
|
+
image = remove("photo.jpg")
|
|
102
|
+
array = remove(numpy_rgb)
|
|
103
|
+
|
|
104
|
+
# Batch work: build the model session once and reuse it.
|
|
105
|
+
remover = Remover("isnet-general-use")
|
|
106
|
+
for path in paths:
|
|
107
|
+
remover.remove(path, refine=True).save(path.with_suffix(".png"))
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Options for `remove()` and `Remover.remove()`:
|
|
111
|
+
|
|
112
|
+
| Option | Default | Meaning |
|
|
113
|
+
| --- | --- | --- |
|
|
114
|
+
| `only_mask` | `False` | Return the grayscale mask instead of the cutout |
|
|
115
|
+
| `refine` | `False` | Harden the confident regions and feather the edge |
|
|
116
|
+
| `foreground_threshold` | `240` | With `refine`: at or above this is fully opaque |
|
|
117
|
+
| `background_threshold` | `15` | With `refine`: at or below this is fully transparent |
|
|
118
|
+
| `erode_size` | `0` | With `refine`: pull the edge in by N pixels |
|
|
119
|
+
| `blur_radius` | `1.0` | With `refine`: feather radius |
|
|
120
|
+
| `background` | `None` | Composite onto a colour (`"white"`, `"#ff00ff"`, a tuple) |
|
|
121
|
+
| `trim_border` | `False` | Crop fully transparent borders away |
|
|
122
|
+
| `output_format` | `"PNG"` | Encoding used when the input was bytes |
|
|
123
|
+
| `quality` | `None` | JPEG/WEBP quality when the input was bytes |
|
|
124
|
+
|
|
125
|
+
`Remover` is lazy and thread-safe. Creating one does not touch the network or
|
|
126
|
+
the disk. The ONNX session is built on first use and then shared across threads.
|
|
127
|
+
|
|
128
|
+
## HTTP API
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
pip install "snipbg[api]"
|
|
132
|
+
snipbg serve --port 3001 # interactive docs at http://localhost:3001/docs
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
| Route | What it does |
|
|
136
|
+
| --- | --- |
|
|
137
|
+
| `POST /remove-background` | Multipart upload, field name `image` |
|
|
138
|
+
| `POST /remove-background-url` | JSON body with `imageUrl` (or `image_url`) |
|
|
139
|
+
| `GET /models` | Models this server will serve |
|
|
140
|
+
| `GET /health` | Health check |
|
|
141
|
+
| `GET /` | API description |
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
curl -X POST -F "image=@photo.jpg" \
|
|
145
|
+
http://localhost:3001/remove-background --output out.png
|
|
146
|
+
|
|
147
|
+
curl -X POST -H "Content-Type: application/json" \
|
|
148
|
+
-d '{"imageUrl": "https://example.com/photo.jpg"}' \
|
|
149
|
+
http://localhost:3001/remove-background-url --output out.png
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Both routes take the library's options: `model`, `only_mask`, `refine`,
|
|
153
|
+
`background`, `trim`, `format`, `quality`. Send them as form fields on the
|
|
154
|
+
upload route and as JSON keys on the URL route. Errors are always
|
|
155
|
+
`{"error": "..."}` with a 4xx or 5xx status.
|
|
156
|
+
|
|
157
|
+
The URL route accepts only `http` and `https`, and the host must resolve to a
|
|
158
|
+
public address. Each redirect hop is checked again, so a public URL cannot
|
|
159
|
+
redirect the server to `169.254.169.254` or another private address.
|
|
160
|
+
|
|
161
|
+
## Models
|
|
162
|
+
|
|
163
|
+
| Name | Size | Input | License | Notes |
|
|
164
|
+
| --- | --- | --- | --- | --- |
|
|
165
|
+
| `u2net` | 168 MB | 320² | Apache-2.0 | General purpose. The default. |
|
|
166
|
+
| `u2netp` | 4 MB | 320² | Apache-2.0 | Small and fast, lower quality |
|
|
167
|
+
| `u2net_human_seg` | 168 MB | 320² | Apache-2.0 | Fine-tuned for people |
|
|
168
|
+
| `silueta` | 42 MB | 320² | Apache-2.0 | u2net pruned; near-u2net quality |
|
|
169
|
+
| `isnet-general-use` | 170 MB | 1024² | Apache-2.0 | Sharper edges, slower |
|
|
170
|
+
| `isnet-anime` | 168 MB | 1024² | Apache-2.0 | Anime and illustration |
|
|
171
|
+
| `birefnet-general-lite` | 214 MB | 1024² | MIT | Best quality that fits in ~8 GB RAM |
|
|
172
|
+
| `birefnet-general` | 928 MB | 1024² | MIT | Highest quality; needs a GPU or 16 GB+ RAM |
|
|
173
|
+
|
|
174
|
+
**Picking one:** start with `u2net`. Use `u2netp` when speed or size matters
|
|
175
|
+
most, `isnet-general-use` for cleaner edges, and `birefnet-general-lite` when
|
|
176
|
+
quality matters more than speed.
|
|
177
|
+
|
|
178
|
+
Every download is checked against the model's SHA-256. A file that does not
|
|
179
|
+
match is thrown away instead of being cached. The weights are hosted on
|
|
180
|
+
[rembg](https://github.com/danielgatis/rembg)'s releases page. snipbg does its
|
|
181
|
+
own preprocessing and postprocessing and does not depend on rembg.
|
|
182
|
+
|
|
183
|
+
## Docker
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
docker build -t snipbg . # bakes in u2net
|
|
187
|
+
docker build --build-arg MODELS=u2netp,silueta -t snipbg .
|
|
188
|
+
docker run -p 3001:3001 snipbg
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The weights are downloaded in a separate build stage and copied into a slim
|
|
192
|
+
runtime image that runs as a non-root user, so the first request does not
|
|
193
|
+
trigger a download. If you bake in models other than the default, set
|
|
194
|
+
`SNIPBG_MODELS` at run time to the same list.
|
|
195
|
+
|
|
196
|
+
## Configuration
|
|
197
|
+
|
|
198
|
+
All settings are environment variables.
|
|
199
|
+
|
|
200
|
+
**Library and CLI**
|
|
201
|
+
|
|
202
|
+
| Variable | Default | Meaning |
|
|
203
|
+
| --- | --- | --- |
|
|
204
|
+
| `SNIPBG_HOME` | `~/.cache/snipbg` | Where weights are cached |
|
|
205
|
+
| `SNIPBG_MODEL_BASE_URL` | rembg releases | Mirror to download weights from |
|
|
206
|
+
| `SNIPBG_PROVIDERS` | onnxruntime default | Comma-separated execution providers |
|
|
207
|
+
| `SNIPBG_NUM_THREADS` | onnxruntime default | Intra-op thread count |
|
|
208
|
+
|
|
209
|
+
**HTTP server**
|
|
210
|
+
|
|
211
|
+
| Variable | Default | Meaning |
|
|
212
|
+
| --- | --- | --- |
|
|
213
|
+
| `SNIPBG_MODELS` | the six models ≤ 180 MB | Comma-separated allow-list |
|
|
214
|
+
| `SNIPBG_DEFAULT_MODEL` | `u2net` | Model used when a request names none |
|
|
215
|
+
| `SNIPBG_MAX_UPLOAD_BYTES` | `10485760` | Upload and fetch size limit (10 MB) |
|
|
216
|
+
| `SNIPBG_MAX_CONCURRENCY` | `2` | Inferences allowed to run at once |
|
|
217
|
+
| `SNIPBG_FETCH_TIMEOUT` | `15` | Seconds allowed for fetching a remote image |
|
|
218
|
+
| `SNIPBG_MAX_REDIRECTS` | `3` | Redirect hops, each one checked again |
|
|
219
|
+
| `SNIPBG_ALLOW_PRIVATE_URLS` | `false` | Let URL fetches reach private addresses |
|
|
220
|
+
| `SNIPBG_CORS_ORIGINS` | `*` | Comma-separated CORS origins |
|
|
221
|
+
| `SNIPBG_PRELOAD` | `false` | Load the default model at startup |
|
|
222
|
+
|
|
223
|
+
The two BiRefNet models are left out of the default `SNIPBG_MODELS` list
|
|
224
|
+
because they are large and use a lot of memory. Add them only on a machine
|
|
225
|
+
with enough RAM.
|
|
226
|
+
|
|
227
|
+
## How it works
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
input (bytes / path / PIL / numpy)
|
|
231
|
+
-> images.load_image decode to an RGB PIL image
|
|
232
|
+
-> processing.preprocess resize to the model's input, normalise, NCHW tensor
|
|
233
|
+
-> onnxruntime run the segmentation model (session.Remover)
|
|
234
|
+
-> processing.postprocess scale the mask back to the image size
|
|
235
|
+
-> processing.refine_mask optional: harden + feather the edge
|
|
236
|
+
-> processing.cutout / trim / apply_background
|
|
237
|
+
-> images.encode_image same type out as came in
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
src/snipbg/
|
|
242
|
+
├── __init__.py Public API, resolved lazily (remove, Remover, MODELS, ...)
|
|
243
|
+
├── models.py Model registry: URL, SHA-256, input size, normalisation
|
|
244
|
+
├── download.py Download, hash-check and cache weights (stdlib only)
|
|
245
|
+
├── session.py Remover: owns the ONNX session, runs the pipeline
|
|
246
|
+
├── processing.py Pure image math: preprocess, postprocess, refine, cutout, trim
|
|
247
|
+
├── images.py Decode any input, encode the output
|
|
248
|
+
├── cli.py `snipbg` command: remove, models, download, serve
|
|
249
|
+
└── server/ Optional HTTP API (the [api] extra)
|
|
250
|
+
├── app.py FastAPI app, routes, error handling, bounded worker pool
|
|
251
|
+
├── config.py Settings read from SNIPBG_* environment variables
|
|
252
|
+
└── fetch.py SSRF-safe image fetching for the URL route
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The core library never imports `server`, and `processing.py` does no I/O, so
|
|
256
|
+
each layer can be tested on its own.
|
|
257
|
+
|
|
258
|
+
## Development
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
git clone https://github.com/asimmakhmudov/snipbg.git
|
|
262
|
+
cd snipbg
|
|
263
|
+
uv venv && uv pip install -e ".[dev]" # or: python -m venv .venv && pip install -e ".[dev]"
|
|
264
|
+
|
|
265
|
+
pytest -q # fast: a fake ONNX session, no network
|
|
266
|
+
pytest -q -m model # also runs real weights (downloads ~5 MB)
|
|
267
|
+
ruff check src tests && ruff format --check src tests
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
CI lints, runs the tests on Python 3.10–3.13, builds the package and
|
|
271
|
+
smoke-tests the Docker image. Publishing a GitHub release uploads the package
|
|
272
|
+
to PyPI.
|
|
273
|
+
|
|
274
|
+
Contributions are welcome. Please open an issue before starting a larger
|
|
275
|
+
change, and make sure `pytest -q` and `ruff` pass.
|
|
276
|
+
|
|
277
|
+
## License
|
|
278
|
+
|
|
279
|
+
MIT. The model weights have their own licenses, listed in the [Models](#models)
|
|
280
|
+
table.
|