HiAPI
Blogue

Tutorial

Remover o fundo da imagem com a API: edição baseada em prompt

Como substituir ou remover o fundo de imagens usando modelos de imagem para imagem no hiapi — mínimo de código, modo em lote e detalhes de schema que tornam a composição de fotos de produto precisa.

Aug 6, 2026
Remover o fundo da imagem com a API: edição baseada em prompt

Ferramentas tradicionais de remoção de fundo oferecem apenas uma operação: recortar o sujeito, deixando uma área transparente (ou preenchimento sólido). Já a remoção de fundo baseada em prompt com modelos de imagem para imagem permite obter, em uma única chamada, tanto o recorte quanto o fundo substituído — um fundo de estúdio branco sem emendas, uma cena de estilo de vida, uma cor de marca sólida — porque o modelo regenera pixel a pixel o fundo ao redor do sujeito preservado, em vez de apenas calcular uma máscara alfa.

Este guia mostrará exatamente como fazer isso no hiapi: quais modelos de imagem para imagem realmente se destacam na remoção de fundo, a estrutura de requisição/resposta na qual você precisa codificar e os padrões de produção (callbacks, retentativas, tratamento de erros) que evitam que tarefas em lote contaminem silenciosamente seu catálogo de produtos.

Pré-requisitos:

  • Obter uma chave de API hiapi no seu painel
  • Python 3.9+ com requests instalado (pip install requests)
  • Sua imagem de origem hospedada em uma URL pública — a API de tarefas do hiapi busca imagens por URL, não por upload de arquivo, portanto caminhos locais não funcionam. Qualquer armazenamento de objetos ou CDN serve.

Como a remoção de fundo funciona como tarefa de imagem para imagem

Toda geração no hiapi — imagem, vídeo ou áudio — segue o mesmo ciclo de vida assíncrono de tarefa: você envia uma tarefa, obtém um taskId e então faz polling do resultado ou o recebe via URL de callback. O contrato completo está na referência da API de tarefas assíncronas; para remoção de fundo, as partes importantes são:

  1. POST /v1/tasks com model + input (seu prompt, URL da imagem de origem e formato de saída) retorna {"code":200,"data":{"taskId":"tk-hiapi-..."}}. Neste momento ainda não há campo status.
  2. GET /v1/tasks/{taskId} retorna data.status, com valor entre queued, handling, archiving, success, fail. Apenas success e fail são estados terminais.
  3. Quando o status é success, data.output é um array de {"url","type","expireAt"}. url é um link assinado de curta duração (expireAt é um timestamp Unix) — baixe imediatamente, não armazene esse hotlink.

Quanto ao modelo, use o endpoint de imagem para imagem e faça o prompt descrever o fundo substituído, em vez de dizer isoladamente "remover o fundo" — esses modelos respondem muito melhor a alvos concretos do que a instruções negativas:

Remova o fundo completamente e substitua-o por um fundo de estúdio
branco puro e sem emendas. Mantenha a forma, as bordas, a iluminação e as cores
do sujeito exatamente inalteradas. Não adicione sombras, reflexos ou quaisquer outros objetos
que não estavam na imagem original.

Exemplo mínimo: inserir uma imagem, substituir o fundo

seedream-5.0-lite/image-to-image é a opção mais barata e suficiente para esse uso — aceita prompt, image_urls, aspect_ratio e resolution (apenas 2K ou 4K; esse endpoint não tem faixa 1K, e campos desconhecidos como n ou seed são rejeitados diretamente com 400).

import os
import time
import requests

API = "https://api.hiapi.ai/v1"
HEADERS = {
    "Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}",
    "Content-Type": "application/json",
}

BACKGROUND_PROMPT = (
    "Remova o fundo completamente e substitua-o por um fundo de estúdio "
    "branco puro e sem emendas. Mantenha a forma, as bordas, a iluminação e as "
    "cores do sujeito exatamente inalteradas. Não adicione sombras, reflexos ou quaisquer outros "
    "objetos que não estavam na imagem original."
)

def create_task(model: str, task_input: dict) -> str:
    resp = requests.post(f"{API}/tasks", headers=HEADERS,
                          json={"model": model, "input": task_input}, timeout=60)
    resp.raise_for_status()
    return resp.json()["data"]["taskId"]

def wait_task(task_id: str, timeout_s: int = 180, poll_s: int = 3) -> dict:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        resp = requests.get(f"{API}/tasks/{task_id}", headers=HEADERS, timeout=30)
        resp.raise_for_status()
        task = resp.json()["data"]
        if task["status"] == "success":
            return task
        if task["status"] == "fail":
            err = task.get("error") or {}
            raise RuntimeError(f"task {task_id} failed: {err.get('code')} {err.get('message')}")
        time.sleep(poll_s)
    raise TimeoutError(f"task {task_id} still {task['status']} after {timeout_s}s")

task_id = create_task("seedream-5.0-lite/image-to-image", {
    "prompt": BACKGROUND_PROMPT,
    "image_urls": ["https://your-cdn.example.com/product-photo.jpg"],
    "aspect_ratio": "1:1",
    "resolution": "2K",
})
task = wait_task(task_id)
image_bytes = requests.get(task["output"][0]["url"], timeout=60).content

with open("product-no-bg.png", "wb") as f:
    f.write(image_bytes)

Esse é o ciclo completo: enviar, fazer polling, baixar. Para uma verificação rápida sem código antes de integrar ao pipeline, o Laboratório de Fotos de Produto com IA executa o mesmo tipo de substituição de fundo no navegador.

Processar catálogos de produtos em lote

Catálogos reais têm dezenas ou centenas de imagens, e a API de tarefas não tem endpoint nativo de lote — você mesmo precisa expandir os envios de tarefas individuais e fazer polling delas em paralelo:

from concurrent.futures import ThreadPoolExecutor, as_completed

def remove_background(image_url: str, aspect_ratio: str = "1:1") -> str:
    task_id = create_task("seedream-5.0-lite/image-to-image", {
        "prompt": BACKGROUND_PROMPT,
        "image_urls": [image_url],
        "aspect_ratio": aspect_ratio,
        "resolution": "2K",
    })
    task = wait_task(task_id)
    return task["output"][0]["url"]

source_urls = [
    "https://your-cdn.example.com/sku-1001.jpg",
    "https://your-cdn.example.com/sku-1002.jpg",
    # ...
]

results = {}
with ThreadPoolExecutor(max_workers=5) as pool:
    futures = {pool.submit(remove_background, url): url for url in source_urls}
    for future in as_completed(futures):
        url = futures[future]
        try:
            results[url] = future.result()
        except Exception as exc:
            results[url] = None
            print(f"failed: {url}: {exc}")

Mantenha max_workers moderado — fazer polling com muita frequência em muitas tarefas concorrentes apenas consome sua cota de requisições e não acelera a geração, pois o tempo de parede de cada tarefa é determinado principalmente pela inferência do modelo, não pela frequência do seu polling.

Reforço para produção: composição precisa, callbacks, idempotência, erros

Preserve a composição exata do produto. Para trabalho de catálogo, você geralmente não pode tolerar que o modelo recomponha ou corte levemente o sujeito — um sapato centralizado na entrada precisa permanecer centralizado na saída. seedream-5.0-pro/image-to-image suporta um valor de aspect_ratio que a faixa lite não suporta: match_input_image, que trava a tela de saída nas dimensões da própria imagem de entrada, em vez de forçar uma proporção fixa. Ele também aceita até 10 image_urls por chamada, e limita resolution a 1K/2K (conjunto de resoluções diferente do lite — não presuma que os schemas dos endpoints seedream sejam iguais, cada endpoint tem seus próprios enums). Para trabalho puro de remoção e substituição de fundo em que a composição precisa corresponder exatamente, essa é a faixa a escolher, com custo maior por imagem — consulte os preços para as tarifas atuais.

Se você precisa referenciar vários ângulos de origem em uma única chamada (por exemplo, vista frontal e vista lateral do mesmo produto), em vez de processar uma imagem por chamada, nano-banana-2 é uma alternativa razoável — ele aceita imagens de referência via image_input (não image_urls), e resolution pode ser 1K/2K/4K.

Use callbacks em vez de polling. Para qualquer tarefa em lote em escala real, webhooks são melhores que polling: adicione um objeto callback como irmão de input — não aninhado dentro dele, POST /v1/tasks rejeita essa estrutura:

task_input = {
    "prompt": BACKGROUND_PROMPT,
    "image_urls": [image_url],
    "aspect_ratio": "1:1",
    "resolution": "2K",
}
resp = requests.post(f"{API}/tasks", headers=HEADERS, json={
    "model": "seedream-5.0-lite/image-to-image",
    "input": task_input,
    "callback": {"url": "https://your-service.example.com/hooks/hiapi", "when": "final"},
}, timeout=60)

Quando a tarefa atinge um estado terminal, o hiapi envia por POST para sua URL de callback o mesmo objeto data que GET /v1/tasks/{taskId} retornaria. Se quiser validar a autenticidade do callback no servidor, consulte o cabeçalho HMAC opcional X-HiAPI-Signature na referência de tarefas assíncronas.

Idempotência. Não há campo idempotency_key documentado em POST /v1/tasks — implemente a idempotência na camada da aplicação. Use um armazenamento pequeno (linha de banco de dados, chave Redis, ou até arquivo local para scripts únicos), com chave baseada no hash de (image_url, prompt, model), e antes de enviar uma nova tarefa verifique se essa chave já tem um taskId ou URL de saída armazenado. Se sua tarefa em lote falhar no meio e for reexecutada, você evita a cobrança duplicada de imagens já processadas, em vez de aumentar custos silenciosamente.

Tratamento de erros — três formas distintas, não trate igual:

  • Validação da requisição (entrada inválida na criação) — HTTP 400, estrutura plana: {"code":400,"error_code":"INVALID_REQUEST","message":"...","data":null}. Isso significa que seu dicionário input não corresponde ao schema do modelo — verifique se há nome de campo digitado errado ou valor de enum incorreto.
  • Falha de autenticação — HTTP 401, aninhamento diferente: {"error":{"code":"permission_denied","message":"...","request_id":"...","type":"hiapi_error"}}. Chave de API ausente/expirada, não erro de requisição — não faça retentativa, corrija a chave.
  • Falha em nível de tarefa (requisição válida, mas a geração em si falha) — manifesta-se durante o polling: data.status == "fail", data.error descreve o motivo. Isso é retentável; erros transitórios do upstream podem ter sucesso ao reenviar.
if task["status"] == "fail":
    err = task.get("error") or {}
    # registre err["code"] / err["message"], depois decida: retentar, pular ou alertar

Não agrupe os três em um único except Exception — um 401 retentado em loop só consome seu limite de taxa e nunca terá sucesso, enquanto um fail em nível de tarefa pode realmente ter sucesso na segunda tentativa.

Próximos passos

Perguntas frequentes

Isso realmente remove o fundo ou apenas o cobre? Funcionalmente, ambos — o modelo regenera toda a tela ao redor do sujeito preservado, portanto não deixa máscara residual ou bordas irregulares como ferramentas de recorte por detecção de bordas. O custo é que você precisa instruir explicitamente no prompt "mantenha todo o resto inalterado", caso contrário o modelo pode reestilizar alegremente até o sujeito.

Posso obter um PNG transparente em vez de um fundo de cor sólida? Peça diretamente no prompt (por exemplo, "substitua o fundo por transparente" ou "isole o sujeito em um fundo transparente") e solicite saída PNG onde o modelo suportar o campo output_format — o suporte varia por modelo, então verifique a página do modelo específico antes de assumir que está disponível.

Por que minha imagem volta recomposta em vez de corresponder à original? Você provavelmente está usando uma faixa cujo enum de aspect_ratio não inclui match_input_image — nas faixas lite/4.5, você escolhe uma proporção fixa, que pode cortar ou adicionar barras em relação à origem. Mude para seedream-5.0-pro/image-to-image e defina aspect_ratio como match_input_image.

Qual é o custo por imagem? O preço varia por modelo e faixa de resolução, e muda com o tempo — consulte os preços em vez de codificar números no seu código ou documentação.

E se minha URL de imagem ainda não for pública? A API de tarefas busca imagens por URL no servidor, portanto ela precisa ser acessível sem autenticação. Faça upload primeiro para qualquer bucket público ou caminho de CDN — URLs assinadas/privadas ou caminhos localhost falharão na criação da tarefa, ou (pior) falharão silenciosamente durante o processamento.

Artigos relacionados

Ver todos os artigos →

Comece a criar com artefactos de IA persistentes

Obtenha a sua chave de API e chame modelos de imagem, vídeo e áudio através de uma única API baseada em tasks.

Ler a documentação da API