Du hast einen Ordner mit ein paar Hundert Produktfotos, Porträts oder Angebotsbildern, und bei jedem muss der Hintergrund weg. Von Hand in einem Editor ist das ein Tag voller Klicks. In Python ist es eine Schleife, und die eigentliche Entscheidung ist, was in der Schleife läuft: ein Open-Source-Modell auf deinem eigenen Rechner oder eine HTTP-API.
Dieser Ratgeber baut beides, mit vollständigen Skripten, die du noch heute auf einen Ordner loslassen kannst. Dazu kommen die Teile, die einen Stapeljob zuverlässig machen: Modellwahl, CPU oder GPU, Wiederholungsversuche, Rate Limits, Idempotenz und Fortsetzen nach einem Absturz.
Option A oder Option B?
| Lokales Modell (rembg) | HTTP-API | |
|---|---|---|
| Kosten | Kostenlose Software; du zahlst mit Hardware und Zeit | Pro Bild |
| Einrichtung | Python 3.11 bis 3.13, Modell-Downloads, optional CUDA | Ein API-Schlüssel und requests |
| Geschwindigkeit | Hängt von CPU oder GPU und dem Modell ab | Hängt vom Dienst ab, parallele Anfragen |
| Qualität | Hängt vom gewählten Modell ab | Hängt vom Dienst ab |
| Datenschutz | Bilder verlassen deinen Rechner nie | Bilder werden zu einem Drittanbieter hochgeladen |
| Wartung | Du kümmerst dich um Modelle, Treiber und Lizenzen | Der Anbieter erledigt das |
| Gut für | Offline-Arbeit, sensible Bilder, Experimente | Produktions-Pipelines, Server ohne GPU |
Viele Teams nutzen beides: ein lokales Modell für Experimente und interne Tools, eine API für die Produktion, wo berechenbare Qualität und keine GPU-Server wichtiger sind.
Option A: rembg, der Open-Source-Weg
rembg ist eine MIT-lizenzierte Python-Bibliothek mit CLI, die mehrere Modelle zur Hintergrundentfernung bündelt und sie mit ONNX Runtime ausführt. Zum Zeitpunkt des Schreibens ist die aktuelle Version 2.0.86, und sie braucht Python 3.11 oder neuer und älter als 3.14.
Installation
Wähle das Backend passend zu deiner Hardware. Die CPU-Variante läuft überall:
python -m venv .venv && source .venv/bin/activate
pip install "rembg[cpu]" # library only
pip install "rembg[cpu,cli]" # library plus the rembg commandFür NVIDIA-GPUs installierst du stattdessen rembg[gpu]. Laut Projekt braucht das onnxruntime-gpu sowie passendes CUDA und cuDNN, und wenn du die nicht installieren kannst, solltest du auf die CPU-Variante ausweichen. AMD-GPUs unter Linux nutzen rembg[rocm] mit onnxruntime-rocm.
rembg wählt automatisch den CUDA- (oder ROCm-) Execution Provider, wenn ONNX Runtime ihn als verfügbar meldet, und warnt, wenn es auf die CPU zurückfällt. So prüfst du, was ONNX Runtime sieht:
import onnxruntime
print(onnxruntime.get_available_providers()) # look for CUDAExecutionProviderEin Modell wählen
Modelle werden beim ersten Einsatz nach ~/.rembg/models/ heruntergeladen (mit REMBG_HOME änderst du den Ort). Einige nützliche aus der Liste des Projekts:
| Modell | Hinweise |
|---|---|
bria-rmbg | Der Standard. BRIAs RMBG-2.0, etwa 1,02 GB, läuft mit 1024 x 1024. Seine Lizenz verlangt für kommerzielle Nutzung eine kostenpflichtige Vereinbarung. |
birefnet-general | BiRefNet-Modell für allgemeine Zwecke; gute weiche Kanten |
birefnet-general-lite | Leichteres BiRefNet für allgemeine Zwecke |
birefnet-portrait | Auf Personen abgestimmt |
isnet-general-use | IS-Net-Modell für allgemeine Zwecke |
u2net | Das klassische Allzweckmodell; schneller, gröbere Kanten |
u2netp | Leichtes u2net; schnelle Tests auf langsamen Rechnern |
silueta | Wie u2net, verkleinert auf 43 MB |
Zwei Punkte sind wichtiger, als sie aussehen. Erstens: Das Standardmodell ist für kommerzielle Nutzung nicht kostenlos. Modellgewichte haben eigene Lizenzen, getrennt von der MIT-Lizenz von rembg, prüfe also die Quelle jedes Modells, das du einsetzt. Zweitens liefern die neueren Modelle (bria-rmbg, birefnet-*, isnet-general-use) weiche, gut geformte Kanten, während die ältere u2net-Familie eher festere, gröbere Kanten erzeugt.
Optionen für die Kantenqualität
rembg bietet drei Arten, weiche Kantenpixel wie Haare und Fell zu behandeln:
- Standard: gut für Motive mit harten Kanten wie Produkte, Autos und Logos.
decontaminate=True(-dcin der CLI): entfernt den farbigen Saum, den der alte Hintergrund in den Kantenpixeln hinterlässt. Es ändert nur die Farbe, nie die Form der Maske, kostet sehr wenig und kann für einen ganzen Stapel eingeschaltet bleiben.alpha_matting=True(-a): schätzt die Kante selbst neu, wenn die Maskenform falsch ist, etwa bei abgeschnittenen Strähnen. Das ist viel langsamer und kann fehlschlagen; dann fällt rembg auf einen dekontaminierten Freisteller zurück.
Für unbeaufsichtigte Stapel empfiehlt das Projekt -dc statt -a, weil es schneller ist und nicht fehlschlagen kann. Der Ratgeber zum Alphakanal erklärt, warum Säume entstehen.
Der schnelle Weg: die CLI
rembg p -m birefnet-general -dc photos/ cutouts/rembg p verarbeitet einen ganzen Ordner; mit -w überwacht es den Ordner und verarbeitet neue Dateien, sobald sie erscheinen.
Das Python-Skript
Diese Version lädt das Modell einmal, überspringt bereits fertige Dateien, damit du es anhalten und neu starten kannst, und macht weiter, wenn eine Datei fehlschlägt:
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:])Starte es mit python batch_rembg.py photos cutouts birefnet-general. Die Session einmal zu erstellen ist wichtig: Ein Modell zu laden dauert viel länger, als es auszuführen, und die README von rembg empfiehlt die Wiederverwendung der Session für die Leistung bei Stapeln. Die Ausgaben sind immer PNG, weil das Ergebnis einen Alphakanal hat.
Wo rembg an Grenzen stößt
- Der erste Lauf ist langsam, während das Modell herunterlädt, und große Modelle brauchen viel RAM oder VRAM.
- Der Durchsatz auf der CPU ist bescheiden mit den hochwertigen Modellen. Teste mit 20 repräsentativen Bildern, bevor du dich auf tausend festlegst.
- Lizenzen und Treiber sind deine Sache. Fixiere die Versionen von rembg und den Modellen, damit ein späteres Upgrade die Ergebnisse nicht unbemerkt verändert.
Option B: eine HTTP-API
Mit einer API erledigen die GPUs von jemand anderem die schwere Arbeit, und dein Skript kümmert sich vor allem darum, das Netzwerk gut zu behandeln. Das Beispiel nutzt die API von removebackground.dev, weil ihr Vertrag als OpenAPI 3.1 unter /openapi.json veröffentlicht ist; dieselben Muster gelten für jeden ähnlichen Dienst.
Der Vertrag
POST https://removebackground.dev/v1/removemit deinem Schlüssel im Headerx-api-key.- Schick das Bild als Multipart-Feld
image_file(JPG, PNG oder WebP, bis 25 MB) oder als JSON mitimage_url. - Optionale Felder sind unter anderem
format(png,jpgoderwebp),bg_color,size(preview,medium,hd,full) undcrop. - 200 heißt, der Body ist das fertige Bild. 202 heißt, nach etwa 55 Sekunden wird noch verarbeitet: Der Body ist JSON mit einer Job-
id, und du fragstGET /v1/jobs/{id}/resultab, das mit 409not_readyundRetry-Afterantwortet, bis das Bild fertig ist. Speichere einen 202-Body nie als Bild. - Bis zu 60 Verarbeitungsanfragen pro Minute pro Konto; darüber bekommst du 429
rate_limitedmitRetry-After. - Ein Header
Idempotency-Keymacht Wiederholungen sicher: Derselbe Schlüssel mit derselben Anfrage liefert den ursprünglichen Job zurück, statt erneut abzurechnen. Derselbe Schlüssel mit einer anderen Anfrage antwortet mit 409idempotency_key_reused. - Ergebnisse werden nach 48 Stunden gelöscht, speichere sie also, sobald du sie bekommst.
Wiederholungsregeln, damit du nicht doppelt zahlst
| Antwort | Was zu tun ist |
|---|---|
| 200 | Bild speichern |
| 202 | Job-Ergebnis abfragen; nicht erneut hochladen |
| 400, 401, 402, 413, 422 | Anfrage, Schlüssel, Credits oder Bild korrigieren; keine Schleife |
| 429 | Retry-After Sekunden warten, mit demselben Schlüssel erneut senden |
| 500, 503, Timeout, abgebrochene Verbindung | Warten und mit demselben Schlüssel erneut senden |
| 502 | Dieses Bild ist fehlgeschlagen und wurde nicht abgerechnet; protokollieren |
Der Trick ist, den Idempotenzschlüssel deterministisch zu machen: ein Hash aus den Bildbytes plus den Optionen. Stürzt das Skript ab und du startest es neu, trägt jedes erneute Senden denselben Schlüssel, und nichts wird doppelt verarbeitet oder abgerechnet.
Das Python-Skript
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])Starte es mit REMOVEBG_API_KEY=... python batch_api.py photos cutouts. Vier Worker sind ein bescheidener Anfang; bringen dich schnelle Antworten über das Minutenlimit, wartet die 429-Behandlung und sendet mit demselben Schlüssel erneut. Um URLs statt Dateien zu schicken, sende 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),
)Für lange URL-Listen hat dieselbe API POST /v1/batch, das bis zu 100 Bild-URLs als einen Job einreiht, den du abfragst.
Jeden Stapeljob robust machen
- Fertige Ausgaben überspringen, damit ein neuer Lauf fortsetzt, statt von vorn zu beginnen. Beide Skripte tun das.
- Auf Namenskollisionen achten.
shoe.jpgundshoe.pngim selben Ordner werden beide zushoe.png; benenne Eingaben um oder nimm die ursprüngliche Endung in den Ausgabenamen auf. - Fehler in eine Datei protokollieren und nur diese erneut verarbeiten.
- Eine Stichprobe prüfen bei 100 % Zoom auf dunklem und hellem Hintergrund, bevor du alles verarbeitest.
- Originale unangetastet lassen. Schreib in einen separaten Ausgabeordner.
- Das Ausgabeformat nach dem Ziel wählen. PNG oder WebP für Transparenz, JPG auf Weiß für Marktplätze. Siehe PNG, JPEG oder WebP.
Fragen
Ist rembg für kommerzielle Nutzung kostenlos?
rembg selbst steht unter MIT-Lizenz, aber jedes Modell hat eine eigene Lizenz. Das Standardmodell, BRIAs RMBG-2.0 (bria-rmbg), verlangt für kommerzielle Nutzung eine kostenpflichtige Vereinbarung. Prüfe also die Lizenz des Modells, das du nutzt, oder wähle ein anderes.
Nutzt rembg automatisch meine GPU?
Ja, wenn du rembg[gpu] installierst und ONNX Runtime den CUDA-Provider laden kann (was passendes CUDA und cuDNN braucht). rembg nutzt CUDA oder ROCm, wenn verfügbar, und warnt, wenn es auf die CPU zurückfällt. Prüfe onnxruntime.get_available_providers().
Welches rembg-Modell liefert die beste Qualität?
Die neueren Modelle, bria-rmbg (der Standard), die birefnet-Familie und isnet-general-use, erzeugen weichere und besser geformte Kanten als die ältere u2net-Familie. birefnet-portrait ist ein guter Start für Personen. Teste eine Stichprobe deiner eigenen Bilder, denn die Ergebnisse hängen vom Motiv ab.
Wie vermeide ich doppelte Abrechnung, wenn ich API-Aufrufe wiederhole?
Sende einen Header Idempotency-Key und verwende ihn bei jeder Wiederholung desselben Bildes erneut. Leitest du ihn aus einem Hash der Bildbytes und Optionen ab, nutzt selbst ein kompletter Neustart des Skripts dieselben Schlüssel.
Was sollte mein Skript mit einer 202-Antwort tun?
Sie als noch in Bearbeitung behandeln, nicht als Bild. Lies die Job-id aus dem JSON-Body und frage GET /v1/jobs/{id}/result ab, wobei du jedes Mal Retry-After abwartest, wenn die Antwort 409 not_ready lautet. Lade das Bild nicht erneut hoch.
Wie schnell darf ich Anfragen an die API von removebackground.dev senden?
Bis zu 60 Verarbeitungsanfragen pro Minute pro Konto. Darüber antwortet die API mit 429 rate_limited und einem Header Retry-After. Für viele URLs nimmt POST /v1/batch bis zu 100 pro Anfrage an.
Probier es mit deinem eigenen Bild
Neue Konten bekommen 50 kostenlose Credits. Ein Credit pro Bild, kein Abo, und Credits verfallen nie.
Hintergrund entfernenQuellen
- 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)