ลบพื้นหลังรูปภาพทีละหลายรูปด้วย Python: rembg หรือ API

สคริปต์ครบชุด 2 แบบสำหรับลบพื้นหลังรูปทั้งโฟลเดอร์ แบบแรกใช้ไลบรารีโอเพนซอร์ส rembg บนเครื่องของคุณเอง แบบที่สองใช้ HTTP API ที่จัดการการลองใหม่ ขีดจำกัดอัตราการเรียก และการคิดเงินซ้ำได้อย่างถูกต้อง

อัปเดต 23 ตุลาคม 2569 · อ่าน 5 นาที

ข้ามขั้นตอนไปเลย: ลบพื้นหลังในคลิกเดียว อัปโหลดรูปภาพ

คุณมีโฟลเดอร์รูปสินค้า รูปโปรไฟล์ หรือรูปประกาศขายอยู่หลายร้อยรูป และทุกรูปต้องลบพื้นหลัง ถ้าทำด้วยมือในโปรแกรมแต่งรูปก็ต้องคลิกกันทั้งวัน แต่ใน Python มันคือลูปเดียว สิ่งที่ต้องตัดสินใจจริงๆ คือจะให้อะไรทำงานอยู่ในลูปนั้น: โมเดลโอเพนซอร์สบนเครื่องของคุณเอง หรือ HTTP API

คู่มือนี้สร้างทั้งสองแบบ พร้อมสคริปต์ครบชุดที่นำไปรันกับโฟลเดอร์ได้ทันที และอธิบายส่วนที่ทำให้งานแบบแบตช์เชื่อถือได้: การเลือกโมเดล, CPU กับ GPU, การลองใหม่, ขีดจำกัดอัตราการเรียก, idempotency และการทำงานต่อหลังโปรแกรมล่ม

เลือกแบบ A หรือแบบ B

โมเดลบนเครื่อง (rembg)HTTP API
ค่าใช้จ่ายซอฟต์แวร์ฟรี แต่จ่ายเป็นค่าฮาร์ดแวร์และเวลาคิดต่อรูป
การติดตั้งPython 3.11 ถึง 3.13, ดาวน์โหลดโมเดล, CUDA (ไม่บังคับ)API key และ requests
ความเร็วขึ้นอยู่กับ CPU หรือ GPU และโมเดลที่ใช้ขึ้นอยู่กับบริการ และการส่งคำขอพร้อมกัน
คุณภาพขึ้นอยู่กับโมเดลที่เลือกขึ้นอยู่กับบริการ
ความเป็นส่วนตัวรูปไม่ออกจากเครื่องของคุณเลยรูปถูกอัปโหลดไปยังบุคคลที่สาม
การดูแลรักษาคุณต้องจัดการโมเดล ไดรเวอร์ และไลเซนส์เองผู้ให้บริการจัดการให้
เหมาะกับงานออฟไลน์ รูปที่เป็นความลับ การทดลองระบบโปรดักชัน เซิร์ฟเวอร์ที่ไม่มี GPU

หลายทีมใช้ทั้งสองแบบ: ใช้โมเดลบนเครื่องสำหรับการทดลองและเครื่องมือภายใน และใช้ API ในโปรดักชัน ซึ่งคุณภาพที่คาดเดาได้และการไม่ต้องดูแลเซิร์ฟเวอร์ GPU สำคัญกว่า

แบบ A: rembg ทางเลือกโอเพนซอร์ส

rembg เป็นไลบรารี Python และ CLI ภายใต้ไลเซนส์ MIT ที่รวมโมเดลลบพื้นหลังหลายตัวไว้ และรันด้วย ONNX Runtime ขณะที่เขียนบทความนี้ เวอร์ชันล่าสุดคือ 2.0.86 และต้องใช้ Python 3.11 ขึ้นไปแต่ต่ำกว่า 3.14

ติดตั้ง

เลือกแบ็กเอนด์ที่ตรงกับฮาร์ดแวร์ของคุณ บิลด์สำหรับ CPU ใช้ได้ทุกที่:

python -m venv .venv && source .venv/bin/activate
pip install "rembg[cpu]"        # library only
pip install "rembg[cpu,cli]"    # library plus the rembg command

สำหรับ GPU ของ NVIDIA ให้ติดตั้ง rembg[gpu] แทน โปรเจกต์ระบุว่าต้องใช้ onnxruntime-gpu พร้อม CUDA และ cuDNN เวอร์ชันที่ตรงกัน และแนะนำให้กลับไปใช้บิลด์ CPU ถ้าติดตั้งสิ่งเหล่านี้ไม่ได้ ส่วน GPU ของ AMD บน Linux ใช้ rembg[rocm] กับ onnxruntime-rocm

rembg จะเลือก execution provider ของ CUDA (หรือ ROCm) ให้อัตโนมัติเมื่อ ONNX Runtime แจ้งว่าใช้งานได้ และจะเตือนถ้าต้องกลับไปใช้ CPU วิธีเช็กว่า ONNX Runtime มองเห็นอะไรบ้าง:

import onnxruntime
print(onnxruntime.get_available_providers())  # look for CUDAExecutionProvider

เลือกโมเดล

โมเดลจะถูกดาวน์โหลดตอนใช้งานครั้งแรกไปที่ ~/.rembg/models/ (ตั้งค่า REMBG_HOME เพื่อเปลี่ยนตำแหน่ง) ตัวที่น่าสนใจจากรายการของโปรเจกต์:

โมเดลหมายเหตุ
bria-rmbgค่าเริ่มต้น คือ RMBG-2.0 ของ BRIA ขนาดประมาณ 1.02 GB ทำงานที่ 1024 x 1024 ไลเซนส์กำหนดให้ต้องทำข้อตกลงแบบเสียเงินหากใช้เชิงพาณิชย์
birefnet-generalโมเดล BiRefNet สำหรับงานทั่วไป ขอบนุ่มดี
birefnet-general-liteBiRefNet รุ่นเบาสำหรับงานทั่วไป
birefnet-portraitปรับมาสำหรับรูปคน
isnet-general-useโมเดล IS-Net สำหรับงานทั่วไป
u2netโมเดลทั่วไปรุ่นคลาสสิก เร็วกว่า แต่ขอบหยาบกว่า
u2netpu2net รุ่นเบา สำหรับทดสอบเร็วๆ บนเครื่องที่ช้า
siluetaเหมือน u2net แต่ลดขนาดเหลือ 43 MB

มีสองเรื่องที่สำคัญกว่าที่เห็น เรื่องแรก โมเดลเริ่มต้นไม่ได้ฟรีสำหรับการใช้เชิงพาณิชย์: น้ำหนักของโมเดลมีไลเซนส์ของตัวเอง แยกจากไลเซนส์ MIT ของ rembg ดังนั้นให้ตรวจสอบแหล่งที่มาของโมเดลที่คุณจะนำไปใช้จริง เรื่องที่สอง โมเดลรุ่นใหม่ (bria-rmbg, birefnet-*, isnet-general-use) ให้ขอบที่นุ่มและได้รูปทรงดี ส่วนตระกูล u2net รุ่นเก่ามักให้ขอบที่แข็งและหยาบกว่า

ตัวเลือกคุณภาพขอบ

rembg มีวิธีจัดการพิกเซลขอบที่นุ่ม เช่น เส้นผมและขนสัตว์ อยู่ 3 แบบ:

  • ค่าเริ่มต้น: เพียงพอสำหรับวัตถุที่ขอบคม เช่น สินค้า รถยนต์ และโลโก้
  • decontaminate=True (-dc ใน CLI): ลบขอบสีที่พื้นหลังเดิมทิ้งไว้ในพิกเซลขอบ เปลี่ยนแค่สี ไม่เปลี่ยนรูปทรงของมาสก์ ใช้ทรัพยากรน้อยมาก และเปิดไว้ทั้งแบตช์ได้อย่างปลอดภัย
  • alpha_matting=True (-a): คำนวณขอบใหม่เมื่อรูปทรงของมาสก์ผิด เช่น เส้นผมขาดหาย ช้ากว่ามากและอาจคำนวณไม่ลู่เข้า ซึ่งในกรณีนั้น rembg จะกลับไปใช้ผลแบบ decontaminate แทน

สำหรับแบตช์ที่ปล่อยให้รันเองโดยไม่มีคนดู โปรเจกต์แนะนำ -dc มากกว่า -a เพราะเร็วกว่าและไม่มีทางล้มเหลว คู่มือ alpha channel อธิบายว่าทำไมจึงเกิดขอบสี

วิธีเร็ว: ใช้ CLI

rembg p -m birefnet-general -dc photos/ cutouts/

rembg p ประมวลผลทั้งโฟลเดอร์ เพิ่ม -w เพื่อเฝ้าดูโฟลเดอร์และประมวลผลไฟล์ใหม่ทันทีที่มีเข้ามา

สคริปต์ Python

เวอร์ชันนี้โหลดโมเดลครั้งเดียว ข้ามไฟล์ที่ทำเสร็จแล้วเพื่อให้หยุดและเริ่มใหม่ได้ และทำงานต่อไปแม้มีไฟล์ใดไฟล์หนึ่งล้มเหลว:

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:])

รันด้วย python batch_rembg.py photos cutouts birefnet-general การสร้าง session ครั้งเดียวสำคัญมาก เพราะการโหลดโมเดลใช้เวลานานกว่าการรันมาก และ README ของ rembg ก็แนะนำให้ใช้ session ซ้ำเพื่อประสิทธิภาพของงานแบตช์ ไฟล์ผลลัพธ์เป็น PNG เสมอ เพราะผลลัพธ์มี alpha channel

ข้อจำกัดของ rembg

  • รันครั้งแรกช้า ระหว่างดาวน์โหลดโมเดล และโมเดลขนาดใหญ่ต้องใช้ RAM หรือ VRAM มาก
  • ความเร็วบน CPU ค่อนข้างจำกัด เมื่อใช้โมเดลคุณภาพสูง ให้ทดสอบกับรูปตัวอย่าง 20 รูปก่อนลงมือทำหนึ่งพันรูป
  • ไลเซนส์และไดรเวอร์เป็นหน้าที่ของคุณ ล็อกเวอร์ชันของ rembg และโมเดลไว้ เพื่อไม่ให้การอัปเกรดในภายหลังเปลี่ยนผลลัพธ์โดยไม่รู้ตัว

แบบ B: HTTP API

เมื่อใช้ API งานหนักจะเกิดขึ้นบน GPU ของคนอื่น และสคริปต์ของคุณจะเน้นไปที่การจัดการเครือข่ายให้ดี ตัวอย่างนี้ใช้ API ของ removebackground.dev เพราะสัญญาการใช้งานเผยแพร่เป็น OpenAPI 3.1 ที่ /openapi.json ส่วนรูปแบบเดียวกันนี้ใช้ได้กับบริการที่คล้ายกันทุกเจ้า

สัญญาการใช้งาน

  • POST https://removebackground.dev/v1/remove พร้อม key ในเฮดเดอร์ x-api-key
  • ส่งรูปเป็นฟิลด์ multipart ชื่อ image_file (JPG, PNG หรือ WebP ขนาดไม่เกิน 25 MB) หรือเป็น JSON ด้วย image_url
  • ฟิลด์เสริมได้แก่ format (png, jpg หรือ webp), bg_color, size (preview, medium, hd, full) และ crop
  • 200 หมายถึงเนื้อหาของคำตอบคือรูปที่เสร็จแล้ว 202 หมายถึงยังประมวลผลอยู่หลังผ่านไปประมาณ 55 วินาที: เนื้อหาเป็น JSON ที่มี id ของงาน และคุณต้อง poll GET /v1/jobs/{id}/result ซึ่งจะตอบ 409 not_ready พร้อม Retry-After จนกว่ารูปจะพร้อม ห้ามบันทึกเนื้อหาของคำตอบ 202 เป็นรูปภาพเด็ดขาด
  • ส่งคำขอประมวลผลได้สูงสุด 60 ครั้งต่อนาที ต่อบัญชี ถ้าเกินจะได้ 429 rate_limited พร้อม Retry-After
  • เฮดเดอร์ Idempotency-Key ทำให้การลองใหม่ปลอดภัย: key เดิมกับคำขอเดิมจะคืนงานเดิมแทนการคิดเงินอีกครั้ง ส่วน key เดิมกับคำขอที่ต่างออกไปจะได้ 409 idempotency_key_reused
  • ผลลัพธ์จะถูกลบหลัง 48 ชั่วโมง ดังนั้นให้บันทึกทันทีที่ได้รับ

กฎการลองใหม่ที่ช่วยไม่ให้จ่ายซ้ำ

คำตอบสิ่งที่ต้องทำ
200บันทึกรูป
202poll ผลลัพธ์ของงาน ห้ามอัปโหลดซ้ำ
400, 401, 402, 413, 422แก้ไขคำขอ key เครดิต หรือรูป อย่าวนลูปส่งซ้ำ
429รอ Retry-After วินาที แล้วส่งใหม่ด้วย key เดิม
500, 503, หมดเวลา, การเชื่อมต่อหลุดรอแบบ backoff แล้วส่งใหม่ด้วย key เดิม
502รูปนั้นล้มเหลวและไม่ถูกคิดเงิน ให้บันทึกลง log

เคล็ดลับคือทำให้ idempotency key กำหนดได้แน่นอน (deterministic): ใช้แฮชของไบต์รูปภาพรวมกับตัวเลือก ถ้าสคริปต์ล่มแล้วคุณรันใหม่ การส่งซ้ำทุกครั้งจะใช้ key เดิม จึงไม่มีอะไรถูกประมวลผลหรือคิดเงินซ้ำ

สคริปต์ 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])

รันด้วย REMOVEBG_API_KEY=... python batch_api.py photos cutouts เริ่มที่ 4 worker ถือว่าพอประมาณ ถ้าคำตอบกลับมาเร็วจนเกินขีดจำกัดต่อนาที ส่วนที่จัดการ 429 จะรอแล้วส่งใหม่ด้วย key เดิม ถ้าต้องการส่ง URL แทนไฟล์ ให้ POST เป็น 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),
)

สำหรับรายการ URL ยาวๆ API เดียวกันมี POST /v1/batch ซึ่งรับ URL รูปได้สูงสุด 100 รายการเป็นงานเดียวที่คุณ poll ผลได้

ทำให้งานแบตช์ทุกแบบทนทาน

  • ข้ามไฟล์ที่ทำเสร็จแล้ว เพื่อให้การรันใหม่ทำงานต่อจากเดิมแทนที่จะเริ่มใหม่ทั้งหมด สคริปต์ทั้งสองทำแบบนี้อยู่แล้ว
  • ระวังชื่อไฟล์ชนกัน shoe.jpg กับ shoe.png ในโฟลเดอร์เดียวกันจะกลายเป็น shoe.png ทั้งคู่ ให้เปลี่ยนชื่อไฟล์ต้นฉบับ หรือใส่นามสกุลเดิมไว้ในชื่อไฟล์ผลลัพธ์
  • บันทึกไฟล์ที่ล้มเหลวลงไฟล์ log แล้วรันใหม่เฉพาะไฟล์เหล่านั้น
  • สุ่มตรวจตัวอย่าง ที่ซูม 100% บนพื้นหลังสีเข้มและสีอ่อน ก่อนประมวลผลทั้งหมด
  • อย่าแตะต้นฉบับ ให้เขียนผลลัพธ์ลงโฟลเดอร์แยก
  • เลือกฟอร์แมตผลลัพธ์ให้ตรงกับปลายทาง PNG หรือ WebP สำหรับพื้นหลังโปร่งใส และ JPG บนพื้นขาวสำหรับมาร์เก็ตเพลส ดู PNG vs JPEG vs WebP

คำถามที่พบบ่อย

rembg ใช้เชิงพาณิชย์ได้ฟรีไหม

ตัว rembg ใช้ไลเซนส์ MIT แต่แต่ละโมเดลมีไลเซนส์ของตัวเอง โมเดลเริ่มต้นคือ RMBG-2.0 ของ BRIA (bria-rmbg) ต้องทำข้อตกลงแบบเสียเงินหากใช้เชิงพาณิชย์ จึงควรตรวจสอบไลเซนส์ของโมเดลที่ใช้ หรือเลือกโมเดลอื่น

rembg ใช้ GPU ให้อัตโนมัติไหม

ใช่ ถ้าคุณติดตั้ง rembg[gpu] และ ONNX Runtime โหลด CUDA provider ได้ (ต้องมี CUDA และ cuDNN เวอร์ชันที่ตรงกัน) rembg จะใช้ CUDA หรือ ROCm เมื่อพร้อมใช้งาน และจะเตือนเมื่อต้องกลับไปใช้ CPU เช็กได้ด้วย onnxruntime.get_available_providers()

โมเดลไหนของ rembg ให้คุณภาพดีที่สุด

โมเดลรุ่นใหม่ ได้แก่ bria-rmbg (ค่าเริ่มต้น) ตระกูล birefnet และ isnet-general-use ให้ขอบที่นุ่มและได้รูปทรงดีกว่าตระกูล u2net รุ่นเก่า สำหรับรูปคน birefnet-portrait เป็นจุดเริ่มต้นที่ดี ควรทดสอบกับรูปตัวอย่างของคุณเอง เพราะผลลัพธ์ต่างกันไปตามวัตถุ

จะป้องกันการถูกคิดเงินซ้ำเมื่อลองเรียก API ใหม่ได้อย่างไร

ส่งเฮดเดอร์ Idempotency-Key และใช้ค่าเดิมทุกครั้งที่ลองส่งรูปเดิมใหม่ ถ้าสร้าง key จากแฮชของไบต์รูปภาพและตัวเลือก แม้จะรันสคริปต์ใหม่ทั้งหมดก็จะใช้ key เดิม

สคริปต์ควรทำอย่างไรเมื่อได้คำตอบ 202

ให้ถือว่ายังประมวลผลอยู่ ไม่ใช่รูปภาพ อ่าน id ของงานจากเนื้อหา JSON แล้ว poll GET /v1/jobs/{id}/result โดยรอตาม Retry-After ทุกครั้งที่ได้คำตอบ 409 not_ready และห้ามอัปโหลดรูปซ้ำ

ส่งคำขอไปยัง API ของ removebackground.dev ได้เร็วแค่ไหน

ได้สูงสุด 60 คำขอประมวลผลต่อนาทีต่อบัญชี ถ้าเกิน API จะตอบ 429 rate_limited พร้อมเฮดเดอร์ Retry-After ถ้ามี URL จำนวนมาก POST /v1/batch รับได้สูงสุด 100 รายการต่อคำขอ

ลองกับรูปของคุณเอง

บัญชีใหม่รับฟรี 50 เครดิต หนึ่งเครดิตต่อรูป ไม่ต้องสมัครสมาชิกรายเดือน และเครดิตไม่มีวันหมดอายุ

ลบพื้นหลัง

แหล่งที่มา