Je hebt een map met een paar honderd productfoto's, portretfoto's of advertentieafbeeldingen, en van elke afbeelding moet je de achtergrond verwijderen. Dat met de hand doen in een editor is een dag klikken. In Python is het een lus, en de echte keuze is wat er in die lus draait: een open-source model op je eigen machine, of een HTTP-API.
Deze gids bouwt allebei, met complete scripts die je vandaag nog op een map kunt draaien, en behandelt de onderdelen die een batchjob betrouwbaar maken: modelkeuze, CPU of GPU, retries, rate limits, idempotentie en hervatten na een crash.
Optie A of optie B?
| Lokaal model (rembg) | HTTP-API | |
|---|---|---|
| Kosten | Gratis software; je betaalt in hardware en tijd | Per afbeelding |
| Installatie | Python 3.11 tot 3.13, modellen downloaden, optioneel CUDA | Een API-sleutel en requests |
| Snelheid | Hangt af van je CPU of GPU en het model | Hangt af van de dienst, parallelle verzoeken |
| Kwaliteit | Hangt af van het model dat je kiest | Hangt af van de dienst |
| Privacy | Afbeeldingen verlaten je machine nooit | Afbeeldingen worden geüpload naar een derde partij |
| Onderhoud | Jij beheert modellen, drivers en licenties | De aanbieder doet het |
| Geschikt voor | Offline werk, gevoelige afbeeldingen, experimenten | Productiepipelines, servers zonder GPU |
Veel teams gebruiken allebei: een lokaal model voor experimenten en interne tools, een API voor productie, waar voorspelbare kwaliteit en geen GPU-servers belangrijker zijn.
Optie A: rembg, de open-source route
rembg is een Python-bibliotheek en CLI onder MIT-licentie die verschillende modellen voor achtergrondverwijdering bundelt en ze draait met ONNX Runtime. Op het moment van schrijven is de huidige release 2.0.86, en die vereist Python 3.11 of later en ouder dan 3.14.
Installeren
Kies de backend die bij je hardware past. De CPU-build werkt overal:
python -m venv .venv && source .venv/bin/activate
pip install "rembg[cpu]" # library only
pip install "rembg[cpu,cli]" # library plus the rembg commandVoor NVIDIA-GPU's installeer je in plaats daarvan rembg[gpu]. Het project meldt dat dit onnxruntime-gpu nodig heeft plus een passende CUDA en cuDNN, en stelt voor terug te vallen op de CPU-build als je die niet kunt installeren. AMD-GPU's op Linux gebruiken rembg[rocm] met onnxruntime-rocm.
rembg kiest automatisch de execution provider CUDA (of ROCm) als ONNX Runtime die als beschikbaar meldt, en waarschuwt als het terugvalt op de CPU. Zo controleer je wat ONNX Runtime ziet:
import onnxruntime
print(onnxruntime.get_available_providers()) # look for CUDAExecutionProviderKies een model
Modellen worden bij het eerste gebruik gedownload naar ~/.rembg/models/ (stel REMBG_HOME in om dat te wijzigen). Een paar handige uit de lijst van het project:
| Model | Opmerkingen |
|---|---|
bria-rmbg | De standaard. RMBG-2.0 van BRIA, ongeveer 1,02 GB, draait op 1024 x 1024. De licentie vereist een betaalde overeenkomst voor commercieel gebruik. |
birefnet-general | BiRefNet-model voor algemeen gebruik; goede zachte randen |
birefnet-general-lite | Lichtere BiRefNet voor algemeen gebruik |
birefnet-portrait | Afgestemd op mensen |
isnet-general-use | IS-Net-model voor algemeen gebruik |
u2net | Het klassieke algemene model; sneller, hoekigere randen |
u2netp | Lichte u2net; snelle tests op trage machines |
silueta | Hetzelfde als u2net, verkleind tot 43 MB |
Twee punten zijn belangrijker dan ze lijken. Ten eerste: het standaardmodel is niet gratis voor commercieel gebruik. Modelgewichten hebben hun eigen licenties, los van de MIT-licentie van rembg, dus controleer de bron van elk model dat je meelevert. Ten tweede geven de nieuwere modellen (bria-rmbg, birefnet-*, isnet-general-use) zachte, goed gevormde randen, terwijl de oudere u2net-familie meestal hardere, hoekigere randen geeft.
Opties voor randkwaliteit
rembg biedt drie manieren om zachte randpixels zoals haar en vacht te behandelen:
- Standaard: prima voor onderwerpen met harde randen, zoals producten, auto's en logo's.
decontaminate=True(-dcin de CLI): haalt de gekleurde rand weg die de oude achtergrond in randpixels achterlaat. Het verandert alleen de kleur, nooit de vorm van het masker, kost heel weinig en kan veilig aan blijven voor een hele batch.alpha_matting=True(-a): berekent de rand zelf opnieuw als de vorm van het masker niet klopt, bijvoorbeeld als lokken zijn afgeknipt. Het is veel trager en kan niet convergeren, waarna rembg terugvalt op een gedecontamineerde uitsnede.
Voor batches zonder toezicht raadt het project -dc aan boven -a, omdat het sneller is en niet kan mislukken. De gids over het alfakanaal legt uit waarom gekleurde randen ontstaan.
De snelle manier: de CLI
rembg p -m birefnet-general -dc photos/ cutouts/rembg p verwerkt een hele map; voeg -w toe om de map in de gaten te houden en nieuwe bestanden te verwerken zodra ze verschijnen.
Het Python-script
Deze versie laadt het model één keer, slaat bestanden over die al klaar zijn zodat je kunt stoppen en opnieuw starten, en gaat door als één bestand mislukt:
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:])Start het met python batch_rembg.py photos cutouts birefnet-general. De sessie één keer aanmaken is belangrijk: een model laden duurt veel langer dan het draaien, en de README van rembg raadt hergebruik van de sessie aan voor batchprestaties. De uitvoer is altijd PNG, omdat het resultaat een alfakanaal heeft.
Waar rembg tekortschiet
- De eerste run is traag terwijl het model wordt gedownload, en grote modellen hebben veel RAM of VRAM nodig.
- De doorvoer op de CPU is bescheiden met de modellen van hoge kwaliteit. Test met 20 representatieve afbeeldingen voordat je aan duizend begint.
- Licenties en drivers zijn jouw taak. Zet je versies van rembg en het model vast, zodat een latere upgrade de resultaten niet ongemerkt verandert.
Optie B: een HTTP-API
Met een API gebeurt het zware werk op de GPU's van iemand anders, en draait je script vooral om het netjes afhandelen van het netwerk. Het voorbeeld gebruikt de API van removebackground.dev, omdat het contract als OpenAPI 3.1 is gepubliceerd op /openapi.json; dezelfde patronen gelden voor elke vergelijkbare dienst.
Het contract
POST https://removebackground.dev/v1/removemet je sleutel in de headerx-api-key.- Stuur de afbeelding als multipartveld
image_file(JPG, PNG of WebP, tot 25 MB), of als JSON metimage_url. - Optionele velden zijn onder meer
format(png,jpgofwebp),bg_color,size(preview,medium,hd,full) encrop. - 200 betekent dat de body de afgewerkte afbeelding is. 202 betekent dat hij na ongeveer 55 seconden nog wordt verwerkt: de body is JSON met een job-
id, en je polltGET /v1/jobs/{id}/result, dat met 409not_readyenRetry-Afterantwoordt tot de afbeelding klaar is. Bewaar een 202-body nooit als afbeelding. - Maximaal 60 verwerkingsverzoeken per minuut per account; daarboven krijg je 429
rate_limitedmetRetry-After. - Een header
Idempotency-Keymaakt retries veilig: dezelfde sleutel met hetzelfde verzoek geeft de oorspronkelijke job terug in plaats van opnieuw kosten te rekenen. Dezelfde sleutel met een ander verzoek geeft 409idempotency_key_reused. - Resultaten worden na 48 uur verwijderd, dus bewaar ze zodra je ze krijgt.
Retryregels waardoor je niet twee keer betaalt
| Response | Wat je doet |
|---|---|
| 200 | Bewaar de afbeelding |
| 202 | Poll het resultaat van de job; upload niet opnieuw |
| 400, 401, 402, 413, 422 | Corrigeer het verzoek, de sleutel, de credits of de afbeelding; niet in een lus herhalen |
| 429 | Wacht Retry-After seconden, stuur opnieuw met dezelfde sleutel |
| 500, 503, time-out, verbroken verbinding | Wacht steeds langer en stuur opnieuw met dezelfde sleutel |
| 502 | Die afbeelding is mislukt en niet in rekening gebracht; log het |
De truc is om de idempotentiesleutel deterministisch te maken: een hash van de bytes van de afbeelding plus de opties. Crasht het script en start je het opnieuw, dan draagt elke herhaling dezelfde sleutel, zodat niets twee keer wordt verwerkt of in rekening gebracht.
Het Python-script
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])Start het met REMOVEBG_API_KEY=... python batch_api.py photos cutouts. Vier workers is een bescheiden begin; duwen snelle antwoorden je over de limiet per minuut, dan wacht de afhandeling van 429 en stuurt ze opnieuw met dezelfde sleutel. Wil je URL's sturen in plaats van bestanden, post dan 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),
)Voor lange lijsten met URL's heeft dezelfde API POST /v1/batch, dat tot 100 afbeeldings-URL's in de wachtrij zet als één job die je pollt.
Elke batchjob robuust maken
- Sla afgewerkte uitvoer over, zodat een nieuwe run hervat in plaats van opnieuw te beginnen. Beide scripts doen dit.
- Let op naamconflicten.
shoe.jpgenshoe.pngin dezelfde map worden allebeishoe.png; hernoem de invoer of neem de oorspronkelijke extensie op in de naam van de uitvoer. - Log mislukkingen in een bestand en draai alleen die opnieuw.
- Controleer steekproefsgewijs op 100% zoom op donkere en lichte achtergronden voordat je alles verwerkt.
- Laat originelen ongemoeid. Schrijf naar een aparte uitvoermap.
- Kies het uitvoerformaat voor de bestemming. PNG of WebP voor transparantie, JPG op wit voor marktplaatsen. Zie PNG vs JPEG vs WebP.
Vragen
Is rembg gratis voor commercieel gebruik?
rembg zelf valt onder de MIT-licentie, maar elk model heeft een eigen licentie. Het standaardmodel, RMBG-2.0 van BRIA (bria-rmbg), vereist een betaalde overeenkomst voor commercieel gebruik, dus controleer de licentie van het model dat je gebruikt of kies een ander.
Gebruikt rembg automatisch mijn GPU?
Ja, als je rembg[gpu] installeert en ONNX Runtime de CUDA-provider kan laden (waarvoor passende versies van CUDA en cuDNN nodig zijn). rembg gebruikt CUDA of ROCm als die beschikbaar zijn en waarschuwt als het terugvalt op de CPU. Controleer onnxruntime.get_available_providers().
Welk rembg-model geeft de beste kwaliteit?
De nieuwere modellen, bria-rmbg (de standaard), de birefnet-familie en isnet-general-use, geven zachtere en beter gevormde randen dan de oudere u2net-familie. birefnet-portrait is een goed begin voor mensen. Test een steekproef van je eigen afbeeldingen, want de resultaten verschillen per onderwerp.
Hoe voorkom ik dat ik twee keer betaal als ik API-calls opnieuw probeer?
Stuur een header Idempotency-Key mee en gebruik die opnieuw bij elke herhaling voor dezelfde afbeelding. Leid je hem af van een hash van de bytes van de afbeelding en de opties, dan hergebruikt zelfs een nieuwe run van het hele script dezelfde sleutels.
Wat moet mijn script doen met een 202-response?
Behandel het als nog in verwerking, niet als een afbeelding. Lees het job-id uit de JSON-body en poll GET /v1/jobs/{id}/result, en wacht Retry-After af zolang het antwoord 409 not_ready is. Upload de afbeelding niet opnieuw.
Hoe snel kan ik verzoeken sturen naar de API van removebackground.dev?
Tot 60 verwerkingsverzoeken per minuut per account. Daarboven antwoordt de API met 429 rate_limited en een header Retry-After. Voor veel URL's accepteert POST /v1/batch er tot 100 per verzoek.
Probeer het met je eigen afbeelding
Nieuwe accounts krijgen 50 gratis credits. Eén credit per afbeelding, geen abonnement, en credits verlopen nooit.
Achtergrond verwijderenBronnen
- 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)