> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apimart.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Слои и редактирование областей Grok Imagine 2.0 Ext

> Получайте слои объектов и точные маски через segment, затем редактируйте полигоны, рамки или найденные объекты через region_edit.

<Info>
  `segment` и `region_edit` используют существующий асинхронный endpoint изображений. Сохраните полученный `task_id`, затем опрашивайте [Статус задачи](/ru/api-reference/tasks/status); запрос создания не возвращает готовые слои или изображения.
</Info>

<Warning>
  Никогда не размещайте API Key в браузерном bundle, LocalStorage, URL или frontend-логах. Вызывайте APIMart через backend или BFF.
</Warning>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.apimart.ai/v1/images/generations \
    --header 'Authorization: Bearer <token>' \
    --header 'Content-Type: application/json' \
    --header 'Accept: application/json' \
    --header 'Idempotency-Key: 6baf0940-25d6-4ec2-9131-925250840fa7' \
    --data '{
      "model": "grok-imagine-2.0-ext",
      "operation": "segment",
      "source_task_id": "<COMPLETED_SINGLE_IMAGE_TASK_ID>",
      "include_mask_rle": true,
      "cached_only": false
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "code": 200,
    "data": [{ "status": "submitted", "task_id": "task_..." }]
  }
  ```
</ResponseExample>

## Обзор операций

| Назначение                                                          | Основной ввод                   | Готовый результат                  | Оплата                       |
| ------------------------------------------------------------------- | ------------------------------- | ---------------------------------- | ---------------------------- |
| `segment`: Найти объекты и получить слои, рамки и точные маски      | `source_task_id`                | `image_id`, `image_url`, `objects` | Бесплатно                    |
| `region_edit`: Изменить полигон, прямоугольник или найденный объект | `image_id`, `prompt`, выделение | Новый URL и `image_id`             | Оплата за завершённую задачу |

```text theme={null}
task_id → segment(source_task_id) → image_id + mask_rle
        → selection_regions → region_edit → new task_id + image_id
```

<Note>
  `source_task_id` и `image_id` не взаимозаменяемы. `segment` принимает ID исходной задачи, а `region_edit` — ID изображения. Чтобы сегментировать отредактированное изображение, используйте ID завершённой задачи `region_edit` как новый `source_task_id`.
</Note>

## Заголовки запроса

Используйте `Authorization: Bearer <APIMART_API_KEY>`, `Content-Type: application/json` и `Accept: application/json`.

`Idempotency-Key` необязателен, но настоятельно рекомендуется для платных запросов `region_edit`. Поддерживается 1–191 видимый символ ASCII; рекомендуется UUID. Используйте новый ключ для каждой логической операции. При сетевом повторе того же запроса используйте исходный ключ и идентичный body. Если результат неопределён, не повторяйте автоматически с новым ключом.

## Асинхронный процесс

Успешное создание возвращает HTTP `200` и `data[0].task_id`. Опрашивайте `GET /v1/tasks/{task_id}?language=ru` с интервалом от 2 до 5 секунд и общим лимитом 10 минут. Останавливайте старый опрос при смене исходного изображения.

<Warning>
  Запрос задачи может вернуть HTTP `200`, когда `data.status` равен `failed`. Всегда определяйте результат по `data.status` и показывайте `data.error`.
</Warning>

## `segment`

### Параметры запроса

| Поле               | Тип     | Обязателен | По умолчанию | Описание                                                                |
| ------------------ | ------- | :--------: | ------------ | ----------------------------------------------------------------------- |
| `model`            | string  |      ✅     | —            | Только `grok-imagine-2.0-ext`                                           |
| `operation`        | string  |      ✅     | —            | Только `segment`                                                        |
| `source_task_id`   | string  |      ✅     | —            | Завершённая задача Grok с одним изображением текущего пользователя      |
| `include_mask_rle` | boolean |      —     | `true`       | Вернуть COCO compressed RLE; оставьте `true` для точного редактирования |
| `cache_only`       | boolean |      —     | `false`      | Проверить только кэш сегментации; не обращаться upstream при miss       |
| `cached_only`      | boolean |      —     | `false`      | Подсказка upstream-кэша, не гарантия локального попадания               |
| `refresh`          | boolean |      —     | `false`      | Обойти кэш; не использовать в обычном редакторе                         |

Для `segment` не нужен `prompt`. Не отправляйте `image_id`, `image_index`, `billing_model_name`, `n`, `size` или `response_format`. `cache_only=true` и `refresh=true` несовместимы.

### Примеры запросов

<Tabs>
  <Tab title="Получить слои">
    ```json theme={null}
    {
      "model": "grok-imagine-2.0-ext",
      "operation": "segment",
      "source_task_id": "<COMPLETED_SINGLE_IMAGE_TASK_ID>",
      "include_mask_rle": true,
      "cached_only": false
    }
    ```
  </Tab>

  <Tab title="Проверить кэш">
    ```json theme={null}
    {
      "model": "grok-imagine-2.0-ext",
      "operation": "segment",
      "source_task_id": "<COMPLETED_SINGLE_IMAGE_TASK_ID>",
      "include_mask_rle": true,
      "cache_only": true
    }
    ```
  </Tab>
</Tabs>

Промах кэша всё равно считается успешной задачей. Используйте `cache_status` или `from_cache`; не определяйте попадание по `cached`.

### Завершённый ответ

Для `segment` поле `data.result` непосредственно содержит сегментацию и не оборачивается в `images`.

```json theme={null}
{
  "code": 200,
  "data": {
    "id": "task_...",
    "status": "completed",
    "progress": 100,
    "cost": 0,
    "credits_cost": 0,
    "result": {
      "source_task_id": "task_...",
      "image_id": "6b78a7c3-5f9e-4a3d-a928-9e9b42113a3f",
      "image_url": "https://.../source.jpg",
      "from_cache": true,
      "cache_status": "hit",
      "objects": [{
        "index": 0,
        "name": "red sports car",
        "box_xyxy": [38.1, 689.8, 945.8, 1065.4],
        "score": 0.9765625,
        "mask_size": [1792, 1008],
        "mask_url": "",
        "mask_rle": { "size": [1792, 1008], "counts": "..." }
      }]
    }
  }
}
```

| Поле                  | Описание                                                   |
| --------------------- | ---------------------------------------------------------- |
| `result.image_id`     | ID изображения для `region_edit`                           |
| `result.image_url`    | HTTP(S) URL, соответствующий `image_id`                    |
| `objects[].index`     | Исходный серверный индекс; сохраняйте для `object_indices` |
| `objects[].box_xyxy`  | Рамка маски в пикселях `[x1,y1,x2,y2]`                     |
| `objects[].score`     | Уверенность распознавания; может быть `null`               |
| `objects[].mask_size` | Всегда `[height,width]`; не фиксируйте размеры             |
| `objects[].mask_rle`  | COCO compressed RLE для точного контура                    |
| `objects[].mask_url`  | Необязательный URL маски; может быть пустым                |

Объект без допустимого `mask_rle` или `mask_url` можно редактировать только приблизительно по рамке.

## Декодирование `mask_rle`

`mask_rle.counts` — строка сжатых счётчиков COCO, а не Base64 или zlib. Она разворачивается по столбцам; первый run — фон, затем чередуются объект и фон.

Следующий TypeScript преобразует её в удобную для браузера двоичную маску по строкам:

```ts theme={null}
export interface CocoRLE {
  size: [height: number, width: number];
  counts: string;
}

export interface BinaryMask {
  width: number;
  height: number;
  data: Uint8Array; // data[y * width + x]
}

function decodeCompressedCounts(counts: string): number[] {
  const runs: number[] = [];
  let cursor = 0;
  while (cursor < counts.length) {
    let value = 0;
    let shift = 0;
    let more = true;
    while (more) {
      if (cursor >= counts.length) throw new Error("Truncated COCO RLE counts");
      const current = counts.charCodeAt(cursor++) - 48;
      value |= (current & 0x1f) << shift;
      more = (current & 0x20) !== 0;
      shift += 5;
      if (!more && (current & 0x10) !== 0) value |= -1 << shift;
    }
    if (runs.length > 2) value += runs[runs.length - 2] ?? 0;
    if (value < 0) throw new Error(\`Invalid COCO RLE run: \${value}\`);
    runs.push(value);
  }
  return runs;
}

export function decodeCocoRLE(rle: CocoRLE): BinaryMask {
  const [height, width] = rle.size;
  if (!Number.isInteger(height) || !Number.isInteger(width) || height <= 0 || width <= 0) {
    throw new Error(\`Invalid mask size: \${JSON.stringify(rle.size)}\`);
  }
  const pixelCount = width * height;
  if (!Number.isSafeInteger(pixelCount) || pixelCount > 32_000_000) {
    throw new Error(\`Mask exceeds the frontend safety limit: \${pixelCount}\`);
  }
  if (!rle.counts) throw new Error("Missing COCO RLE counts");

  const data = new Uint8Array(pixelCount);
  const runs = decodeCompressedCounts(rle.counts);
  let position = 0;
  let foreground = false;
  for (const run of runs) {
    if (position + run > data.length) throw new Error("COCO RLE exceeds mask_size");
    if (foreground) {
      for (let offset = 0; offset < run; offset++) {
        const index = position + offset;
        const y = index % height;
        const x = (index - y) / height;
        data[y * width + x] = 1;
      }
    }
    position += run;
    foreground = !foreground;
  }
  if (position !== data.length) throw new Error("COCO RLE does not cover the mask");
  return { width, height, data };
}
```

Декодируйте большие маски в Web Worker. Не отправляйте полные `mask_rle.counts` в логи, аналитику, URL или отчёты об ошибках.

### Преобразование маски в точное выделение

```ts theme={null}
interface SelectionBoundary { points: number[] }
interface SelectionRegion { outer: SelectionBoundary; holes?: SelectionBoundary[] }
```

Найдите связанные области и отверстия, упростите контуры и нормализуйте каждую точку в `0–1`. В каждом кольце должно быть не менее 3 разных точек, ненулевая площадь и не должно быть самопересечений. Оставляйте до 16 крупнейших областей на слой и 400 точек на кольцо.

<Warning>
  `mask_size` имеет порядок `[height,width]` и использует координаты исходной маски, а не CSS. При `object-fit: contain` вычтите поля, масштабируйте по фактической области и ограничьте значения диапазоном `0–1`.
</Warning>

Для чтения пикселей исходного изображения или `mask_url` нужен CORS. Задайте `crossOrigin = "anonymous"` до `src` либо загрузите Blob. Прямое декодирование `mask_rle` не имеет этой зависимости.

## Редактирование области: `region_edit`

### Параметры запроса

| Поле                | Тип          | Обязателен | Описание                                                                                                                     |
| ------------------- | ------------ | :--------: | ---------------------------------------------------------------------------------------------------------------------------- |
| `model`             | string       |      ✅     | Только `grok-imagine-2.0-ext`                                                                                                |
| `operation`         | string       |      ✅     | `region_edit`                                                                                                                |
| `image_id`          | string       |      ✅     | ID исходного изображения; сначала используйте `image_id` из `segment`, затем последнее значение из результата редактирования |
| `prompt`            | string       |      ✅     | Непустая инструкция с описанием изменения                                                                                    |
| `selection_regions` | array        |     \*     | Нормализованные полигоны `0–1` с `outer` и необязательными `holes`; рекомендуется                                            |
| `boxes`             | number\[]\[] |     \*     | Прямоугольники `[x1,y1,x2,y2]`; для пиксельных рамок нужен `mask_size`                                                       |
| `object_indices`    | integer\[]   |     \*     | Исходные значения `objects[].index`; только приближённое редактирование рамки                                                |
| `mask_size`         | integer\[]   |     \*     | Обязателен для пиксельных рамок; `[height,width]` из положительных целых                                                     |

Хотя бы одно из `selection_regions`, `boxes` или `object_indices` должно быть непустым. API разрешает комбинации, но frontend должен использовать один способ на запрос.

<Warning>
  Не отправляйте `billing_model_name`, `size`, `aspect_ratio`, `source_aspect_ratio`, `source_size` или `image_urls`. Пропустите `n` или задайте `1`; пропустите `claim_asset` или задайте `false`; пропустите `response_format` или задайте `url`. Base64 и `stream=true` не поддерживаются.
</Warning>

### Способы выделения

| Способ              | Источник выделения       | Точность                 | Применение                  |
| ------------------- | ------------------------ | ------------------------ | --------------------------- |
| `selection_regions` | Полигоны frontend        | Точно, включая отверстия | Работа со слоями и кистью   |
| `boxes`             | Прямоугольники frontend  | Приближение рамкой       | Инструмент рамки или MVP    |
| `object_indices`    | Исходные индексы segment | Приближение рамкой       | Быстрая проверка интеграции |

<Tabs>
  <Tab title="Точный полигон">
    ```json theme={null}
    {
      "model": "grok-imagine-2.0-ext",
      "operation": "region_edit",
      "image_id": "<SOURCE_IMAGE_ID>",
      "prompt": "Change the selected car to bright red and preserve the rest",
      "selection_regions": [{
        "outer": { "points": [0.12, 0.20, 0.48, 0.20, 0.48, 0.61, 0.12, 0.61] },
        "holes": [{ "points": [0.30, 0.35, 0.38, 0.35, 0.38, 0.45, 0.30, 0.45] }]
      }]
    }
    ```

    `points` может быть плоским массивом или вложенными парами. Все значения должны быть конечными и находиться в `0–1`; в каждом кольце нужно не менее 3 пар.
  </Tab>

  <Tab title="Нормализованная рамка">
    ```json theme={null}
    {
      "model": "grok-imagine-2.0-ext",
      "operation": "region_edit",
      "image_id": "<SOURCE_IMAGE_ID>",
      "prompt": "Change the car inside the box to bright red",
      "boxes": [[0.04, 0.385, 0.938, 0.594]]
    }
    ```
  </Tab>

  <Tab title="Пиксельная рамка">
    ```json theme={null}
    {
      "model": "grok-imagine-2.0-ext",
      "operation": "region_edit",
      "image_id": "<SOURCE_IMAGE_ID>",
      "prompt": "Change the car inside the box to bright red",
      "boxes": [[40, 689.6, 945.9, 1064.4]],
      "mask_size": [1792, 1008]
    }
    ```
  </Tab>

  <Tab title="Индекс объекта">
    ```json theme={null}
    {
      "model": "grok-imagine-2.0-ext",
      "operation": "region_edit",
      "image_id": "<SOURCE_IMAGE_ID>",
      "prompt": "Change the selected car to bright red",
      "object_indices": [0]
    }
    ```

    Индексы должны быть из ответа segment для того же `image_id`. Не заменяйте их индексами отфильтрованного, отсортированного или сгруппированного frontend-массива.
  </Tab>
</Tabs>

### Завершённый ответ

```json theme={null}
{
  "code": 200,
  "data": {
    "id": "task_...",
    "status": "completed",
    "progress": 100,
    "cost": 0.016,
    "credits_cost": 0.16,
    "result": {
      "images": [{
        "url": ["https://.../result.jpg"],
        "image_ids": ["<NEW_IMAGE_ID>"],
        "items": [{
          "url": "https://.../result.jpg",
          "image_id": "<NEW_IMAGE_ID>",
          "source_image_id": "<SOURCE_IMAGE_ID>",
          "role": "region_edit"
        }],
        "expires_at": 1787040000
      }]
    }
  }
}
```

Предпочитайте `result.images[0].items[0]`. Для старого ответа сопоставляйте `url[0]` и `image_ids[0]` только при равной длине массивов. Продолжайте лишь после получения HTTP(S) URL и нового `image_id`.

Срок URL определяйте по `expires_at`; не фиксируйте число часов. Нужные надолго файлы скачивайте или сохраняйте.

## Последовательное редактирование

После завершения одновременно обновите отображаемый URL, текущий ID изображения и ID исходной задачи, затем очистите старые слои и состояние опроса.

* Повторная сегментация: использовать ID этой задачи `region_edit` как `source_task_id`
* Повторное редактирование: использовать новый `image_id`
* Не передавайте `image_id` в `segment` и не продолжайте редактировать предыдущий ID изображения.

## Обработка ошибок

| HTTP / статус                      | Частая причина                                                                                 | Действие                                                                                        |
| ---------------------------------- | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| 400 неверный источник или операция | Неверная операция, недоступная исходная задача либо `image_id/image_index` отправлен в segment | Проверить операцию и использовать завершённую задачу текущего пользователя с одним изображением |
| 400 неверное выделение             | Пустой prompt, нет выделения или неверный полигон, рамка либо индекс                           | Проверить prompt и выделение до отправки                                                        |
| 400 неподдерживаемая опция         | Неверный `claim_asset`, `n`, формат, размер или streaming                                      | Удалить неподдерживаемые поля и использовать URL                                                |
| 401 / 403                          | Неверный ключ или нет доступа к модели                                                         | Проверить серверный ключ и права аккаунта                                                       |
| 402                                | Недостаточно средств                                                                           | Пополнить баланс перед повтором                                                                 |
| 409                                | Идемпотентный запрос выполняется, изменён или неопределён                                      | Следовать ответу и не менять ключ автоматически                                                 |
| 429 / 5xx                          | Лимит или временный сбой                                                                       | Соблюдать `Retry-After` и ограниченный backoff                                                  |
| failed / task\_failed              | Ошибка асинхронного выполнения                                                                 | Остановить опрос и показать `data.error.message`                                                |

## Оплата

* `segment` бесплатен и завершается с `cost=0` и `credits_cost=0`, но требует авторизации и допустимой исходной задачи.
* `region_edit` платный. Используйте `cost` и `credits_cost` завершённой задачи; не фиксируйте цены во frontend.
* Не отправляйте внутреннее поле `billing_model_name`.

## Проверка frontend

* Хранить API Key только на backend или BFF.
* Отправлять в `segment` только `source_task_id`, без `image_id` и `image_index`.
* Использовать `image_id` из segment для `region_edit` и передавать хотя бы один способ выделения.
* Для точного редактирования использовать `selection_regions`; `object_indices` — лишь приближение рамкой.
* Всегда читать `mask_size` как `[height,width]` и учитывать масштаб и поля.
* Для того же сетевого повтора использовать исходный ключ идемпотентности и проверять URL вместе с новым `image_id`.
