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.
@@ -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
+ [![PyPI](https://img.shields.io/pypi/v/snipbg.svg)](https://pypi.org/project/snipbg/)
46
+ [![Python](https://img.shields.io/pypi/pyversions/snipbg.svg)](https://pypi.org/project/snipbg/)
47
+ [![CI](https://github.com/asimmakhmudov/snipbg/actions/workflows/ci.yml/badge.svg)](https://github.com/asimmakhmudov/snipbg/actions/workflows/ci.yml)
48
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](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
+ [![PyPI](https://img.shields.io/pypi/v/snipbg.svg)](https://pypi.org/project/snipbg/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/snipbg.svg)](https://pypi.org/project/snipbg/)
5
+ [![CI](https://github.com/asimmakhmudov/snipbg/actions/workflows/ci.yml/badge.svg)](https://github.com/asimmakhmudov/snipbg/actions/workflows/ci.yml)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](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.