HiAPI
Блог

Руководство

Удаление фона изображения через API: редактирование по текстовому запросу

Как заменить или удалить фон изображения с помощью моделей image-to-image на hiapi — минимум кода, пакетный режим и детали schema, которые обеспечивают безошибочную композицию продуктовых фотографий.

Aug 6, 2026
Удаление фона изображения через API: редактирование по текстовому запросу

Традиционные инструменты удаления фона предлагают только одно действие: вырезать объект, оставив прозрачную область (или сплошную заливку). А удаление фона по текстовому запросу с помощью моделей image-to-image позволяет за один вызов получить одновременно и результат вырезания, и заменённый фон — бесшовный белый студийный фон, сцену из жизни, сплошной брендовый цвет — потому что модель попиксельно перегенерирует фон вокруг сохранённого объекта, а не просто вычисляет альфа-маску.

Это руководство покажет, как именно это сделать на hiapi: какие модели image-to-image действительно хорошо справляются с удалением фона, структуру запроса/ответа, под которую нужно писать код, и производственные паттерны (колбэки, повторные попытки, обработка ошибок), которые не позволят пакетным задачам незаметно испортить ваш продуктовый каталог.

Предварительные требования:

  • Получите ключ hiapi API из вашей панели управления
  • Python 3.9+ с установленным requests (pip install requests)
  • Ваши исходные изображения размещены на публичном URL — task API hiapi получает изображения по URL, а не через загрузку файлов, поэтому локальные пути к файлам не подходят. Подойдёт любое объектное хранилище или CDN.

Как удаление фона работает как задача image-to-image

Каждая генерация на hiapi — изображение, видео или аудио — проходит один и тот же асинхронный жизненный цикл задачи: вы отправляете задачу, получаете taskId, а затем либо опрашиваете результат, либо получаете его через callback URL. Полный контракт описан в справочнике по асинхронному API задач; для удаления фона важны следующие части:

  1. POST /v1/tasks с model + input (ваш промпт, URL исходного изображения и форма вывода) возвращает {"code":200,"data":{"taskId":"tk-hiapi-..."}}. Поля status на этом этапе ещё нет.
  2. GET /v1/tasks/{taskId} возвращает data.status со значением одно из queued, handling, archiving, success, fail. Только success и fail являются терминальными.
  3. Когда статус success, data.output — это массив объектов {"url","type","expireAt"}. url — это подписанная ссылка с коротким сроком действия (expireAt — это Unix-временная метка) — скачайте её немедленно, не сохраняйте эту горячую ссылку.

Что касается модели, используйте эндпоинт image-to-image и описывайте в промпте заменённый фон, а не говорите изолированно «удалить фон» — эти модели гораздо лучше реагируют на конкретную цель, чем на отрицательные инструкции:

Remove the background completely and replace it with a seamless, pure white
studio background. Keep the subject's shape, edges, lighting, and colors
exactly unchanged. Do not add shadows, reflections, or any other objects
that were not in the original image.

Минимальный пример: одно изображение на входе, замена фона

seedream-5.0-lite/image-to-image — самый дешёвый и достаточный вариант для этой цели — он принимает prompt, image_urls, aspect_ratio и resolution (поддерживается только 2K или 4K; у этого эндпоинта нет уровня 1K, а неизвестные поля вроде n или seed отклоняются с кодом 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 = (
    "Remove the background completely and replace it with a seamless, pure "
    "white studio background. Keep the subject's shape, edges, lighting, and "
    "colors exactly unchanged. Do not add shadows, reflections, or any other "
    "objects that were not in the original image."
)

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)

Это весь цикл: отправить, опросить, скачать. Если перед подключением к конвейеру вы хотите быстро проверить без кода, AI 产品照片实验室 выполняет тот же тип замены фона прямо в браузере.

Пакетная обработка продуктового каталога

Реальные продуктовые каталоги содержат десятки или сотни изображений, а у task API нет нативного пакетного эндпоинта — вам нужно самостоятельно развернуть отправку отдельных задач и опрашивать их параллельно:

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}")

Держите max_workers умеренным — слишком частый опрос на множестве параллельных задач лишь расходует вашу квоту запросов и не ускоряет генерацию, поскольку время выполнения каждой задачи в основном определяется выводом модели, а не частотой вашего опроса.

Подготовка к продакшену: точная композиция, колбэки, идемпотентность, ошибки

Сохраняйте точную композицию продукта. Для работы с каталогом вы обычно не можете допустить, чтобы модель изменила композицию или слегка обрезала объект — центрированный ботинок на входе должен оставаться центрированным на выходе. seedream-5.0-pro/image-to-image поддерживает значение aspect_ratio, которого нет у уровня lite: match_input_image, которое фиксирует выходной холст под собственные размеры входного изображения, а не принудительно задаёт фиксированный соотношение. Он также принимает до 10 image_urls за вызов и ограничивает resolution значениями 1K/2K (отличный от lite набор разрешений — не предполагайте, что schema всех эндпоинтов seedream одинаковы, у каждого эндпоинта свои перечисления). Для задач чистого удаления и замены фона, где композиция должна точно совпадать, это тот уровень, который следует выбирать, хотя стоимость за изображение выше — актуальные тарифы см. в прайс-листе.

Если вам нужно ссылаться на несколько исходных ракурсов за раз (например, вид спереди и вид сбоку одного продукта), а не обрабатывать одно изображение за вызов, nano-banana-2 — разумная альтернатива — он принимает референсные изображения через image_input (а не image_urls), а resolution может быть 1K/2K/4K.

Используйте колбэки вместо опроса. Для любой пакетной задачи реального масштаба webhook предпочтительнее опроса: добавьте объект callback на том же уровне, что и input — не вкладывайте его внутрь, POST /v1/tasks отклонит такую структуру:

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)

После достижения задачей терминального состояния hiapi отправит POST на ваш callback URL с тем же объектом data, который вернул бы GET /v1/tasks/{taskId}. Если вы хотите проверять подлинность колбэка на сервере, обратитесь к опциональному HMAC-заголовку X-HiAPI-Signature в справочнике по асинхронным задачам.

Идемпотентность. У POST /v1/tasks нет документированного поля idempotency_key — реализуйте идемпотентность на уровне приложения. Используйте небольшое хранилище (строка в БД, ключ Redis или даже локальный файл для одноразового скрипта) с ключом по хешу (image_url, prompt, model) и перед отправкой новой задачи проверяйте, есть ли для этого ключа сохранённый taskId или URL вывода. Если ваша пакетная задача упадёт на полпути и перезапустится, вы сможете пропустить повторную оплату за уже обработанные изображения, а не незаметно увеличить расходы.

Обработка ошибок — три разные формы, не относитесь к ним одинаково:

  • Валидация запроса (ошибочные входные данные при создании) — HTTP 400, плоская структура: {"code":400,"error_code":"INVALID_REQUEST","message":"...","data":null}. Это означает, что ваш словарь input не соответствует schema модели — проверьте опечатки в именах полей или неверные значения перечислений.
  • Ошибка аутентификации — HTTP 401, вложенность другая: {"error":{"code":"permission_denied","message":"...","request_id":"...","type":"hiapi_error"}}. Ключ API отсутствует/просрочен, а не ошибка запроса — не повторяйте попытку, исправьте ключ.
  • Ошибка уровня задачи (запрос действителен, но сама генерация не удалась) — проявляется при опросе: data.status == "fail", data.error описывает причину. Это можно повторить; временные ошибки вышестоящего сервиса могут завершиться успешно при повторной отправке.
if task["status"] == "fail":
    err = task.get("error") or {}
    # log err["code"] / err["message"], then decide: retry, skip, or alert

Не объединяйте все три в один except Exception — повторная попытка при 401 в цикле лишь израсходует ваш лимит запросов и никогда не завершится успешно, а задача уровня fail действительно может успешно завершиться со второй попытки.

Следующие шаги

Часто задаваемые вопросы

Это действительно удаляет фон или просто перекрывает его? Функционально и то и другое — модель перегенерирует весь холст вокруг сохранённого объекта, поэтому не остаются остаточные маски или рваные края, как у инструментов вырезания на основе обнаружения краёв. Цена этого — необходимость явно указывать в промпте «сохранить всё остальное без изменений», иначе модель с радостью перестилизует и сам объект.

Могу ли я получить прозрачный PNG вместо сплошного фона? Укажите это прямо в промпте (например, «заменить фон на прозрачный» или «изолировать объект на прозрачном фоне») и запросите вывод в PNG там, где модель поддерживает поле output_format — поддержка зависит от модели, поэтому перед предположением о доступности проверьте страницу конкретной модели.

Почему моё изображение возвращается с другой композицией, а не совпадает с исходным? Скорее всего, вы используете уровень, в перечислении aspect_ratio которого нет match_input_image — на уровнях lite/4.5 вы выбираете фиксированное соотношение, которое может обрезать или добавить чёрные полосы относительно исходника. Переключитесь на seedream-5.0-pro/image-to-image и установите aspect_ratio в match_input_image.

Сколько стоит одно изображение? Цены зависят от модели и уровня разрешения и меняются со временем — смотрите прайс-лист, а не хардкодьте цифры в своём коде или документации.

Что делать, если URL моих изображений ещё не публичны? Task API получает изображения по URL на стороне сервера, поэтому они должны быть доступны без аутентификации. Сначала загрузите их в любой публичный бакет или путь CDN — подписанные/приватные URL или пути localhost приведут к ошибке при создании задачи или (что хуже) молчаливому сбою в процессе обработки.

Похожие статьи

Все статьи →

Начните работать с постоянными артефактами ИИ

Получите API-ключ и вызывайте модели изображений, видео и аудио через один API на основе tasks.

Читать документацию API