Anda punya satu folder berisi beberapa ratus foto produk, foto profil, atau gambar listing, dan semuanya perlu dihapus background-nya. Mengerjakannya satu per satu di aplikasi edit berarti seharian penuh mengeklik. Di Python, ini cukup sebuah loop, dan keputusan sebenarnya adalah apa yang berjalan di dalam loop itu: model open source di komputer Anda sendiri, atau API HTTP.
Panduan ini membangun keduanya, dengan skrip lengkap yang bisa langsung Anda jalankan pada sebuah folder hari ini, dan membahas bagian yang membuat proses batch bisa diandalkan: pemilihan model, CPU vs GPU, retry, batas rate, idempotensi, dan melanjutkan proses setelah crash.
Opsi A atau opsi B?
| Model lokal (rembg) | API HTTP | |
|---|---|---|
| Biaya | Software gratis; Anda membayar dengan hardware dan waktu | Per gambar |
| Persiapan | Python 3.11 sampai 3.13, unduhan model, CUDA opsional | API key dan requests |
| Kecepatan | Tergantung CPU atau GPU Anda dan modelnya | Tergantung layanannya, request paralel |
| Kualitas | Tergantung model yang Anda pilih | Tergantung layanannya |
| Privasi | Gambar tidak pernah keluar dari komputer Anda | Gambar diunggah ke pihak ketiga |
| Perawatan | Anda yang mengelola model, driver, dan lisensi | Diurus penyedia layanan |
| Cocok untuk | Kerja offline, gambar sensitif, eksperimen | Pipeline produksi, server tanpa GPU |
Banyak tim memakai keduanya: model lokal untuk eksperimen dan tool internal, API untuk produksi, saat kualitas yang konsisten dan tidak perlu server GPU lebih penting.
Opsi A: rembg, jalur open source
rembg adalah library Python dan CLI berlisensi MIT yang membungkus beberapa model penghapus background dan menjalankannya dengan ONNX Runtime. Saat tulisan ini dibuat, rilis terbarunya adalah 2.0.86, dan membutuhkan Python 3.11 atau lebih baru, tetapi lebih lama dari 3.14.
Instalasi
Pilih backend yang sesuai dengan hardware Anda. Build CPU bisa berjalan di mana saja:
python -m venv .venv && source .venv/bin/activate
pip install "rembg[cpu]" # library only
pip install "rembg[cpu,cli]" # library plus the rembg commandUntuk GPU NVIDIA, instal rembg[gpu] sebagai gantinya. Proyek ini mencatat bahwa opsi ini membutuhkan onnxruntime-gpu serta CUDA dan cuDNN yang versinya cocok, dan menyarankan kembali ke build CPU jika Anda tidak bisa menginstalnya. GPU AMD di Linux memakai rembg[rocm] dengan onnxruntime-rocm.
rembg otomatis memilih execution provider CUDA (atau ROCm) jika ONNX Runtime melaporkannya tersedia, dan memberi peringatan jika kembali ke CPU. Untuk mengecek apa yang terdeteksi oleh ONNX Runtime:
import onnxruntime
print(onnxruntime.get_available_providers()) # look for CUDAExecutionProviderPilih model
Model diunduh saat pertama kali dipakai ke ~/.rembg/models/ (atur REMBG_HOME untuk mengubahnya). Beberapa model yang berguna dari daftar proyek ini:
| Model | Catatan |
|---|---|
bria-rmbg | Default. RMBG-2.0 dari BRIA, sekitar 1.02 GB, berjalan di 1024 x 1024. Lisensinya mewajibkan perjanjian berbayar untuk penggunaan komersial. |
birefnet-general | Model BiRefNet serbaguna; tepi lembut yang bagus |
birefnet-general-lite | BiRefNet yang lebih ringan untuk penggunaan umum |
birefnet-portrait | Disesuaikan untuk foto orang |
isnet-general-use | Model IS-Net serbaguna |
u2net | Model umum klasik; lebih cepat, tepi lebih kasar |
u2netp | u2net versi ringan; untuk tes cepat di komputer lambat |
silueta | Sama seperti u2net, diperkecil menjadi 43 MB |
Ada dua hal yang lebih penting daripada kelihatannya. Pertama, model default tidak gratis untuk penggunaan komersial: bobot model punya lisensinya sendiri, terpisah dari lisensi MIT rembg, jadi cek sumber model mana pun yang Anda pakai di produk. Kedua, model yang lebih baru (bria-rmbg, birefnet-*, isnet-general-use) menghasilkan tepi yang lembut dan rapi, sedangkan keluarga u2net yang lebih lama cenderung memberi tepi yang lebih keras dan kasar.
Opsi kualitas tepi
rembg menawarkan tiga cara untuk menangani piksel tepi yang lembut seperti rambut dan bulu:
- Default: cukup untuk objek bertepi tegas seperti produk, mobil, dan logo.
decontaminate=True(-dcdi CLI): menghapus halo berwarna yang ditinggalkan background lama di piksel tepi. Opsi ini hanya mengubah warna, tidak pernah mengubah bentuk mask, nyaris tanpa biaya, dan aman dibiarkan aktif untuk satu batch penuh.alpha_matting=True(-a): menghitung ulang tepinya sendiri saat bentuk mask salah, misalnya helai rambut terpotong. Opsi ini jauh lebih lambat dan bisa gagal konvergen; dalam kasus itu rembg kembali ke hasil potongan yang sudah didekontaminasi.
Untuk batch yang berjalan tanpa diawasi, proyek ini menyarankan -dc daripada -a karena lebih cepat dan tidak bisa gagal. Panduan alpha channel menjelaskan mengapa halo muncul.
Cara cepat: CLI
rembg p -m birefnet-general -dc photos/ cutouts/rembg p memproses satu folder penuh; tambahkan -w untuk memantau folder dan memproses file baru begitu muncul.
Skrip Python
Versi ini memuat model sekali saja, melewati file yang sudah selesai sehingga Anda bisa menghentikan dan menjalankannya lagi, dan tetap jalan saat satu file gagal:
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:])Jalankan dengan python batch_rembg.py photos cutouts birefnet-general. Membuat session sekali itu penting: memuat model jauh lebih lama daripada menjalankannya, dan README rembg menyarankan memakai ulang session untuk performa batch. Hasilnya selalu PNG karena memiliki alpha channel.
Kekurangan rembg
- Jalankan pertama lambat karena model sedang diunduh, dan model besar butuh banyak RAM atau VRAM.
- Throughput CPU terbatas dengan model berkualitas tinggi. Tes dengan 20 gambar yang mewakili sebelum memproses seribu gambar.
- Lisensi dan driver adalah urusan Anda. Kunci versi rembg dan model Anda supaya upgrade di kemudian hari tidak diam-diam mengubah hasil.
Opsi B: API HTTP
Dengan API, pekerjaan berat dilakukan di GPU milik pihak lain, dan skrip Anda sebagian besar hanya perlu menangani jaringan dengan baik. Contoh ini memakai API removebackground.dev karena kontraknya dipublikasikan sebagai OpenAPI 3.1 di /openapi.json; pola yang sama berlaku untuk layanan serupa mana pun.
Kontraknya
POST https://removebackground.dev/v1/removedengan key Anda di headerx-api-key.- Kirim gambar sebagai field multipart
image_file(JPG, PNG, atau WebP, hingga 25 MB), atau sebagai JSON denganimage_url. - Field opsional mencakup
format(png,jpg, atauwebp),bg_color,size(preview,medium,hd,full), dancrop. - 200 berarti body-nya adalah gambar yang sudah jadi. 202 berarti gambar masih diproses setelah sekitar 55 detik: body-nya berupa JSON dengan
idjob, lalu Anda melakukan polling keGET /v1/jobs/{id}/result, yang menjawab 409not_readydenganRetry-Aftersampai gambarnya siap. Jangan pernah menyimpan body 202 sebagai gambar. - Maksimal 60 request pemrosesan per menit per akun; lebih dari itu Anda mendapat 429
rate_limiteddenganRetry-After. - Header
Idempotency-Keymembuat retry aman: key yang sama dengan request yang sama mengembalikan job awal, bukan menagih lagi. Key yang sama dengan request berbeda dijawab 409idempotency_key_reused. - Hasil dihapus setelah 48 jam, jadi simpan begitu Anda menerimanya.
Aturan retry supaya Anda tidak membayar dua kali
| Respons | Yang harus dilakukan |
|---|---|
| 200 | Simpan gambarnya |
| 202 | Lakukan polling hasil job; jangan unggah ulang |
| 400, 401, 402, 413, 422 | Perbaiki request, key, kredit, atau gambarnya; jangan diulang terus |
| 429 | Tunggu Retry-After detik, kirim ulang dengan key yang sama |
| 500, 503, timeout, koneksi terputus | Tunggu bertahap lalu kirim ulang dengan key yang sama |
| 502 | Gambar itu gagal dan tidak ditagih; catat di log |
Kuncinya adalah membuat idempotency key yang deterministik: hash dari byte gambar ditambah opsinya. Jika skrip crash dan Anda menjalankannya lagi, setiap pengiriman ulang membawa key yang sama, jadi tidak ada yang diproses atau ditagih dua kali.
Skrip 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])Jalankan dengan REMOVEBG_API_KEY=... python batch_api.py photos cutouts. Empat worker adalah awal yang wajar; jika respons yang cepat membuat Anda melewati batas per menit, penanganan 429 akan menunggu lalu mengirim ulang dengan key yang sama. Untuk mengirim URL alih-alih file, kirim 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),
)Untuk daftar URL yang panjang, API yang sama punya POST /v1/batch, yang mengantrekan hingga 100 URL gambar sebagai satu job yang bisa Anda polling.
Membuat batch job apa pun lebih andal
- Lewati output yang sudah selesai supaya saat dijalankan ulang, proses berlanjut, bukan mulai dari awal. Kedua skrip melakukan ini.
- Waspadai nama file yang bentrok.
shoe.jpgdanshoe.pngdi folder yang sama sama-sama menjadishoe.png; ganti nama input atau sertakan ekstensi asli di nama output. - Catat kegagalan ke file lalu jalankan ulang hanya yang gagal.
- Cek sampel secara acak di zoom 100% pada background gelap dan terang sebelum memproses semuanya.
- Jangan ubah file asli. Tulis hasilnya ke folder output terpisah.
- Pilih format output sesuai tujuan. PNG atau WebP untuk transparansi, JPG dengan background putih untuk marketplace. Lihat PNG vs JPEG vs WebP.
Pertanyaan
Apakah rembg gratis untuk penggunaan komersial?
rembg sendiri berlisensi MIT, tetapi setiap model punya lisensinya sendiri. Model default, RMBG-2.0 dari BRIA (bria-rmbg), mewajibkan perjanjian berbayar untuk penggunaan komersial, jadi cek lisensi model yang Anda pakai atau pilih model lain.
Apakah rembg otomatis memakai GPU saya?
Ya, jika Anda menginstal rembg[gpu] dan ONNX Runtime bisa memuat provider CUDA (yang membutuhkan CUDA dan cuDNN dengan versi yang cocok). rembg memakai CUDA atau ROCm jika tersedia dan memberi peringatan saat kembali ke CPU. Cek dengan onnxruntime.get_available_providers().
Model rembg mana yang memberi kualitas terbaik?
Model yang lebih baru, yaitu bria-rmbg (default), keluarga birefnet, dan isnet-general-use, menghasilkan tepi yang lebih lembut dan rapi dibanding keluarga u2net yang lebih lama. birefnet-portrait adalah titik awal yang bagus untuk foto orang. Tes dengan sampel gambar Anda sendiri, karena hasilnya berbeda tergantung objeknya.
Bagaimana cara menghindari tagihan ganda saat mengulang panggilan API?
Kirim header Idempotency-Key dan pakai ulang key itu di setiap retry untuk gambar yang sama. Jika key dibuat dari hash byte gambar dan opsinya, bahkan menjalankan ulang seluruh skrip pun akan memakai key yang sama.
Apa yang harus dilakukan skrip saya dengan respons 202?
Anggap gambar masih diproses, bukan sebagai gambar jadi. Baca id job dari body JSON lalu lakukan polling ke GET /v1/jobs/{id}/result, dan tunggu sesuai Retry-After setiap kali jawabannya 409 not_ready. Jangan unggah ulang gambarnya.
Seberapa cepat saya bisa mengirim request ke API removebackground.dev?
Hingga 60 request pemrosesan per menit per akun. Lebih dari itu, API menjawab 429 rate_limited dengan header Retry-After. Untuk banyak URL, POST /v1/batch menerima hingga 100 URL per request.
Coba dengan gambar Anda sendiri
Akun baru mendapat 50 kredit gratis. Satu kredit per gambar, tanpa langganan, dan kredit tidak pernah kedaluwarsa.
Hapus backgroundSumber
- 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)