Masz folder z kilkuset zdjęciami produktów, portretami albo zdjęciami do ofert i z każdego trzeba usunąć tło. Ręcznie w edytorze to dzień klikania. W Pythonie to jedna pętla, a prawdziwa decyzja dotyczy tego, co działa w jej środku: otwartoźródłowy model na Twoim komputerze czy API HTTP.
W tym poradniku budujemy oba warianty, z kompletnymi skryptami, które możesz uruchomić na folderze jeszcze dziś. Omawiamy też to, co sprawia, że hurtowe usuwanie tła działa niezawodnie: wybór modelu, CPU czy GPU, ponowienia, limity zapytań, idempotencję i wznawianie po awarii.
Opcja A czy opcja B?
| Model lokalny (rembg) | API HTTP | |
|---|---|---|
| Koszt | Darmowe oprogramowanie; płacisz sprzętem i czasem | Za zdjęcie |
| Konfiguracja | Python od 3.11 do 3.13, pobieranie modeli, opcjonalnie CUDA | Klucz API i requests |
| Szybkość | Zależy od CPU lub GPU i modelu | Zależy od usługi, zapytania równoległe |
| Jakość | Zależy od wybranego modelu | Zależy od usługi |
| Prywatność | Zdjęcia nigdy nie opuszczają Twojego komputera | Zdjęcia trafiają do zewnętrznej firmy |
| Utrzymanie | Sam zarządzasz modelami, sterownikami i licencjami | Zajmuje się tym dostawca |
| Dobre do | Pracy offline, wrażliwych zdjęć, eksperymentów | Procesów produkcyjnych, serwerów bez GPU |
Wiele zespołów korzysta z obu: z lokalnego modelu do eksperymentów i narzędzi wewnętrznych, a z API na produkcji, gdzie przewidywalna jakość i brak serwerów z GPU są ważniejsze.
Opcja A: rembg, droga open source
rembg to biblioteka Pythona i narzędzie wiersza poleceń na licencji MIT, które obsługuje kilka modeli do usuwania tła i uruchamia je przez ONNX Runtime. W chwili pisania aktualne wydanie to 2.0.86 i wymaga Pythona w wersji co najmniej 3.11, ale niższej niż 3.14.
Instalacja
Wybierz backend pasujący do Twojego sprzętu. Wersja na CPU działa wszędzie:
python -m venv .venv && source .venv/bin/activate
pip install "rembg[cpu]" # library only
pip install "rembg[cpu,cli]" # library plus the rembg commandDla kart NVIDIA zainstaluj zamiast tego rembg[gpu]. Projekt zaznacza, że wymaga to onnxruntime-gpu oraz pasujących wersji CUDA i cuDNN, i radzi wrócić do wersji na CPU, jeśli nie możesz ich zainstalować. Karty AMD w Linuksie używają rembg[rocm] z onnxruntime-rocm.
rembg sam wybiera dostawcę wykonawczego CUDA (lub ROCm), gdy ONNX Runtime zgłasza go jako dostępnego, i ostrzega, jeśli przełącza się na CPU. Aby sprawdzić, co widzi ONNX Runtime:
import onnxruntime
print(onnxruntime.get_available_providers()) # look for CUDAExecutionProviderWybór modelu
Modele pobierają się przy pierwszym użyciu do ~/.rembg/models/ (zmienna REMBG_HOME zmienia tę lokalizację). Kilka przydatnych z listy projektu:
| Model | Uwagi |
|---|---|
bria-rmbg | Domyślny. RMBG-2.0 od BRIA, około 1,02 GB, działa w rozdzielczości 1024 x 1024. Licencja wymaga płatnej umowy przy użyciu komercyjnym. |
birefnet-general | Uniwersalny model BiRefNet; dobre miękkie krawędzie |
birefnet-general-lite | Lżejszy BiRefNet do ogólnych zastosowań |
birefnet-portrait | Dostrojony do zdjęć ludzi |
isnet-general-use | Uniwersalny model IS-Net |
u2net | Klasyczny model ogólny; szybszy, bardziej kanciaste krawędzie |
u2netp | Lekki u2net; szybkie testy na wolnych komputerach |
silueta | To samo co u2net, zmniejszone do 43 MB |
Dwie rzeczy są ważniejsze, niż się wydaje. Po pierwsze, domyślny model nie jest darmowy do użytku komercyjnego: wagi modeli mają własne licencje, niezależne od licencji MIT rembg, więc sprawdź źródło modelu, którego używasz w produkcie. Po drugie, nowsze modele (bria-rmbg, birefnet-*, isnet-general-use) dają miękkie, dobrze ukształtowane krawędzie, a starsza rodzina u2net zwykle daje twardsze, bardziej kanciaste.
Opcje jakości krawędzi
rembg oferuje trzy sposoby traktowania miękkich pikseli na krawędziach, takich jak włosy i sierść:
- Domyślny: wystarcza przy obiektach o twardych krawędziach, takich jak produkty, samochody i logo.
decontaminate=True(-dcw wierszu poleceń): usuwa kolorową poświatę, którą stare tło zostawia w pikselach krawędzi. Zmienia tylko kolor, nigdy kształtu maski, prawie nic nie kosztuje i można go bezpiecznie włączyć dla całej partii.alpha_matting=True(-a): ponownie wyznacza samą krawędź, gdy kształt maski jest błędny, na przykład gdy ucięło kosmyki. Jest dużo wolniejszy i może się nie zbiec, a wtedy rembg zwraca wycinek z samą dekontaminacją.
Przy partiach przetwarzanych bez nadzoru projekt zaleca -dc zamiast -a, bo jest szybsze i nie może zawieść. Poradnik o kanale alfa wyjaśnia, skąd biorą się poświaty.
Szybki sposób: wiersz poleceń
rembg p -m birefnet-general -dc photos/ cutouts/rembg p przetwarza cały folder; dodaj -w, aby obserwować folder i przetwarzać nowe pliki, gdy się pojawią.
Skrypt w Pythonie
Ta wersja ładuje model raz, pomija pliki już przetworzone, więc możesz ją zatrzymać i uruchomić ponownie, i działa dalej, gdy jeden plik się nie powiedzie:
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:])Uruchom go poleceniem python batch_rembg.py photos cutouts birefnet-general. Jednorazowe utworzenie sesji ma znaczenie: ładowanie modelu trwa znacznie dłużej niż jego działanie, a README rembg zaleca ponowne użycie sesji dla wydajności przy partiach. Wyniki są zawsze w PNG, bo mają kanał alfa.
Gdzie rembg nie wystarcza
- Pierwsze uruchomienie jest wolne, bo trwa pobieranie modelu, a duże modele potrzebują dużo RAM lub VRAM.
- Wydajność na CPU jest umiarkowana przy modelach wysokiej jakości. Przetestuj 20 reprezentatywnych zdjęć, zanim weźmiesz się za tysiąc.
- Licencje i sterowniki są na Twojej głowie. Przypnij wersje rembg i modeli, żeby późniejsza aktualizacja nie zmieniła po cichu wyników.
Opcja B: API HTTP
Przy API ciężką pracę wykonują cudze GPU, a Twój skrypt zajmuje się głównie dobrą obsługą sieci. Przykład używa API removebackground.dev, bo jego kontrakt jest opublikowany jako OpenAPI 3.1 pod /openapi.json; te same wzorce sprawdzą się w każdej podobnej usłudze.
Kontrakt
POST https://removebackground.dev/v1/removez kluczem w nagłówkux-api-key.- Wyślij zdjęcie jako pole multipart
image_file(JPG, PNG lub WebP, do 25 MB) albo jako JSON zimage_url. - Opcjonalne pola to między innymi
format(png,jpglubwebp),bg_color,size(preview,medium,hd,full) icrop. - 200 oznacza, że treść odpowiedzi to gotowy obraz. 202 oznacza, że zdjęcie wciąż jest przetwarzane po około 55 sekundach: treść to JSON z
idzadania, a Ty odpytujeszGET /v1/jobs/{id}/result, który odpowiada 409not_readyzRetry-After, dopóki obraz nie będzie gotowy. Nigdy nie zapisuj treści odpowiedzi 202 jako obrazu. - Do 60 zapytań przetwarzających na minutę na konto; powyżej dostajesz 429
rate_limitedzRetry-After. - Nagłówek
Idempotency-Keysprawia, że ponowienia są bezpieczne: ten sam klucz z tym samym zapytaniem zwraca pierwotne zadanie zamiast pobierać opłatę drugi raz. Ten sam klucz z innym zapytaniem daje odpowiedź 409idempotency_key_reused. - Wyniki są usuwane po 48 godzinach, więc zapisuj je od razu.
Zasady ponowień, dzięki którym nie zapłacisz dwa razy
| Odpowiedź | Co zrobić |
|---|---|
| 200 | Zapisz obraz |
| 202 | Odpytuj wynik zadania; nie wysyłaj zdjęcia ponownie |
| 400, 401, 402, 413, 422 | Popraw zapytanie, klucz, kredyty lub zdjęcie; nie ponawiaj w pętli |
| 429 | Odczekaj Retry-After sekund, wyślij ponownie z tym samym kluczem |
| 500, 503, przekroczenie czasu, zerwane połączenie | Odczekaj i wyślij ponownie z tym samym kluczem |
| 502 | To zdjęcie się nie udało i nie zostało policzone; zapisz to w logu |
Cała sztuka polega na tym, żeby klucz idempotencji był deterministyczny: hash bajtów zdjęcia plus opcji. Jeśli skrypt się wysypie i uruchomisz go ponownie, każde ponowne wysłanie ma ten sam klucz, więc nic nie zostanie przetworzone ani policzone dwa razy.
Skrypt w Pythonie
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])Uruchom go poleceniem REMOVEBG_API_KEY=... python batch_api.py photos cutouts. Cztery wątki to skromny początek; jeśli szybkie odpowiedzi wypchną Cię ponad limit na minutę, obsługa 429 odczeka i wyśle zapytanie ponownie z tym samym kluczem. Aby wysyłać adresy URL zamiast plików, wyślij 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),
)Przy długich listach adresów URL to samo API ma POST /v1/batch, które kolejkuje do 100 adresów zdjęć jako jedno zadanie do odpytywania.
Jak zrobić każde przetwarzanie hurtowe odpornym na błędy
- Pomijaj gotowe pliki wynikowe, żeby ponowne uruchomienie wznawiało pracę zamiast zaczynać od zera. Oba skrypty to robią.
- Uważaj na kolizje nazw.
shoe.jpgishoe.pngw tym samym folderze dają ten samshoe.png; zmień nazwy plików wejściowych albo dodaj pierwotne rozszerzenie do nazwy pliku wynikowego. - Zapisuj błędy do pliku i uruchamiaj ponownie tylko te pliki.
- Sprawdź próbkę w powiększeniu 100% na ciemnym i jasnym tle, zanim przetworzysz wszystko.
- Nie ruszaj oryginałów. Zapisuj wyniki do osobnego folderu.
- Dobierz format pliku do miejsca docelowego. PNG lub WebP, gdy potrzebujesz przezroczystości, JPG na białym tle dla marketplace'ów. Zobacz PNG, JPEG czy WebP.
Pytania
Czy rembg można używać komercyjnie za darmo?
Sam rembg ma licencję MIT, ale każdy model ma własną licencję. Domyślny model, RMBG-2.0 od BRIA (bria-rmbg), wymaga płatnej umowy przy użyciu komercyjnym, więc sprawdź licencję modelu, którego używasz, albo wybierz inny.
Czy rembg automatycznie korzysta z mojej karty graficznej?
Tak, jeśli zainstalujesz rembg[gpu], a ONNX Runtime może załadować dostawcę CUDA (co wymaga pasujących wersji CUDA i cuDNN). rembg używa CUDA lub ROCm, gdy są dostępne, i ostrzega, gdy przełącza się na CPU. Sprawdź onnxruntime.get_available_providers().
Który model rembg daje najlepszą jakość?
Nowsze modele, bria-rmbg (domyślny), rodzina birefnet i isnet-general-use, dają miększe i lepiej ukształtowane krawędzie niż starsza rodzina u2net. Do zdjęć ludzi dobrym punktem wyjścia jest birefnet-portrait. Przetestuj próbkę własnych zdjęć, bo wyniki zależą od tematu.
Jak uniknąć podwójnej opłaty przy ponawianiu wywołań API?
Wysyłaj nagłówek Idempotency-Key i używaj tego samego klucza przy każdym ponowieniu dla tego samego zdjęcia. Jeśli wyliczasz go z hasha bajtów zdjęcia i opcji, nawet ponowne uruchomienie całego skryptu użyje tych samych kluczy.
Co skrypt powinien zrobić z odpowiedzią 202?
Potraktuj ją jako zadanie wciąż w toku, a nie jako obraz. Odczytaj id zadania z treści JSON i odpytuj GET /v1/jobs/{id}/result, czekając tyle, ile podaje Retry-After, za każdym razem, gdy odpowiedź to 409 not_ready. Nie wysyłaj zdjęcia ponownie.
Jak szybko mogę wysyłać zapytania do API removebackground.dev?
Do 60 zapytań przetwarzających na minutę na konto. Powyżej tego limitu API odpowiada 429 rate_limited z nagłówkiem Retry-After. Przy wielu adresach URL POST /v1/batch przyjmuje do 100 w jednym zapytaniu.
Wypróbuj na własnym zdjęciu
Nowe konta dostają 50 darmowych kredytów. Jeden kredyt za zdjęcie, bez subskrypcji, a kredyty nigdy nie wygasają.
Usuń tłoŹródła
- 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)