لديك مجلد فيه بضع مئات من صور المنتجات أو الصور الشخصية أو صور الإعلانات، وكل صورة تحتاج إلى إزالة خلفيتها. القيام بذلك يدويًا في برنامج تحرير يعني يومًا كاملًا من النقر. في Python الأمر مجرد حلقة تكرار، والقرار الحقيقي هو ما الذي يعمل داخلها: نموذج مفتوح المصدر على جهازك، أم واجهة HTTP API.
يبني هذا الدليل الطريقتين معًا، بسكربتات كاملة يمكنك تشغيلها على مجلد اليوم، ويشرح ما يجعل المعالجة الدفعية موثوقة: اختيار النموذج، وCPU مقابل GPU، وإعادة المحاولة، وحدود المعدل، وعدم التكرار (idempotency)، والاستئناف بعد التعطل.
الخيار A أم الخيار B؟
| نموذج محلي (rembg) | HTTP API | |
|---|---|---|
| التكلفة | برنامج مجاني، وتدفع من عتادك ووقتك | لكل صورة |
| الإعداد | Python من 3.11 إلى 3.13، وتنزيل النماذج، وCUDA اختياريًا | مفتاح API ومكتبة requests |
| السرعة | تعتمد على CPU أو GPU لديك وعلى النموذج | تعتمد على الخدمة وعلى الطلبات المتوازية |
| الجودة | تعتمد على النموذج الذي تختاره | تعتمد على الخدمة |
| الخصوصية | الصور لا تغادر جهازك أبدًا | الصور تُرفع إلى طرف ثالث |
| الصيانة | أنت تدير النماذج والتعريفات والتراخيص | المزوّد يتولى ذلك |
| مناسب لـ | العمل دون اتصال، والصور الحساسة، والتجارب | خطوط الإنتاج، والخوادم بلا GPU |
تستخدم فرق كثيرة الطريقتين: نموذجًا محليًا للتجارب والأدوات الداخلية، وAPI للإنتاج حيث تهم الجودة المتوقعة وعدم الحاجة إلى خوادم GPU أكثر من غيرها.
الخيار A: rembg، الطريق مفتوح المصدر
rembg مكتبة Python وأداة سطر أوامر بترخيص 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لبطاقات NVIDIA الرسومية، ثبّت rembg[gpu] بدلًا من ذلك. يوضح المشروع أن هذا يحتاج إلى onnxruntime-gpu مع إصدارات متوافقة من CUDA وcuDNN، وينصح بالرجوع إلى نسخة CPU إذا تعذر تثبيتها. أما بطاقات AMD على Linux فتستخدم rembg[rocm] مع onnxruntime-rocm.
يختار rembg مزوّد التنفيذ 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-lite | نسخة أخف من BiRefNet للاستخدام العام |
birefnet-portrait | مضبوط لصور الأشخاص |
isnet-general-use | نموذج IS-Net للأغراض العامة |
u2net | النموذج العام الكلاسيكي، أسرع، وحوافه أخشن |
u2netp | نسخة خفيفة من u2net، لاختبارات سريعة على الأجهزة البطيئة |
silueta | مثل u2net، مضغوط إلى 43 MB |
نقطتان أهم مما تبدوان. الأولى: النموذج الافتراضي ليس مجانيًا للاستخدام التجاري، فأوزان النماذج لها تراخيصها الخاصة المنفصلة عن ترخيص MIT الخاص بـ rembg، لذا راجع مصدر أي نموذج تعتمده في منتجك. الثانية: النماذج الأحدث (bria-rmbg وbirefnet-* وisnet-general-use) تعطي حوافًا ناعمة ومتقنة الشكل، بينما تميل عائلة u2net الأقدم إلى حواف أصلب وأخشن.
خيارات جودة الحواف
يقدم rembg ثلاث طرق للتعامل مع بكسلات الحواف الناعمة مثل الشعر والفرو:
- الافتراضي: مناسب للعناصر ذات الحواف الحادة مثل المنتجات والسيارات والشعارات.
decontaminate=True(-dcفي سطر الأوامر): يزيل الهالة الملونة التي تتركها الخلفية القديمة في بكسلات الحواف. يغيّر اللون فقط ولا يغيّر شكل القناع أبدًا، وتكلفته ضئيلة، ويمكن تركه مفعّلًا لدفعة كاملة دون قلق.alpha_matting=True(-a): يعيد تقدير الحافة نفسها عندما يكون شكل القناع خاطئًا، كأن تُقطع خصلات الشعر. أبطأ بكثير وقد يفشل في التقارب، وعندها يرجع rembg إلى قصّ منظَّف الحواف.
للدفعات التي تعمل دون إشراف، يوصي المشروع بـ -dc بدل -a لأنه أسرع ولا يمكن أن يفشل. يشرح دليل قناة ألفا سبب ظهور الهالات.
الطريقة السريعة: سطر الأوامر
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. إنشاء الجلسة مرة واحدة مهم: تحميل النموذج يستغرق وقتًا أطول بكثير من تشغيله، وREADME الخاص بـ rembg يوصي بإعادة استخدام الجلسة لأداء أفضل في الدفعات. المخرجات دائمًا PNG لأن النتيجة تحتوي على قناة ألفا.
أين يقصّر 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مع مفتاحك في الترويسة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للمهمة، وتستعلم عبرGET /v1/jobs/{id}/resultالذي يرد بـ 409not_readyمعRetry-Afterحتى تجهز الصورة. لا تحفظ جسم استجابة 202 كصورة أبدًا. - حتى 60 طلب معالجة في الدقيقة لكل حساب، وما يزيد على ذلك يعيد 429
rate_limitedمعRetry-After. - الترويسة
Idempotency-Keyتجعل إعادة المحاولة آمنة: المفتاح نفسه مع الطلب نفسه يعيد المهمة الأصلية دون خصم جديد. أما المفتاح نفسه مع طلب مختلف فيرد بـ 409idempotency_key_reused. - تُحذف النتائج بعد 48 ساعة، فاحفظها فور استلامها.
قواعد إعادة المحاولة التي تمنعك من الدفع مرتين
| الاستجابة | ماذا تفعل |
|---|---|
| 200 | احفظ الصورة |
| 202 | استعلم عن نتيجة المهمة، ولا ترفع الصورة مجددًا |
| 400, 401, 402, 413, 422 | أصلح الطلب أو المفتاح أو الرصيد أو الصورة، ولا تكرر في حلقة |
| 429 | انتظر عدد ثواني Retry-After، ثم أعد الإرسال بالمفتاح نفسه |
| 500, 503، انتهاء المهلة، انقطاع الاتصال | تراجع قليلًا ثم أعد الإرسال بالمفتاح نفسه |
| 502 | فشلت تلك الصورة ولم تُخصم، سجّلها |
الحيلة هي جعل مفتاح عدم التكرار حتميًا: تجزئة (hash) لبايتات الصورة مع الخيارات. إذا تعطل السكربت وأعدت تشغيله، تحمل كل إعادة إرسال المفتاح نفسه، فلا يُعالج شيء ولا يُخصم مرتين.
سكربت 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. أربعة عمّال بداية متواضعة، وإذا دفعتك الاستجابات السريعة إلى تجاوز الحد في الدقيقة، فإن معالجة 429 تنتظر وتعيد الإرسال بالمفتاح نفسه. لإرسال روابط بدل الملفات، أرسل 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),
)للقوائم الطويلة من الروابط، تتيح الواجهة نفسها POST /v1/batch الذي يضع حتى 100 رابط صورة في مهمة واحدة تستعلم عنها.
كيف تجعل أي معالجة دفعية متينة
- تخطَّ المخرجات المنجزة حتى تستأنف إعادة التشغيل بدل البدء من الصفر. السكربتان يفعلان ذلك.
- انتبه لتعارض الأسماء. الملفان
shoe.jpgوshoe.pngفي المجلد نفسه يتحولان كلاهما إلىshoe.png، فأعد تسمية المدخلات أو ضمّن الامتداد الأصلي في اسم المخرج. - سجّل الإخفاقات في ملف وأعد تشغيل تلك الملفات وحدها.
- افحص عينة بتكبير 100% على خلفيات داكنة وفاتحة قبل معالجة كل شيء.
- لا تمسّ الأصول. اكتب المخرجات في مجلد منفصل.
- اختر صيغة المخرج حسب الوجهة. PNG أو WebP للشفافية، وJPG على خلفية بيضاء للمتاجر الإلكترونية. راجع PNG مقابل JPEG مقابل WebP.
أسئلة
هل rembg مجاني للاستخدام التجاري؟
مكتبة rembg نفسها بترخيص MIT، لكن لكل نموذج ترخيصه الخاص. النموذج الافتراضي، RMBG-2.0 من BRIA (bria-rmbg)، يشترط اتفاقًا مدفوعًا للاستخدام التجاري، فراجع ترخيص النموذج الذي تستخدمه أو اختر نموذجًا آخر.
هل يستخدم rembg بطاقة GPU تلقائيًا؟
نعم، إذا ثبّتّ rembg[gpu] وتمكن ONNX Runtime من تحميل مزوّد CUDA (وهذا يحتاج إلى إصدارات متوافقة من CUDA وcuDNN). يستخدم rembg CUDA أو ROCm عند توفرهما ويعرض تحذيرًا عند الرجوع إلى CPU. تحقق عبر onnxruntime.get_available_providers().
أي نموذج في rembg يعطي أفضل جودة؟
النماذج الأحدث، bria-rmbg (الافتراضي) وعائلة birefnet وisnet-general-use، تعطي حوافًا أنعم وأدق شكلًا من عائلة u2net الأقدم. birefnet-portrait بداية جيدة لصور الأشخاص. اختبر عينة من صورك، فالنتائج تختلف حسب الموضوع.
كيف أتجنب الخصم مرتين عند إعادة محاولة طلبات API؟
أرسل الترويسة Idempotency-Key وأعد استخدامها في كل محاولة للصورة نفسها. اشتقاقها من تجزئة بايتات الصورة والخيارات يعني أن إعادة تشغيل السكربت كاملًا تستخدم المفاتيح نفسها.
ماذا يفعل السكربت مع استجابة 202؟
يعاملها على أنها ما زالت قيد المعالجة، لا على أنها صورة. اقرأ id المهمة من جسم JSON واستعلم عبر GET /v1/jobs/{id}/result، منتظرًا مدة Retry-After كلما ردّ بـ 409 not_ready. لا ترفع الصورة مجددًا.
ما سرعة إرسال الطلبات المسموحة إلى API موقع removebackground.dev؟
حتى 60 طلب معالجة في الدقيقة لكل حساب. بعد ذلك يرد الـ API بـ 429 rate_limited مع ترويسة Retry-After. ولعدد كبير من الروابط، يقبل POST /v1/batch حتى 100 رابط في الطلب.
جرّبها على صورتك
تحصل الحسابات الجديدة على 50 رصيدًا مجانيًا. رصيد واحد لكل صورة، بلا اشتراك، والرصيد لا ينتهي.
إزالة الخلفيةالمصادر
- 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)