> ## 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`는 기존 비동기 이미지 엔드포인트를 사용합니다. 반환된 `task_id`를 저장한 뒤 [작업 상태 조회](/ko/api-reference/tasks/status)를 폴링하세요. 생성 요청은 최종 레이어나 이미지를 직접 반환하지 않습니다.
</Info>

<Warning>
  API Key를 브라우저 번들, LocalStorage, URL 또는 프런트엔드 로그에 노출하지 마세요. 백엔드나 BFF를 통해 APIMart를 호출하세요.
</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를 사용합니다. 편집된 이미지를 다시 분할하려면 완료된 `region_edit` 작업 ID를 다음 `source_task_id`로 사용하세요.
</Note>

## 요청 헤더

`Authorization: Bearer <APIMART_API_KEY>`, `Content-Type: application/json`, `Accept: application/json`을 사용합니다.

`Idempotency-Key`는 선택 사항이지만 유료 `region_edit` 요청에는 강력히 권장합니다. 표시 가능한 ASCII 문자 1\~191자를 지원하며 UUID를 권장합니다. 새 논리 작업마다 새 Key를 사용하고 같은 요청의 네트워크 재시도에는 원래 Key와 동일한 body를 재사용하세요. 결과가 불확실하면 새 Key로 자동 재시도하지 마세요.

## 비동기 작업 흐름

생성 성공 시 HTTP `200`과 `data[0].task_id`가 반환됩니다. `GET /v1/tasks/{task_id}?language=ko`를 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` | 분할 캐시만 확인하며 miss 시 업스트림 호출 안 함           |
| `cached_only`      | boolean |  —  | `false` | 업스트림 캐시 힌트이며 로컬 캐시 보장은 아님                |
| `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>

캐시 miss도 성공 작업입니다. `cache_status` 또는 `from_cache`를 사용하고 `cached`로 hit를 판단하지 마세요.

### 완료 응답

`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`     | `region_edit`에서 사용하는 자산 ID          |
| `result.image_url`    | `image_id`와 대응하는 HTTP(S) URL        |
| `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개 이상, 0이 아닌 면적, 자기 교차 없음 조건을 충족해야 합니다. 레이어당 최대 16개 영역, 링당 최대 400개 점을 유지합니다.

<Warning>
  `mask_size`는 `[height,width]`이며 CSS 표시 좌표가 아닌 원본 마스크 좌표입니다. `object-fit: contain`에서는 여백을 빼고 실제 그리기 영역으로 변환한 뒤 `0–1`로 제한하세요.
</Warning>

원본 이미지나 `mask_url` 픽셀을 읽으려면 CORS가 필요합니다. `src`보다 먼저 `crossOrigin = "anonymous"`를 설정하거나 Blob을 가져오세요. `mask_rle` 직접 디코딩은 이 제약을 피합니다.

## 영역 편집: `region_edit`

### 요청 매개변수

| 필드                  | 유형           |  필수 | 설명                                                     |
| ------------------- | ------------ | :-: | ------------------------------------------------------ |
| `model`             | string       |  ✅  | `grok-imagine-2.0-ext`로 고정                             |
| `operation`         | string       |  ✅  | `region_edit`                                          |
| `image_id`          | string       |  ✅  | 원본 자산 ID. 처음에는 `segment`의 `image_id`, 이후에는 최신 편집 결과 사용 |
| `prompt`            | string       |  ✅  | 원하는 변경을 설명하는 비어 있지 않은 지시문                              |
| `selection_regions` | array        |  \* | `outer`와 선택적 `holes`가 있는 `0–1` 정규화 다각형. 권장             |
| `boxes`             | number\[]\[] |  \* | 사각형 `[x1,y1,x2,y2]`. 픽셀 박스에는 `mask_size` 필요            |
| `object_indices`    | integer\[]   |  \* | 원본 `objects[].index` 값. 근사 박스 편집 전용                    |
| `mask_size`         | integer\[]   |  \* | 픽셀 박스에 필수. 양의 정수 `[height,width]`                      |

`selection_regions`, `boxes`, `object_indices` 중 하나 이상이 비어 있지 않아야 합니다. API는 조합을 허용하지만 프런트엔드는 요청당 한 방식만 사용하는 것이 좋습니다.

<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` | 프런트엔드 다각형      | 구멍을 포함한 정밀 선택 | 프로덕션 레이어 또는 브러시 편집 |
| `boxes`             | 프런트엔드 사각형      | 박스 근사         | 박스 도구 또는 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]
    }
    ```

    인덱스는 같은 `image_id`의 segment 응답에서 가져와야 합니다. 프런트엔드에서 필터링, 정렬, 그룹화한 배열의 인덱스로 바꾸지 마세요.
  </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를 함께 갱신하고 이전 레이어와 폴링 상태를 지우세요.

* 다시 분할: 이번 `region_edit` 작업 ID를 `source_task_id`로 사용
* 다시 편집: 새로 반환된 `image_id` 사용
* `image_id`를 `segment`에 전달하지 말고 이전 이미지 ID를 계속 편집하지 마세요.

## 오류 처리

| HTTP / 상태             | 일반 원인                                                         | 처리                                |
| --------------------- | ------------------------------------------------------------- | --------------------------------- |
| 400 원본 또는 작업 오류       | 잘못된 작업, 사용할 수 없는 원본 작업, 또는 segment에 `image_id/image_index` 전송 | 작업을 검증하고 현재 사용자의 완료된 단일 이미지 작업 사용 |
| 400 선택 영역 오류          | 빈 프롬프트, 선택 없음, 또는 잘못된 다각형·박스·인덱스                              | 전송 전에 프롬프트와 선택 영역 검증              |
| 400 미지원 옵션            | 잘못된 `claim_asset`, `n`, 출력 형식, 크기 또는 스트림                      | 미지원 필드를 제거하고 URL 출력 사용            |
| 401 / 403             | Key 오류 또는 모델 권한 없음                                            | 서버 Key와 계정 권한 확인                  |
| 402                   | 잔액 부족                                                         | 충전 후 재시도                          |
| 409                   | 멱등 요청 처리 중, 변경 또는 결과 불확실                                      | 응답에 따르고 Key를 자동 변경하지 않음           |
| 429 / 5xx             | 요청 제한 또는 일시적 장애                                               | `Retry-After`에 따라 제한된 백오프         |
| failed / task\_failed | 비동기 실행 실패                                                     | 폴링 중지 후 `data.error.message` 표시   |

## 과금

* `segment`는 무료이며 `cost=0`, `credits_cost=0`으로 완료되지만 인증과 유효한 원본 작업이 필요합니다.
* `region_edit`는 유료입니다. 완료 작업의 `cost`와 `credits_cost`를 사용하고 프런트엔드에 가격을 고정하지 마세요.
* 내부 필드 `billing_model_name`은 보내지 마세요.

## 프런트엔드 체크리스트

* API Key는 백엔드 또는 BFF에만 보관합니다.
* `segment`에는 `source_task_id`만 보내고 `image_id`나 `image_index`는 보내지 않습니다.
* `region_edit`에는 segment의 `image_id`와 하나 이상의 선택 방식을 사용합니다.
* 정밀 편집은 `selection_regions`를 사용하고 `object_indices`는 박스 근사로만 사용합니다.
* `mask_size`를 항상 `[height,width]`로 해석하고 표시 배율과 여백을 보정합니다.
* 같은 네트워크 재시도에는 원래 멱등 Key를 재사용하고 URL과 새 `image_id`를 모두 검증합니다.
