Hai una cartella con qualche centinaio di foto prodotto, ritratti o immagini per inserzioni, e a ognuna va tolto lo sfondo. Farlo a mano in un editor è una giornata di clic. In Python è un ciclo, e la vera decisione è cosa gira dentro il ciclo: un modello open source sulla tua macchina o un'API HTTP.
Questa guida li costruisce entrambi, con script completi da lanciare oggi su una cartella, e copre gli aspetti che rendono affidabile un'elaborazione in blocco: scelta del modello, CPU o GPU, nuovi tentativi, limiti di frequenza, idempotenza e ripresa dopo un crash.
Opzione A o opzione B?
| Modello locale (rembg) | API HTTP | |
|---|---|---|
| Costo | Software gratuito; paghi in hardware e tempo | A immagine |
| Configurazione | Python da 3.11 a 3.13, download dei modelli, CUDA facoltativo | Una chiave API e requests |
| Velocità | Dipende da CPU o GPU e dal modello | Dipende dal servizio, richieste in parallelo |
| Qualità | Dipende dal modello scelto | Dipende dal servizio |
| Privacy | Le immagini non lasciano mai la tua macchina | Le immagini vengono caricate presso terzi |
| Manutenzione | Gestisci tu modelli, driver e licenze | Se ne occupa il fornitore |
| Ideale per | Lavoro offline, immagini sensibili, esperimenti | Pipeline di produzione, server senza GPU |
Molti team usano entrambe: un modello locale per esperimenti e strumenti interni, un'API per la produzione, dove contano di più una qualità prevedibile e non dover gestire server con GPU.
Opzione A: rembg, la strada open source
rembg è una libreria Python e una CLI con licenza MIT che racchiude diversi modelli di rimozione dello sfondo e li esegue con ONNX Runtime. Al momento in cui scriviamo la versione attuale è la 2.0.86, e richiede Python 3.11 o successivo e precedente al 3.14.
Installazione
Scegli il backend adatto al tuo hardware. La versione per CPU funziona ovunque:
python -m venv .venv && source .venv/bin/activate
pip install "rembg[cpu]" # library only
pip install "rembg[cpu,cli]" # library plus the rembg commandPer le GPU NVIDIA installa invece rembg[gpu]. Il progetto segnala che servono onnxruntime-gpu più versioni compatibili di CUDA e cuDNN, e suggerisce di tornare alla versione CPU se non riesci a installarle. Le GPU AMD su Linux usano rembg[rocm] con onnxruntime-rocm.
rembg sceglie automaticamente il provider di esecuzione CUDA (o ROCm) quando ONNX Runtime lo segnala come disponibile, e avvisa se ripiega sulla CPU. Per vedere cosa rileva ONNX Runtime:
import onnxruntime
print(onnxruntime.get_available_providers()) # look for CUDAExecutionProviderScegliere un modello
I modelli si scaricano al primo utilizzo in ~/.rembg/models/ (imposta REMBG_HOME per cambiare percorso). Alcuni utili dall'elenco del progetto:
| Modello | Note |
|---|---|
bria-rmbg | Il predefinito. RMBG-2.0 di BRIA, circa 1,02 GB, lavora a 1024 x 1024. La sua licenza richiede un accordo a pagamento per l'uso commerciale. |
birefnet-general | Modello BiRefNet generico; buoni bordi morbidi |
birefnet-general-lite | BiRefNet più leggero per uso generico |
birefnet-portrait | Ottimizzato per le persone |
isnet-general-use | Modello IS-Net generico |
u2net | Il classico modello generico; più veloce, bordi più squadrati |
u2netp | u2net alleggerito; test rapidi su macchine lente |
silueta | Come u2net, ridotto a 43 MB |
Due punti contano più di quanto sembri. Primo, il modello predefinito non è gratuito per l'uso commerciale: i pesi dei modelli hanno licenze proprie, separate dalla licenza MIT di rembg, quindi controlla la fonte del modello che distribuisci. Secondo, i modelli più recenti (bria-rmbg, birefnet-*, isnet-general-use) producono bordi morbidi e ben definiti, mentre la vecchia famiglia u2net tende a dare bordi più rigidi e squadrati.
Opzioni per la qualità dei bordi
rembg offre tre modi per trattare i pixel morbidi del bordo come capelli e pelo:
- Predefinito: va bene per soggetti dai bordi netti come prodotti, auto e loghi.
decontaminate=True(-dcda CLI): elimina l'alone colorato che il vecchio sfondo lascia nei pixel del bordo. Cambia solo il colore, mai la forma della maschera, costa pochissimo e si può lasciare attivo per un intero lotto.alpha_matting=True(-a): ristima il bordo stesso quando la forma della maschera è sbagliata, ad esempio con ciocche tagliate. È molto più lento e può non convergere, nel qual caso rembg ripiega su un ritaglio decontaminato.
Per i lotti senza supervisione il progetto consiglia -dc invece di -a, perché è più veloce e non può fallire. La guida al canale alfa spiega perché si formano gli aloni.
Il modo rapido: la CLI
rembg p -m birefnet-general -dc photos/ cutouts/rembg p elabora un'intera cartella; aggiungi -w per monitorarla ed elaborare i nuovi file appena compaiono.
Lo script Python
Questa versione carica il modello una sola volta, salta i file già elaborati così puoi fermarlo e riavviarlo, e va avanti quando un file fallisce:
import sys
from pathlib import Path
from rembg import new_session, remove
EXTS = {".jpg", ".jpeg", ".png", ".webp"}
def main(in_dir: str, out_dir: str, model: str = "birefnet-general") -> None:
src_dir, dst_dir = Path(in_dir), Path(out_dir)
dst_dir.mkdir(parents=True, exist_ok=True)
session = new_session(model) # load the model once, reuse it for every image
files = sorted(p for p in src_dir.iterdir() if p.suffix.lower() in EXTS)
for i, src in enumerate(files, 1):
dst = dst_dir / f"{src.stem}.png"
if dst.exists():
continue # resume where a previous run stopped
try:
out = remove(src.read_bytes(), session=session, decontaminate=True)
dst.write_bytes(out)
print(f"[{i}/{len(files)}] ok {src.name}")
except Exception as e: # one bad file shouldn't stop the batch
print(f"[{i}/{len(files)}] failed {src.name}: {e}")
if __name__ == "__main__":
main(*sys.argv[1:])Avvialo con python batch_rembg.py photos cutouts birefnet-general. Creare la sessione una sola volta è importante: caricare un modello richiede molto più tempo che eseguirlo, e il README di rembg consiglia di riutilizzare la sessione per le prestazioni in blocco. L'output è sempre PNG perché il risultato ha un canale alfa.
Dove rembg non basta
- Il primo avvio è lento mentre il modello si scarica, e i modelli grandi richiedono molta RAM o VRAM.
- Su CPU la velocità è modesta con i modelli di alta qualità. Prova con 20 immagini rappresentative prima di impegnarti su mille.
- Licenze e driver sono affar tuo. Fissa le versioni di rembg e dei modelli, così un aggiornamento non cambia i risultati senza che te ne accorga.
Opzione B: un'API HTTP
Con un'API il lavoro pesante avviene sulle GPU di qualcun altro, e il tuo script si occupa soprattutto di gestire bene la rete. L'esempio usa l'API di removebackground.dev perché il suo contratto è pubblicato in OpenAPI 3.1 su /openapi.json; gli stessi schemi valgono per qualsiasi servizio simile.
Il contratto
POST https://removebackground.dev/v1/removecon la chiave nell'intestazionex-api-key.- Invia l'immagine come campo multipart
image_file(JPG, PNG o WebP, fino a 25 MB), oppure come JSON conimage_url. - I campi facoltativi includono
format(png,jpgowebp),bg_color,size(preview,medium,hd,full) ecrop. - 200 significa che il corpo è l'immagine finita. 202 significa che l'elaborazione è ancora in corso dopo circa 55 secondi: il corpo è un JSON con un
iddi job, e interroghiGET /v1/jobs/{id}/result, che risponde 409not_readyconRetry-Afterfinché l'immagine non è pronta. Non salvare mai un corpo 202 come immagine. - Fino a 60 richieste di elaborazione al minuto per account; oltre ricevi 429
rate_limitedconRetry-After. - Un'intestazione
Idempotency-Keyrende sicuri i nuovi tentativi: la stessa chiave con la stessa richiesta restituisce il job originale invece di addebitare di nuovo. La stessa chiave con una richiesta diversa risponde 409idempotency_key_reused. - I risultati vengono eliminati dopo 48 ore, quindi salvali appena li ricevi.
Regole per i nuovi tentativi che evitano di pagare due volte
| Risposta | Cosa fare |
|---|---|
| 200 | Salva l'immagine |
| 202 | Interroga il risultato del job; non ricaricare l'immagine |
| 400, 401, 402, 413, 422 | Correggi richiesta, chiave, crediti o immagine; non ripetere in ciclo |
| 429 | Attendi Retry-After secondi, reinvia con la stessa chiave |
| 500, 503, timeout, connessione interrotta | Attendi sempre più a lungo e reinvia con la stessa chiave |
| 502 | Quell'immagine non è riuscita e non è stata addebitata; registralo |
Il trucco è rendere la chiave di idempotenza deterministica: un hash dei byte dell'immagine più le opzioni. Se lo script va in crash e lo rilanci, ogni reinvio porta la stessa chiave, quindi niente viene elaborato o addebitato due volte.
Lo script Python
import hashlib
import mimetypes
import os
import sys
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
import requests
API = os.environ.get("REMOVEBG_API", "https://removebackground.dev")
KEY = os.environ["REMOVEBG_API_KEY"] # keep the key out of your code
OPTIONS = {"format": "png", "size": "full"} # or {"format": "jpg", "bg_color": "ffffff"}
WORKERS = 4 # the API allows 60 processing requests a minute
EXTS = {".jpg", ".jpeg", ".png", ".webp"}
RETRYABLE = {429, 500, 503}
def idempotency_key(data: bytes) -> str:
# Same image + same options = same key, so a rerun never pays twice.
h = hashlib.sha256(data)
h.update(repr(sorted(OPTIONS.items())).encode())
return h.hexdigest()
def wait(res, default=2.0):
try:
return float(res.headers.get("Retry-After", default))
except ValueError:
return default
def save(res, dst: Path) -> Path:
if res.status_code != 200 or not res.headers.get("Content-Type", "").startswith("image/"):
raise RuntimeError(f"HTTP {res.status_code}: {res.text[:300]}")
dst.write_bytes(res.content)
return dst
def process(src: Path, dst: Path) -> Path:
data = src.read_bytes()
mime = mimetypes.guess_type(src.name)[0] or "application/octet-stream"
headers = {"x-api-key": KEY, "Idempotency-Key": idempotency_key(data)}
for attempt in range(6):
try:
res = requests.post(
f"{API}/v1/remove",
files={"image_file": (src.name, data, mime)},
data=OPTIONS,
headers=headers,
timeout=(10, 120),
)
except (requests.ConnectionError, requests.Timeout):
time.sleep(2 ** attempt) # same key: safe to resend
continue
if res.status_code in RETRYABLE:
time.sleep(wait(res, 2 ** attempt))
continue
break
else:
raise RuntimeError("gave up after retries")
if res.status_code != 202:
return save(res, dst) # 200 is the image; 4xx and 502 raise
# 202: still processing. Poll the same job; never upload the image again.
job_id = res.json()["id"]
deadline = time.monotonic() + 600
while time.monotonic() < deadline:
time.sleep(wait(res))
try:
res = requests.get(f"{API}/v1/jobs/{job_id}/result", headers=headers, timeout=30)
except (requests.ConnectionError, requests.Timeout):
continue # reading a job is free and safe to repeat
if res.status_code == 409 and res.json()["error"]["code"] == "not_ready":
continue
if res.status_code >= 500:
continue
return save(res, dst)
raise TimeoutError(f"job {job_id} still running; fetch it later")
def main(in_dir: str, out_dir: str) -> None:
src_dir, dst_dir = Path(in_dir), Path(out_dir)
dst_dir.mkdir(parents=True, exist_ok=True)
ext = ".jpg" if OPTIONS.get("format") == "jpg" else "." + OPTIONS.get("format", "png")
todo = [
(p, dst_dir / (p.stem + ext))
for p in sorted(src_dir.iterdir())
if p.suffix.lower() in EXTS and not (dst_dir / (p.stem + ext)).exists()
]
print(f"{len(todo)} images to process")
failed = []
with ThreadPoolExecutor(max_workers=WORKERS) as pool:
jobs = {pool.submit(process, s, d): s for s, d in todo}
for fut in as_completed(jobs):
src = jobs[fut]
try:
print("ok ", src.name, "->", fut.result().name)
except Exception as e:
failed.append(src.name)
print("failed", src.name, e)
print(f"done: {len(todo) - len(failed)} ok, {len(failed)} failed")
if __name__ == "__main__":
main(sys.argv[1], sys.argv[2])Avvialo con REMOVEBG_API_KEY=... python batch_api.py photos cutouts. Quattro worker sono un inizio prudente; se risposte rapide ti fanno superare il limite al minuto, la gestione del 429 attende e reinvia con la stessa chiave. Per inviare URL invece di file, invia un JSON:
res = requests.post(
f"{API}/v1/remove",
json={"image_url": "https://example.com/shoe.jpg", "format": "png"},
headers={"x-api-key": KEY, "Idempotency-Key": "shoe-front-v1"},
timeout=(10, 120),
)Per lunghi elenchi di URL, la stessa API ha POST /v1/batch, che mette in coda fino a 100 URL di immagini come un unico job da interrogare.
Rendere robusta qualsiasi elaborazione in blocco
- Salta gli output già prodotti così un nuovo avvio riprende invece di ricominciare. Entrambi gli script lo fanno.
- Attenzione ai nomi in conflitto.
shoe.jpgeshoe.pngnella stessa cartella diventano entrambishoe.png; rinomina i file di input o includi l'estensione originale nel nome di output. - Registra gli errori in un file e rilancia solo quelli.
- Controlla un campione allo zoom al 100% su sfondi scuri e chiari prima di elaborare tutto.
- Non toccare gli originali. Scrivi in una cartella di output separata.
- Scegli il formato di output in base alla destinazione. PNG o WebP per la trasparenza, JPG su bianco per i marketplace. Vedi PNG, JPEG o WebP.
Domande
rembg è gratuito per l'uso commerciale?
rembg in sé ha licenza MIT, ma ogni modello ha una licenza propria. Il modello predefinito, RMBG-2.0 di BRIA (bria-rmbg), richiede un accordo a pagamento per l'uso commerciale, quindi controlla la licenza del modello che usi o scegline un altro.
rembg usa automaticamente la mia GPU?
Sì, se installi rembg[gpu] e ONNX Runtime riesce a caricare il provider CUDA (che richiede versioni compatibili di CUDA e cuDNN). rembg usa CUDA o ROCm quando disponibili e avvisa quando ripiega sulla CPU. Controlla con onnxruntime.get_available_providers().
Quale modello di rembg dà la qualità migliore?
I modelli più recenti, bria-rmbg (il predefinito), la famiglia birefnet e isnet-general-use, producono bordi più morbidi e meglio definiti della vecchia famiglia u2net. birefnet-portrait è un buon punto di partenza per le persone. Prova su un campione delle tue immagini, perché i risultati variano in base al soggetto.
Come evito il doppio addebito quando ritento le chiamate all'API?
Invia un'intestazione Idempotency-Key e riutilizzala a ogni nuovo tentativo per la stessa immagine. Ricavandola da un hash dei byte dell'immagine e delle opzioni, anche un nuovo avvio completo dello script riutilizza le stesse chiavi.
Cosa deve fare il mio script con una risposta 202?
Considerarla come elaborazione ancora in corso, non come un'immagine. Leggi l'id del job dal corpo JSON e interroga GET /v1/jobs/{id}/result, attendendo Retry-After ogni volta che risponde 409 not_ready. Non ricaricare l'immagine.
Con che frequenza posso inviare richieste all'API di removebackground.dev?
Fino a 60 richieste di elaborazione al minuto per account. Oltre, l'API risponde 429 rate_limited con un'intestazione Retry-After. Per molti URL, POST /v1/batch ne accetta fino a 100 per richiesta.
Provalo con la tua immagine
I nuovi account ricevono 50 crediti gratuiti. Un credito per immagine, nessun abbonamento, e i crediti non scadono mai.
Rimuovi uno sfondoFonti
- rembg on GitHub (README: installation, models, edge modes)
- rembg on PyPI (version and Python requirement)
- BRIA RMBG-2.0 model card and license
- ONNX Runtime: Get started and installation matrix
- removebackground.dev API reference and guides
- removebackground.dev OpenAPI 3.1 schema
- Requests: Quickstart (multipart file uploads, timeouts)