Você tem uma pasta com algumas centenas de fotos de produtos, fotos de rosto ou imagens de anúncios, e todas precisam ter o fundo removido. Fazer isso à mão num editor é um dia inteiro de cliques. Em Python é um loop, e a decisão real é o que roda dentro do loop: um modelo de código aberto na sua própria máquina ou uma API HTTP.
Este guia monta as duas opções, com scripts completos que você pode rodar hoje numa pasta, e cobre o que torna um processamento em lote confiável: escolha do modelo, CPU ou GPU, novas tentativas, limites de taxa, idempotência e retomada depois de uma falha.
Opção A ou opção B?
| Modelo local (rembg) | API HTTP | |
|---|---|---|
| Custo | Software gratuito; você paga em hardware e tempo | Por imagem |
| Configuração | Python 3.11 a 3.13, download de modelos, CUDA opcional | Uma chave de API e requests |
| Velocidade | Depende da sua CPU ou GPU e do modelo | Depende do serviço, requisições em paralelo |
| Qualidade | Depende do modelo escolhido | Depende do serviço |
| Privacidade | As imagens nunca saem da sua máquina | As imagens são enviadas a terceiros |
| Manutenção | Você cuida de modelos, drivers e licenças | O fornecedor cuida |
| Bom para | Trabalho offline, imagens sensíveis, experimentos | Pipelines de produção, servidores sem GPU |
Muitas equipes usam as duas: um modelo local para experimentos e ferramentas internas, e uma API em produção, onde qualidade previsível e não ter servidores com GPU pesam mais.
Opção A: rembg, o caminho de código aberto
O rembg é uma biblioteca e CLI Python com licença MIT que reúne vários modelos de remoção de fundo e os executa com o ONNX Runtime. No momento em que este texto foi escrito, a versão atual é a 2.0.86, que exige Python 3.11 ou posterior e anterior ao 3.14.
Instalação
Escolha o backend que combina com o seu hardware. A versão para CPU funciona em qualquer lugar:
python -m venv .venv && source .venv/bin/activate
pip install "rembg[cpu]" # library only
pip install "rembg[cpu,cli]" # library plus the rembg commandPara GPUs NVIDIA, instale rembg[gpu] no lugar. O projeto avisa que isso exige onnxruntime-gpu mais versões compatíveis de CUDA e cuDNN, e sugere voltar para a versão de CPU se você não conseguir instalá-los. GPUs AMD no Linux usam rembg[rocm] com onnxruntime-rocm.
O rembg escolhe automaticamente o provedor de execução CUDA (ou ROCm) quando o ONNX Runtime informa que ele está disponível, e avisa se voltar para a CPU. Para ver o que o ONNX Runtime enxerga:
import onnxruntime
print(onnxruntime.get_available_providers()) # look for CUDAExecutionProviderEscolha um modelo
Os modelos são baixados no primeiro uso para ~/.rembg/models/ (defina REMBG_HOME para mudar o local). Alguns úteis da lista do projeto:
| Modelo | Observações |
|---|---|
bria-rmbg | O padrão. RMBG-2.0 da BRIA, cerca de 1,02 GB, roda em 1024 x 1024. A licença exige um acordo pago para uso comercial. |
birefnet-general | Modelo BiRefNet de uso geral; boas bordas suaves |
birefnet-general-lite | BiRefNet mais leve para uso geral |
birefnet-portrait | Ajustado para pessoas |
isnet-general-use | Modelo IS-Net de uso geral |
u2net | O modelo geral clássico; mais rápido, bordas mais grosseiras |
u2netp | u2net leve; testes rápidos em máquinas lentas |
silueta | Igual ao u2net, reduzido para 43 MB |
Dois pontos importam mais do que parecem. Primeiro, o modelo padrão não é gratuito para uso comercial: os pesos dos modelos têm licenças próprias, separadas da licença MIT do rembg, então confira a origem do modelo que você for usar em produção. Segundo, os modelos mais novos (bria-rmbg, birefnet-*, isnet-general-use) geram bordas suaves e bem definidas, enquanto a família u2net, mais antiga, tende a dar bordas mais duras e grosseiras.
Opções de qualidade de borda
O rembg oferece três formas de tratar pixels de borda suaves, como cabelo e pelo:
- Padrão: bom para objetos de borda nítida, como produtos, carros e logotipos.
decontaminate=True(-dcna CLI): remove o halo colorido que o fundo antigo deixa nos pixels da borda. Muda só a cor, nunca o formato da máscara, custa muito pouco e pode ficar ligado sem risco num lote inteiro.alpha_matting=True(-a): recalcula a própria borda quando o formato da máscara está errado, por exemplo com mechas cortadas. É bem mais lento e pode não convergir; nesse caso, o rembg volta para um recorte descontaminado.
Para lotes sem supervisão, o projeto recomenda -dc em vez de -a, porque é mais rápido e não falha. O guia sobre canal alfa explica por que os halos aparecem.
O jeito rápido: a CLI
rembg p -m birefnet-general -dc photos/ cutouts/rembg p processa uma pasta inteira; adicione -w para monitorar a pasta e processar arquivos novos conforme aparecem.
O script Python
Esta versão carrega o modelo uma vez, pula arquivos já processados para você poder parar e reiniciar, e continua quando um arquivo falha:
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:])Rode com python batch_rembg.py photos cutouts birefnet-general. Criar a sessão uma vez só faz diferença: carregar um modelo demora muito mais do que executá-lo, e o README do rembg recomenda reutilizar a sessão para ter desempenho em lote. As saídas são sempre PNG porque o resultado tem canal alfa.
Onde o rembg deixa a desejar
- A primeira execução é lenta enquanto o modelo é baixado, e modelos grandes precisam de bastante RAM ou VRAM.
- O desempenho em CPU é modesto com os modelos de alta qualidade. Teste com 20 imagens representativas antes de partir para mil.
- Licenças e drivers são por sua conta. Fixe as versões do rembg e do modelo para que uma atualização futura não mude os resultados sem você perceber.
Opção B: uma API HTTP
Com uma API, o trabalho pesado acontece nas GPUs de outra pessoa, e o seu script cuida principalmente de lidar bem com a rede. O exemplo usa a API do removebackground.dev porque o contrato dela está publicado em OpenAPI 3.1 em /openapi.json; os mesmos padrões valem para qualquer serviço parecido.
O contrato
POST https://removebackground.dev/v1/removecom sua chave no cabeçalhox-api-key.- Envie a imagem como campo multipart
image_file(JPG, PNG ou WebP, até 25 MB), ou como JSON comimage_url. - Os campos opcionais incluem
format(png,jpgouwebp),bg_color,size(preview,medium,hd,full) ecrop. - 200 significa que o corpo é a imagem pronta. 202 significa que ainda está processando depois de cerca de 55 segundos: o corpo é um JSON com o
iddo job, e você consultaGET /v1/jobs/{id}/result, que responde 409not_readycomRetry-Afteraté a imagem ficar pronta. Nunca salve o corpo de um 202 como imagem. - Até 60 requisições de processamento por minuto por conta; acima disso você recebe 429
rate_limitedcomRetry-After. - Um cabeçalho
Idempotency-Keytorna as novas tentativas seguras: a mesma chave com a mesma requisição devolve o job original em vez de cobrar de novo. A mesma chave com uma requisição diferente responde 409idempotency_key_reused. - Os resultados são apagados após 48 horas, então salve assim que receber.
Regras de nova tentativa para não pagar duas vezes
| Resposta | O que fazer |
|---|---|
| 200 | Salve a imagem |
| 202 | Consulte o resultado do job; não envie de novo |
| 400, 401, 402, 413, 422 | Corrija a requisição, a chave, os créditos ou a imagem; não fique em loop |
| 429 | Espere Retry-After segundos e reenvie com a mesma chave |
| 500, 503, timeout, conexão caída | Espere cada vez mais e reenvie com a mesma chave |
| 502 | Essa imagem falhou e não foi cobrada; registre no log |
O truque é deixar a chave de idempotência determinística: um hash dos bytes da imagem mais as opções. Se o script cair e você rodar de novo, todo reenvio leva a mesma chave, então nada é processado nem cobrado duas vezes.
O script 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])Rode com REMOVEBG_API_KEY=... python batch_api.py photos cutouts. Quatro workers é um começo modesto; se respostas rápidas fizerem você passar do limite por minuto, o tratamento do 429 espera e reenvia com a mesma chave. Para enviar URLs em vez de arquivos, envie 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),
)Para listas longas de URLs, a mesma API tem POST /v1/batch, que coloca na fila até 100 URLs de imagens como um único job que você consulta.
Como deixar qualquer processamento em lote robusto
- Pule as saídas já prontas para que uma nova execução continue de onde parou em vez de recomeçar. Os dois scripts fazem isso.
- Cuidado com nomes repetidos.
shoe.jpgeshoe.pngna mesma pasta viram os doisshoe.png; renomeie as entradas ou inclua a extensão original no nome de saída. - Registre as falhas num arquivo e rode de novo só essas.
- Confira uma amostra com zoom de 100% sobre fundos escuros e claros antes de processar tudo.
- Não mexa nos originais. Grave numa pasta de saída separada.
- Escolha o formato de saída pelo destino. PNG ou WebP para transparência, JPG no branco para marketplaces. Veja PNG vs JPEG vs WebP.
Perguntas
O rembg é gratuito para uso comercial?
O rembg em si tem licença MIT, mas cada modelo tem sua própria licença. O modelo padrão, RMBG-2.0 da BRIA (bria-rmbg), exige um acordo pago para uso comercial, então confira a licença do modelo que você usa ou escolha outro.
O rembg usa minha GPU automaticamente?
Sim, se você instalar rembg[gpu] e o ONNX Runtime conseguir carregar o provedor CUDA (o que exige versões compatíveis de CUDA e cuDNN). O rembg usa CUDA ou ROCm quando disponíveis e avisa quando volta para a CPU. Confira com onnxruntime.get_available_providers().
Qual modelo do rembg dá a melhor qualidade?
Os modelos mais novos, bria-rmbg (o padrão), a família birefnet e o isnet-general-use, geram bordas mais suaves e mais bem definidas que a família u2net, mais antiga. O birefnet-portrait é um bom começo para pessoas. Teste com uma amostra das suas próprias imagens, porque os resultados variam conforme o objeto.
Como evito ser cobrado duas vezes ao repetir chamadas à API?
Envie um cabeçalho Idempotency-Key e reutilize a mesma chave em toda nova tentativa da mesma imagem. Gerando a chave a partir de um hash dos bytes da imagem e das opções, até uma nova execução do script inteiro reutiliza as mesmas chaves.
O que meu script deve fazer com uma resposta 202?
Tratar como algo ainda em processamento, não como uma imagem. Leia o id do job no corpo JSON e consulte GET /v1/jobs/{id}/result, esperando o tempo de Retry-After sempre que a resposta for 409 not_ready. Não envie a imagem de novo.
Com que frequência posso enviar requisições à API do removebackground.dev?
Até 60 requisições de processamento por minuto por conta. Acima disso, a API responde 429 rate_limited com um cabeçalho Retry-After. Para muitas URLs, POST /v1/batch aceita até 100 por requisição.
Experimente com a sua imagem
Contas novas ganham 50 créditos grátis. Um crédito por imagem, sem assinatura, e os créditos nunca expiram.
Remover um fundoFontes
- 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)