Пакетное удаление фона на Python: rembg или API

Два полных скрипта для удаления фона со всей папки изображений: один с открытой библиотекой rembg на вашем железе, другой с HTTP API, который правильно обрабатывает повторы, лимиты запросов и двойные списания.

Обновлено 23 октября 2026 г. · 8 мин чтения

У вас есть папка с несколькими сотнями фото товаров, портретов или снимков для объявлений, и с каждого нужно удалить фон. Вручную в редакторе на это уйдёт день кликов. В Python хватит цикла, и настоящий выбор в том, что работает внутри цикла: открытая модель на вашей машине или HTTP API.

В этом руководстве мы соберём оба варианта, с полными скриптами, которые можно запустить на папке уже сегодня, и разберём то, от чего зависит надёжность пакетной обработки: выбор модели, CPU или GPU, повторные попытки, лимиты запросов, идемпотентность и продолжение работы после сбоя.

Вариант A или вариант B?

Локальная модель (rembg)HTTP API
СтоимостьБесплатный софт; платите железом и временемЗа каждое изображение
НастройкаPython от 3.11 до 3.13, загрузка моделей, по желанию CUDAAPI-ключ и requests
СкоростьЗависит от CPU или GPU и моделиЗависит от сервиса, параллельные запросы
КачествоЗависит от выбранной моделиЗависит от сервиса
КонфиденциальностьИзображения не покидают вашу машинуИзображения загружаются третьей стороне
ОбслуживаниеМодели, драйверы и лицензии на васНа стороне провайдера
Подходит дляОфлайн-работы, чувствительных изображений, экспериментовПродакшн-конвейеров, серверов без GPU

Многие команды используют оба: локальную модель для экспериментов и внутренних инструментов, API в продакшне, где предсказуемое качество и отсутствие GPU-серверов важнее.

Вариант A: rembg, путь open source

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, если их не удаётся установить. Видеокарты 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 в CLI): убирает цветной ореол, который старый фон оставляет в краевых пикселях. Меняет только цвет, никогда не форму маски, почти ничего не стоит и его можно смело оставлять включённым для всего пакета.
  • alpha_matting=True (-a): заново оценивает сам край, когда форма маски неверна, например обрезаны пряди. Работает гораздо медленнее и может не сойтись, и тогда rembg откатывается к вырезке с очисткой краёв.

Для пакетов без присмотра проект рекомендует -dc, а не -a, потому что он быстрее и не может завершиться ошибкой. Почему появляются ореолы, объясняет руководство по альфа-каналу.

Быстрый способ: 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. Важно создать сессию один раз: загрузка модели занимает гораздо больше времени, чем её запуск, и 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, который отвечает 409 not_ready с Retry-After, пока изображение не готово. Никогда не сохраняйте тело ответа 202 как изображение.
  • До 60 запросов на обработку в минуту на аккаунт; сверх этого вы получите 429 rate_limited с Retry-After.
  • Заголовок Idempotency-Key делает повторы безопасными: тот же ключ с тем же запросом возвращает исходную задачу, а не списывает кредиты снова. Тот же ключ с другим запросом даёт 409 idempotency_key_reused.
  • Результаты удаляются через 48 часов, поэтому сохраняйте их сразу.

Правила повторов, чтобы не платить дважды

ОтветЧто делать
200Сохранить изображение
202Опрашивать результат задачи; не загружать повторно
400, 401, 402, 413, 422Исправить запрос, ключ, кредиты или изображение; не повторять в цикле
429Подождать Retry-After секунд, отправить снова с тем же ключом
500, 503, таймаут, обрыв соединенияВыждать паузу и отправить снова с тем же ключом
502Это изображение не обработалось, и кредиты не списаны; запишите в лог

Хитрость в том, чтобы ключ идемпотентности был детерминированным: хеш байтов изображения плюс параметры. Если скрипт упадёт и вы запустите его снова, каждая повторная отправка несёт тот же ключ, и ничего не обрабатывается и не оплачивается дважды.

Скрипт на 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 подождёт и отправит запрос снова с тем же ключом. Чтобы отправлять URL вместо файлов, передайте 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, который ставит в очередь до 100 URL изображений как одну задачу, которую вы опрашиваете.

Как сделать любой пакетный процесс надёжным

  • Пропускайте готовые результаты, чтобы повторный запуск продолжал работу, а не начинал заново. Оба скрипта так делают.
  • Следите за совпадением имён. shoe.jpg и shoe.png в одной папке оба превращаются в shoe.png; переименуйте исходники или включите исходное расширение в имя результата.
  • Пишите ошибки в файл и перезапускайте только их.
  • Выборочно проверьте результат при 100% масштабе на тёмном и светлом фоне, прежде чем обрабатывать всё.
  • Не трогайте оригиналы. Пишите результаты в отдельную папку.
  • Выбирайте формат под место назначения. PNG или WebP для прозрачности, JPG на белом для маркетплейсов. См. PNG, JPEG или WebP.

Вопросы

Можно ли бесплатно использовать rembg в коммерческих целях?

Сам rembg под лицензией MIT, но у каждой модели своя лицензия. Модель по умолчанию, RMBG-2.0 от BRIA (bria-rmbg), требует платного соглашения для коммерческого использования, поэтому проверьте лицензию своей модели или выберите другую.

rembg сам использует мою видеокарту?

Да, если установить 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. Для множества URL POST /v1/batch принимает до 100 за запрос.

Попробуйте на своём изображении

Новые аккаунты получают 50 бесплатных кредитов. Один кредит за изображение, без подписки, кредиты не сгорают.

Удалить фон

Источники