# Seedream 5.0 Proレイヤー分解APIの使い方：実測14枚とPSD化

> Seedream 5.0 Proのレイヤー分解は画像生成APIにlayer_decomposition: trueを足すだけ。1Kの実測では14枚返り、費用は枚数×単価です。

- URL: https://blog.laozhang.ai/ja/posts/seedream-5-pro-layer-decomposition-api
- Published: 2026-10-02
- Updated: 2026-10-02
- Author: LaoZhang AI Team (https://blog.laozhang.ai/ja/about)
- Category: AI画像生成
- Tags: Seedream 5.0 Pro, レイヤー分解, 画像生成API, PSD, Seedream 5.0 Flash

---
Seedream 5.0 Proのレイヤー分解（layer decomposition）は専用のモデルやエンドポイントではなく、通常の画像生成APIに`"layer_decomposition": true`と入力画像1枚を渡すだけで動きます。返ってくるのはベース画像1枚と最大16枚の透過PNGレイヤー、それに各レイヤーの重ね順と座標です。PSDは返ってこないので、必要なら座標どおりに自分で組み立てます。料金は返ってきた枚数で決まり、その枚数は呼び出す側では指定できません。

以下の数字のうち「実測」と書いたものは、2026年10月2日に次の範囲で実際に呼び出した結果です。

- **実行したこと**：`api.laozhang.ai`経由で3回（Flashの自動分解、Proの自動分解、Flashで2要素だけを指定）。サイズはすべて1K、入力はBytePlusが公式チュートリアルで公開しているサンプル画像1枚（2784×3441のPNG、文字なしの3Dイラスト）。あわせて、3回分のレスポンスをPNG・再合成プレビュー・PSDに書き出すスクリプトを実行。
- **実行していないこと**：BytePlus ModelArkへの直接呼び出し、1.5K・2K・auto、bboxタグによる領域指定、文字の多いポスター、エラーになる入力（WebP、画像2枚など）、PhotoshopでのPSDの確認。

各設定1回ずつ、画像1枚の結果なので、所要時間や枚数は「この条件ではこうだった」という記録として読んでください。実行していない部分はBytePlusの英語ドキュメントの記載に基づきます（日本語の公式ドキュメントはありません）。

## 実測結果：1Kで14枚、Flashは94.8秒、Proは110.6秒

| 呼び出し | モデル | プロンプト | 所要時間 | 返ってきた画像 | LaoZhangの単価での費用 |
| --- | --- | --- | --- | --- | --- |
| 自動分解 | `seedream-5-0-flash-260915` | なし | 94.8秒 | 14枚（ベース+13レイヤー） | 14×$0.018＝$0.252 |
| 自動分解 | `seedream-5-0-pro-260628` | なし | 110.6秒 | 14枚（ベース+13レイヤー） | 14×$0.12＝$1.68 |
| 2要素を指定 | `seedream-5-0-flash-260915` | 中央のボーカルとギターだけを指定 | 34.8秒 | 3枚（ベース+2レイヤー） | 3×$0.018＝$0.054 |

3回ともHTTP 200で、ベース画像は880×1088のJPEGでした。費用は公開されている1枚あたりの単価に`usage.generated_images`を掛けた計算値で、管理画面の請求ログで確かめた金額ではありません。

この3行から読み取れることは3つあります。同じ画像でもFlashとProは同じ14枚を返したが、分け方は違った（Flashは木のステージをレイヤーにし、Proはステージをベース画像に残してウクレレを独立したレイヤーにした）。取り出す要素を2つに絞ると、枚数は14枚から3枚、時間は94.8秒から34.8秒、費用は$0.252から$0.054に下がった。そして、どの呼び出しも数十秒から2分近くかかるので、HTTPクライアントの既定のタイムアウトでは足りない場合がある、という点です。

## リクエストの書き方：通常の画像生成にlayer_decompositionを足す

レイヤー分解に対応しているのはSeedream 5.0 ProとSeedream 5.0 Flashの2モデルだけです。モデルIDは接続先で表記が違います。

| 接続先 | エンドポイント | モデルID |
| --- | --- | --- |
| BytePlus ModelArk | `https://ark.ap-southeast.bytepluses.com/api/v3/images/generations` | `dola-seedream-5-0-pro-260628`、`dola-seedream-5-0-flash-260915` |
| LaoZhang | `https://api.laozhang.ai/v1/images/generations` | `seedream-5-0-pro-260628`、`seedream-5-0-flash-260915` |

実際に送ったリクエスト本体はこれです（Flashの自動分解）。Proの回は`model`だけを差し替えました。

```json
{
  "model": "seedream-5-0-flash-260915",
  "image": "https://arkdocs-en.tos-ap-southeast-1.volces.com/images/image-generation/layer_auto.png",
  "layer_decomposition": true,
  "size": "1K",
  "response_format": "url",
  "watermark": false
}
```

送信に使ったのはPythonの`requests`で、タイムアウトは420秒にしました。下のコードは実行したスクリプトの送信部分で、APIキーの読み込みを環境変数に、保存先を`response.json`に書き換えてあります。書き換えた後の形では実行していません。

```python
import json
import os
import time
from pathlib import Path

import requests

body = {
    "model": "seedream-5-0-flash-260915",
    "image": "https://arkdocs-en.tos-ap-southeast-1.volces.com/images/image-generation/layer_auto.png",
    "layer_decomposition": True,
    "size": "1K",
    "response_format": "url",
    "watermark": False,
}

start = time.time()
resp = requests.post(
    "https://api.laozhang.ai/v1/images/generations",
    headers={"Authorization": f"Bearer {os.environ['LAOZHANG_API_KEY']}"},
    json=body,
    timeout=420,
)
print("HTTP", resp.status_code, round(time.time() - start, 1), "s")
Path("response.json").write_text(json.dumps(resp.json(), indent=2, ensure_ascii=False))
```

BytePlusに直接送る場合の書き方は、公式チュートリアルのcURLが基準になります。こちらは実行していません。

```bash
curl https://ark.ap-southeast.bytepluses.com/api/v3/images/generations \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $ARK_API_KEY" \
  -d '{
    "model": "dola-seedream-5-0-pro-260628",
    "image": "https://arkdocs-en.tos-ap-southeast-1.volces.com/images/image-generation/layer_auto.png",
    "size": "2K",
    "layer_decomposition": true,
    "watermark": false
  }'
```

### レイヤー分解で変わるパラメータ

[BytePlusのAPIリファレンス](https://docs.byteplus.com/en/docs/modelark/image-generation-api)と[Seedream 5.0 Proのチュートリアル](https://docs.byteplus.com/en/docs/modelark/seedream-5-0-pro)（どちらも英語、2026年10月2日時点）の記載をまとめると次のとおりです。

| パラメータ | レイヤー分解での扱い |
| --- | --- |
| `image` | 必須で、ちょうど1枚。複数枚を渡すとエラー |
| `prompt` | 任意。省略すると自動分解 |
| `size` | `1K`、`1.5K`、`2K`、`auto`のみ（既定は`auto`）。幅×高さの直接指定は不可 |
| `output_format` | `png`か`jpeg`（既定は`jpeg`）。効くのはベース画像だけで、レイヤーは常にPNG |
| `response_format` | `url`（既定、URLの有効期間は24時間）か`b64_json` |
| `watermark` | 既定は`true`で、右下に「AI-generated」の透かしが入る |

`size`は「ベース画像の解像度の段階」を決めるもので、縦横比は入力のまま保たれます。実測では2784×3441の入力に`1K`を指定して880×1088のベース画像が返りました。`auto`は、入力が921,600〜4,624,220画素なら入力と同じ寸法、それより小さければ1K、大きければ2Kで出力すると記載されています。Seedream 5.0 Proを「ネイティブ4K出力」と紹介するページもありますが、公式ドキュメントにある段階は2Kまでです。

LaoZhang経由では、リクエストに`sequential_image_generation`や`stream`を含めるとHTTP 400になると[同社のドキュメント](https://docs.laozhang.ai/en/api-capabilities/seedream-image)に書かれています。通常の画像生成用のコードを流用するときは、この2つを外してください。

### 入力画像の条件：PNGかJPEG、1枚、30MB以下

通常の画像生成より入力の条件が狭くなります。

- 形式はPNGとJPEGのみ（通常の生成で使えるWebP、BMP、TIFF、GIF、HEIC、HEIFは対象外）
- 総画素数は512×512（262,144画素）から6000×6000（36,000,000画素）まで
- 縦横比は1/16〜16、ファイルサイズは30MB以下
- URLは外部から取得できること。Base64で渡すなら`data:image/png;base64,...`の形で、形式名は小文字

WebPや2枚目の画像を渡したときに返るエラーの中身は確かめていません。素材がWebPなら、送る前にPNGかJPEGに変換しておくのが確実です。

## プロンプトの3通り：省略、要素を名指し、bboxタグ

何をレイヤーにするかは、プロンプトの書き方で3段階に指定できます。

**省略して自動分解する。** `prompt`を送らなければ、モデルが主要な要素を判断して分けます。実測の14枚はこの方法です。BytePlusのチュートリアルには、cURL・Java・Goでは`prompt`を省略でき、PythonとOpenAIのSDKは`prompt`が必須なので「主要な視覚要素を分解する」という一般的な指示を入れる、という注記があります。SDKを使わずHTTPで直接送れば、この制約はかかりません。

空文字列を送った場合の挙動は情報が食い違っています。LaoZhangのドキュメントは「省略しても空文字列でもよい」とし、中継サービスEvoLinkの解説は「空文字列では自動検出が働かなくなる」としています。どちらが正しいかは確かめていないので、自動分解にしたいときは`prompt`のキー自体を送らないのが安全です。

**要素を自然文で名指しする。** 公式の例は「Decompose the person, title text, and decorative icon in the lower-right corner」のような書き方です。実測では次のプロンプトを足しました。

```json
{
  "prompt": "Separate only the central toast lead singer with sunglasses and the toast-shaped electric guitar."
}
```

返ってきたレイヤーは「toast-shaped electric guitar」と「central toast lead singer with sunglasses」の2枚だけで、それ以外はすべてベース画像に残りました。欲しい素材が決まっているなら、自動分解より速く安く済みます。入力画像に手描きの丸や選択範囲を描き込んで対象を示す方法も公式に記載されています。

**bboxタグで領域を座標指定する。** プロンプトの中にbboxタグを書き、左・上・右・下を0〜1000の正規化座標で渡します。

```text
Decompose the element in <bbox>263 462 378 824</bbox> as a separate layer.
```

座標の形式は公式の記載どおりですが、この方法は実行していません。上の文面は書式を示す例です。自動分解で返ってきた`bounding_box.normalized`が同じ0〜1000の座標系なので、1回目の結果から欲しい要素の値を取り、2回目に領域を絞って呼ぶ使い方ができます。

なお、要求したレイヤー数が上限の16枚を超えると、一部のレイヤー情報が失われることがあると公式に書かれています。

## レスポンスの読み方：z_index、bounding_box、usage

`data`配列にベース画像と全レイヤーが入り、`z_index`が0のものがベース画像です。Flashの自動分解で実際に返ってきたベース画像とマイクスタンドのレイヤーを抜き出すとこうなります（URLは途中を省略）。

```json
{
  "model": "dola-seedream-5-0-flash-260915",
  "data": [
    {
      "url": "https://ark-acg-ap-southeast-1.tos-ap-southeast-1.volces.com/.../..._base.jpeg",
      "size": "880x1088",
      "output_format": "jpeg",
      "z_index": 0
    },
    {
      "url": "https://ark-acg-ap-southeast-1.tos-ap-southeast-1.volces.com/.../..._layer_12.png",
      "size": "271x1037",
      "output_format": "png",
      "z_index": 12,
      "bounding_box": {
        "absolute": [231, 503, 334, 897],
        "normalized": [263, 462, 378, 824]
      },
      "name": "Standing microphone stand",
      "description": "A black standing microphone stand including the microphone and base, positioned to the left of the central toast."
    }
  ],
  "usage": {
    "input_images": 1,
    "generated_images": 14,
    "output_tokens": 53663,
    "total_tokens": 53663
  }
}
```

| フィールド | 意味 |
| --- | --- |
| `z_index` | 重ね順。0がベース画像、1から上へ。実測では1〜13が欠番なく並んだ |
| `bounding_box.absolute` | `[left, top, right, bottom]`。ベース画像のピクセル座標 |
| `bounding_box.normalized` | 同じ枠を、ベース画像の幅と高さを0〜1000とした整数で表したもの |
| `size` | その画像ファイル自体の寸法。レイヤーでは枠の寸法と一致しない |
| `name`、`description` | モデルが付けた英語のレイヤー名と説明。ベース画像には`name`も`bounding_box`もない |
| `usage.generated_images` | 返ってきた画像の枚数。ベース画像を含む（13レイヤーで14） |
| `usage.output_tokens` | 全画像の幅×高さの合計を256で割って丸めた値。入力トークンは数えない |

LaoZhang経由で送っても、レスポンスの`model`はBytePlus側の表記（`dola-`付き）で返ってきました。

### レイヤーをキャンバスに戻す：枠の大きさに縮小してから置く

公式の手順は、ベース画像を背景にし、レイヤーを`z_index`の小さい順に重ねるというものです。位置と大きさは`absolute`から求めます。

```text
x = left
y = top
w = right - left
h = bottom - top
```

ここで見落としやすいのが、レイヤーのPNGは枠より大きいという点です。公式も「レイヤーをw×hに拡大縮小してから(x, y)に置く」としていて、実測でもすべてのレイヤーがそうでした。

| レイヤー（Flash・自動分解） | PNGファイルの寸法 | 枠の寸法と位置 |
| --- | --- | --- |
| マイクスタンド | 271×1037 | 103×394、(231, 503) |
| 折りたたみ椅子 | 660×940 | 118×169、(9, 785) |
| ミニバン | 759×627 | 390×323、(305, 528) |

![マイクスタンドのレイヤーは271×1037のPNGで返り、枠の103×394に縮小してからベース画像の(231, 503)に置く手順の図](https://blog.laozhang.ai/posts/ja/seedream-5-pro-layer-decomposition-api/img/layer-box-scaling.webp)

PNGを原寸のまま(x, y)に貼ると、要素が何倍にも大きく写ります。`size`パラメータの説明に「各レイヤーは指定した解像度に近い大きさで出力される」とあるとおり、小さな要素でも1K相当の画素数を持ったファイルとして返るためです。

ベース画像と違う大きさのキャンバスに置くときは`normalized`を使います。キャンバスが幅W・高さHなら次の式です。

```text
x = left / 1000 × W
y = top / 1000 × H
w = (right - left) / 1000 × W
h = (bottom - top) / 1000 × H
```

正規化座標は整数なので、換算で丸め誤差が出ることがあると公式に注記されています。レイヤーのファイルは1Kの枠より多くの画素を持っているので、寸法の上では、ベース画像より少し大きいキャンバスに置いても引き伸ばしにならない余裕があります。ただし余裕はレイヤーごとに違い、元画像と同じ幅2784のキャンバスでは、マイクスタンドの枠は幅およそ320になり、271のファイルでは足りません（見た目での確認はしていません）。

## PSDで受け取れるか：返るのはPNGとJSON、PSDはスクリプトで組む

APIがPSDを返すことはありません。返るのは画像のURL（またはBase64）と座標なので、PSDは手元で組み立てます。下のスクリプトは、レスポンスのJSONを読み、全画像を`layer-NN-名前.png`として保存し、座標どおりに重ねた`recomposed.png`と、レイヤー名付きの`layers.psd`を書き出します。

実行した環境はPython 3.12、Pillow 12.3.0、psd-tools 1.23.0で、上の3回分のレスポンスすべてで動かしました。

```bash
python3 -m pip install pillow psd-tools
python3 layers_to_psd.py response.json out_dir
```

```python
"""Save every image of a Seedream layer-decomposition response, recompose a
flat preview, and write a layered PSD.

Usage: python3 layers_to_psd.py response.json out_dir
Needs: python3 -m pip install pillow psd-tools
"""
import base64
import json
import re
import sys
import urllib.request
from io import BytesIO
from pathlib import Path

from PIL import Image
from psd_tools import PSDImage

response = json.loads(Path(sys.argv[1]).read_text())
out = Path(sys.argv[2])
out.mkdir(parents=True, exist_ok=True)


def load(item):
    if item.get("b64_json"):
        value = item["b64_json"].split(",", 1)[-1]
        return base64.b64decode(value + "=" * (-len(value) % 4))
    with urllib.request.urlopen(item["url"], timeout=120) as download:
        return download.read()


items = sorted(response["data"], key=lambda item: item["z_index"])
base_item, layer_items = items[0], items[1:]
assert base_item["z_index"] == 0 and "bounding_box" not in base_item

content = load(base_item)
ext = "png" if base_item.get("output_format") == "png" else "jpg"
(out / f"layer-00-base.{ext}").write_bytes(content)
base = Image.open(BytesIO(content)).convert("RGBA")
canvas = base.copy()

psd = PSDImage.new("RGBA", base.size)
psd.append(psd.create_pixel_layer(base, name="base", top=0, left=0))

for item in layer_items:
    content = load(item)
    slug = re.sub(r"[^a-z0-9]+", "-", item.get("name", "layer").lower()).strip("-") or "layer"
    (out / f"layer-{item['z_index']:02d}-{slug}.png").write_bytes(content)
    left, top, right, bottom = item["bounding_box"]["absolute"]
    # The PNG is usually larger than its box: scale it to the box first.
    layer = Image.open(BytesIO(content)).convert("RGBA").resize((right - left, bottom - top), Image.LANCZOS)
    canvas.alpha_composite(layer, (left, top))
    psd.append(psd.create_pixel_layer(layer, name=item.get("name", slug), top=top, left=left))
    print(f"z={item['z_index']:>2} file={item['size']:>9} box={right - left}x{bottom - top} at ({left},{top})  {item.get('name')}")

canvas.save(out / "recomposed.png")
psd.save(out / "layers.psd")
print(f"Saved {len(layer_items)} layers + base, recomposed.png and layers.psd in {out}")
```

掲載したコードは、実行したファイルから、テスト記録の入れ物を外すための1行だけを除いたものです。APIのレスポンスをそのまま保存したJSONを渡す使い方では、その行は何もしません。

スクリプトの出力は次のとおりでした。書き出されたPSDは880×1088で、自動分解の2回は名前付きのピクセルレイヤーが14枚、2要素指定の回は3枚。各レイヤーは`absolute`の位置に置かれ、psd-toolsで開き直して合成した結果は`recomposed.png`と一致しました（平均絶対差0.001以下）。Photoshop、Photopea、GIMPでは開いていないので、それらのアプリでの表示は未確認です。

使うときの注意が3つあります。

- URLの有効期間は24時間なので、レスポンスを受け取ったらすぐ実行します。後から処理するなら`response_format`を`b64_json`にして、JSONごと保存しておけばスクリプトはそのまま読めます（`b64_json`での呼び出し自体は実行していません）。
- PSDに入るレイヤーは枠の大きさに縮小した後のものです。元の高い解像度が必要な素材は、同時に保存される`layer-NN-名前.png`を使ってください。
- レイヤーはすべてラスター画像です。レスポンスに編集できるテキストオブジェクトは含まれないので、文字を打ち直せるPSDにはなりません。

コードを書かずにレイヤー付きPSDだけ欲しい場合は、ブラウザで使える[layerpsd.com](https://layerpsd.com/)があります。JPG・PNG・WebPをレイヤー付きPSDに分けるツールで、同サイトの表示では料金は1レイヤー$0.018から、サブスクリプションなし、モデルはSeedream 5.0 Flashです（この記述は同サイトの説明によるもので、出力は試していません）。

## 再合成すると元画像に戻るか：Flashは30.2%の画素が変わった

戻りません。レイヤーを座標どおりに重ねた画像と、同じ大きさに縮小した入力画像を比べると、3回とも差が出ました。

| 呼び出し | 差が出た画素の割合 | 平均絶対差 |
| --- | --- | --- |
| Flash・自動分解（13レイヤー） | 30.2% | 20.6 |
| Pro・自動分解（13レイヤー） | 12.6% | 13.6 |
| Flash・2要素を指定 | 4.0% | 6.7 |

![再合成で差が出た画素の割合はFlashの自動分解が30.2%、Proの自動分解が12.6%、2要素指定が4.0%だったことを示す棒グラフ](https://blog.laozhang.ai/posts/ja/seedream-5-pro-layer-decomposition-api/img/recompose-pixel-diff.webp)

「差が出た画素」は、RGBのいずれかのチャンネルで値が32（255段階中）を超えて違う画素です。画像1枚、各1回の数字なので、モデルの一般的な再現率ではありません。

差の理由はファイルを見ると分かります。各レイヤーは、ほかの要素に隠れていた部分まで描き足された「完成した物体」として返り、ベース画像は取り除かれた要素の裏側が塗り直されています。2要素を指定した回のベース画像では、ボーカルが立っていた場所にミニバンのフロントガラスとグリルが描かれていました。レイヤーを動かしても裏に穴が空かないのはこのためで、同じ理由で、重ね直しても画素単位の複製にはなりません。切り抜きやセグメンテーションのマスクとは性質が違う出力です。

Flashの自動分解では、変化が目に見える大きさでした。ピントの外れていた手前の2体はくっきりした別の形になり、左の演奏者はひと回り大きい全身像に描き直され、ステージは完全な円盤になって元画像で見えていた芝生を覆い、宙に浮いていた泡はベース画像からもレイヤーからも消えました。Proは手前のぼけと泡を残し、見た目は入力に近く、差は主に輪郭に出ました。

EvoLinkの解説には「z_index順にabsoluteの枠へ重ねれば元画像が正確に再現される」とありますが、この実測とは合いません。元の見た目を保つことが条件なら、取り出す要素を絞る、Proを使う、そして必ず再合成プレビューを入力と見比べる、の3つが現実的な対策です。

## 1回いくらかかるか：枚数×単価、14枚で$0.252〜$1.68

1回の費用は「1枚あたりの単価×返ってきた枚数（ベース画像+レイヤー）」です。枚数は最少2枚、最大17枚で、`usage.generated_images`に出ます。2026年10月2日時点の公開単価で、実測の枚数を当てはめるとこうなります。

| 接続先とモデル | 1枚の単価 | 3枚 | 14枚 | 17枚（上限） |
| --- | --- | --- | --- | --- |
| BytePlus・Flash | $0.018 | $0.054 | $0.252 | $0.306 |
| BytePlus・Pro（261万画素以下） | $0.0225 | $0.0675 | $0.315 | $0.3825 |
| BytePlus・Pro（261万画素超） | $0.045 | $0.135 | $0.63 | $0.765 |
| LaoZhang・Flash | $0.018 | $0.054 | $0.252 | $0.306 |
| LaoZhang・Pro | $0.12 | $0.36 | $1.68 | $2.04 |

読み方の条件をまとめます。

- BytePlusの単価は[料金ページ](https://docs.byteplus.com/en/docs/modelark/model-pricing)の定価で、税や割引は含みません。Proのレイヤー分解は出力画像ごとに画素数で段階が決まり、261万画素以下（1.5K以下）が$0.0225、それを超えると$0.045です。同じリクエストのレイヤーでも段階が分かれることがあり、1枚ずつ課金されます。2Kのベース画像（たとえば2048×2048は約419万画素）は上の段階に入ります。
- BytePlusの行は、ベース画像もレイヤーと同じ単価で課金されるという前提の計算です。料金ページにベース画像の扱いは明記されていませんが、`generated_images`はベース画像を含めて数えられ、課金は生成に成功した画像数に基づくと書かれています。
- 入力画像は、Flashは無料、Proも1枚目は無料です。レイヤー分解の入力は1枚だけなので、BytePlusでは入力料金はかかりません。
- LaoZhangは画素数による段階がなく、ベース画像を含む枚数だけで決まります（Flash $0.018、Pro $0.12）。
- BytePlusは1.5Kについて「1Kと同じ価格で、より高い品質」と説明しています。

バッチ処理の見積もりは、まず数枚を自動分解して平均の枚数を出し、それに単価と画像数を掛けます。上限で見るなら1画像あたり17枚です。たとえばFlashで100画像なら、14枚平均で100×14×$0.018＝$25.2、上限では100×17×$0.018＝$30.6になります。欲しい要素が決まっている素材づくりなら、名指しで絞るほうが費用を読みやすくなります。

## FlashとPro、BytePlus直接とLaoZhang経由の選び方

**最初の1回はFlashで試す。** 1枚$0.018はBytePlusでもLaoZhangでも同じで、実測の14枚でも$0.252です。画像がどう分かれるか、枚数がどのくらいになるかを安く確かめられます。

**元画像の見た目を保ちたいならProを検討する。** 同じ画像で、再合成の差はFlashの30.2%に対してProは12.6%でした。背景のぼけや細かい要素を残したいデザインの再編集では、この差が効きます。ただし1枚の画像での比較です。自分の素材で両方を1回ずつ試してから決めてください。

**ProをまとめてまわすならBytePlus直接が安い。** Proのレイヤー分解は、BytePlusが1枚$0.0225または$0.045、LaoZhangが1枚$0.12です。0.12÷0.0225≒5.3、0.12÷0.045≒2.7なので、LaoZhang経由は定価の約5.3倍（261万画素以下）から約2.7倍（261万画素超）になります。14枚なら$0.315に対して$1.68です。

**FlashはLaoZhang経由でも割高にならない。** 単価がBytePlusの定価と同じなので、BytePlusのアカウントを作らずに、既存のキーで同じJSONを送れます。Proを数回試すだけの場合も、アカウント開設の手間と差額を比べて決めればよい範囲です。LaoZhangでは、キーの課金方式が「Usage first」か「Per-call」である必要があると同社のドキュメントに書かれています。

接続先で変わる点はほかにもあります。BytePlusでは、Seedream 5.0 ProとFlashは`ap-southeast-1`リージョンのみで提供されていて、EUのエンドポイントはありません。中国本土向けの火山方舟（Volcengine）にも同じ機能がありますが、モデルIDもエンドポイントも別で、料金は人民元建てです。

Seedream 5.0 Proそのものを別のモデルと比べて選びたい場合は、[Nano Banana Pro対Seedream 5.0 Pro](https://blog.laozhang.ai/ja/posts/nano-banana-pro-vs-seedream)に用途別の判断と公式料金があります。

## なぜ遅いか、失敗したらどうなるか：1リクエストで最大17枚を生成

レイヤー分解が通常の1枚生成より遅いのは、1回のリクエストで最大17枚の画像を作るからです。実測では、3枚で34.8秒、14枚で94.8秒（Flash）と110.6秒（Pro）でした。BytePlusのAPIは同期型で、Seedream 5.0 ProとFlashは`stream`に対応していないので、接続を張ったまま結果を待つことになります。

**タイムアウトは長めに取る。** LaoZhangのドキュメントは1Kで300秒以上を勧めています。実測では420秒に設定しました。同じドキュメントには、2Kのリクエストは長時間かかることがあり、クライアントが途中で切断しても課金されるとあります。短いタイムアウトで切って再送すると、結果を受け取れないまま費用だけが重なるおそれがあります。

**部分的な成功はない。** BytePlusは「レイヤーが1枚でも生成に失敗すると、リクエスト全体が失敗する」と明記しています。課金は成功した画像に対してだけで、モデレーションなどで出力されなかった画像は課金されません。LaoZhangのドキュメントも、分解できない画像はHTTP 400を返し課金されないとしています。エラー時の実際の応答は確かめていません。

**古いモデルでは動かない。** LaoZhangのドキュメントによると、`seedream-5-0-260128`や`seedream-4-5-251128`に`layer_decomposition`を付けるとHTTP 400（`InvalidParameter`）になります。

**レート制限は17枚分が先に引かれる。** BytePlusの既定は、モデルごとに1分あたり500枚（500 IPM）です。レイヤー分解では、リクエスト開始時にベース画像1枚とレイヤー16枚の最大出力分として17 IPMが先に差し引かれ、生成が終わってから実際の枚数との差が戻されます。500÷17＝29.4なので、枠が満杯の状態から1分間に開始できるのは29リクエストまで（29×17＝493）です。戻るのは完了後で、実測では1リクエストが35〜110秒かかっているため、並列数を決めるときは実際の枚数ではなく17で割って考えます。これはBytePlusに直接つなぐ場合の計算で、中継サービス側の制限は別です。

**ブラウザから直接画像を読む場合。** LaoZhangのドキュメントによると、結果のURLはCORSヘッダーを返しません。ブラウザのコードで画素を扱うなら`b64_json`で受け取るよう案内されています。

## レイヤー分解APIが向かないケースと、代わりの手段

次の条件に当てはまるなら、別の方法のほうが合っています。

- **元画像と画素単位で同じでなければならない。** 隠れた部分を描き足し、背景を塗り直す仕組みなので、再合成は入力の複製になりません。実測で差がいちばん小さかった2要素指定でも4.0%の画素が変わりました。
- **切り抜きが1枚欲しいだけ。** 背景を消した素材1枚なら、レイヤー一式を生成する必要はありません。素材別の方法は[透過PNGの作り方：素材で選ぶ方法と保存後の確認](https://blog.laozhang.ai/ja/posts/transparent-image-maker)にまとめてあります。
- **文字を打ち直せるデータが欲しい。** APIが返すのはラスターのPNGです。BytePlusの公式例ではタイトル文字などがレイヤーとして分かれますが、文字の多い画像は今回の実測に含まれていません。
- **4Kで納品する。** レイヤー分解の`size`は2Kまでです。
- **要素が17個以上ある画像をすべて分けたい。** 上限は16レイヤーで、超えた分は欠けることがあります。領域を分けて複数回呼ぶ設計になります。
- **枚数と費用を事前に固定したい。** 自動分解の枚数はモデルが決めます。LaoZhangのドキュメントは、同じ画像でも実行ごとに枚数が変わりうるとしています。要素を名指しして絞るのが、枚数をいちばん制御しやすい方法です。

逆に、取り出したレイヤーをさらに加工する道は用意されています。公式チュートリアルによると、返ってきたレイヤーのPNGを通常のSeedreamリクエストに1枚だけ入力し、`"background": "transparent"`とPNG出力を指定すれば、透過を保ったまま色や質感を変えられます。入力がアルファチャンネル付きの画像1枚であることが条件で、`output_format`が`jpeg`の場合やJPEGを入力した場合はエラーになります。この手順は実行していません。

最初の1回は、Flash・1K・プロンプトなしで手元の画像を1枚送り、返ってきた枚数と`recomposed.png`を見るところから始めるのが確実です。そこで分け方と見た目の差を確かめてから、要素を絞るか、Proに切り替えるか、解像度を上げるかを決めれば、費用の読み違いを避けられます。
