zhwx-layer · 图层拆分 / 可编辑 PPT 导出
把一张扁平图片(AI 生成的、拍的、设计的都行)拆成「1 张干净背景板 + 最多 15 个带 alpha 通道的 RGBA 图层」,导出成真正可编辑的 PPTX(每层一个独立图片对象),也可选导出 PSD。
快速开始
# 拆分 + 导出可编辑 PPT(最常用)
python zhwx_layer.py --image poster.jpg --outdir ./layers --pptx out.pptx
# 只拆指定元素(自然语言控制)
python zhwx_layer.py --image poster.jpg --prompt "只拆出所有人物和双肩背包" --pptx out.pptx
# 已有公网 URL,跳过图床上传
python zhwx_layer.py --image https://xxx/a.png --pptx out.pptx
# 顺便导出 PSD
python zhwx_layer.py --image poster.jpg --outdir ./layers --pptx out.pptx --psd out.psd
脚本:~/.workbuddy/skills/zhwx-layer/zhwx_layer.py
参数
| 参数 | 说明 |
|---|---|
--image |
输入图:本地路径(自动上传图床)或公网 URL |
--prompt |
留空=自动识别全部元素;也可写「只拆出所有人物和背包」 |
--outdir |
图层输出目录,默认 ./layers |
--pptx |
导出可编辑 PPTX 的路径(推荐) |
--psd |
额外导出 PSD(需 psd-tools) |
--size |
1K(默认)或 2K |
--key |
Key 文件路径 |
Key 优先级:环境变量 ZHWX_API_KEY > --key > 默认 D:\公司\AI聚合平台\AI工作区\.secrets\zhwxkey.txt。
Key 缺失时脚本会引导到 https://www.omniboxai.cn/ 注册申请。
输出文件
layers/
├── 00_base.jpeg # 干净背景板(元素已抹除)
├── 01_站立黑外套男生.png # 透明底图层,紧贴裁剪
├── 02_站立牛仔套装女生.png
├── ...
├── layers.pptx # ★ 可编辑 PPT:每层一个独立图片对象
├── _response.json # 完整 API 响应(含 bbox / name / description)
└── _recomposed.jpg # 重组校验图,应≈原图
核心事实(2026-09-26 全部实测确认)
| 项 | 结论 |
|---|---|
| 后端模型 | 智盒万象 → doubao-seedream-5-0-pro-260628。必须带 -260628 后缀 |
| 触发参数 | "layer_decomposition": true |
| 能否文生图直接出分层 | 不能。官方报错 layer_decomposition requires exactly one input image |
| 返回结构 | data[0]=底图(z_index:0,JPEG);data[1..N]=图层(PNG,带 z_index/name/description/bounding_box) |
| 最多层数 | 16 层(实测 15 层 + 1 底图) |
| 底图是什么 | 干净背景板——被拆出的元素都从底图上抹掉了。底图+所有图层=原图;单看底图=空场景 |
| 图层尺寸 | 每个图层是紧贴内容裁剪的小图,需按 bounding_box.absolute 的宽高缩放后放到 (left, top) |
| 输入要求 | 单张 PNG/JPEG,像素 ≥ 262144(如 512×512 起),否则报错 |
| 互斥项 | 与 background: transparent 互斥;与 sequential_image_generation(组图)互斥 |
| size | 智盒万象只认 1K/2K,不接受 auto |
关键踩坑(都已实测)
0. ★ base64 的真实状态:分端点,不是一刀切(2026-09-27 修正)
之前"只认 URL"的结论不准确。 实测(图 1024×592,base64 270180 字符):
| 端点 | 模型 | base64 | 分层 |
|---|---|---|---|
/v1/images/edits |
seedream-5-0-pro-260628 | ✅ 支持 | ❌ 不支持(带 layer_decomposition 只回单图) |
/v1/images/edits |
qwen-image-3.0-pro | ✅ 支持 | — |
/v1/images/generations |
qwen-image-3.0-pro | ✅ 支持 | — |
/v1/images/generations |
seedream-5-0-pro-260628 | ⚠️ 被当 URL 解析 | ✅ 支持 |
结论:base64 与「分层」目前分属两个端点,无法同时满足。
- 要 base64 直传 → 用 /v1/images/edits
- 要 分层 → 必须用 /v1/images/generations + 公网 URL
典型报错:
- /generations + seedream + base64 → invalid url specified: "/9j/4AAQ..."(把 base64 当 URL 了)
- /generations + 分层 + base64 → layer_decomposition requires exactly one input image
- /edits + 分层 → missing prompt parameter(该端点强制要 prompt)
脚本已内置自动图床兜底:本地图片先上传到 uguu.se(失败自动退到 0x0.st)换 URL。
> 实测图床可用性:uguu.se ✅ / 0x0.st ❌ / catbox.moe ❌。
0.5 ★ 内容审核:真人+内衣/泳装类会被整图拦截
报错:InputImageSensitiveContentDetected — "the input image may contain sensitive information"
- 智盒万象和火山方舟官方用同一套审核,换后端绕不过。
- 触发条件是「模特躯干」,不是「内衣」本身。实测同一张内衣海报分区裁剪:
| 区域 | 结果 |
|---|---|
| 整图 1440×1920 | 🚫 拦截 |
| 顶部头肩 y0-250(含脸、耳环、手镯) | ✅ 通过,8 层 |
| 右侧图标 x1010-1440(含肩臂) | ✅ 通过,5 层 |
| 底部文案 y1400-1920(含腰腹) | ✅ 通过,9 层 |
| 右侧区域取宽 x760 起(含胸部) | 🚫 拦截 |
绕行方案:分区裁剪后分别分层,再把各层 bbox 映射回原图坐标合并。
对电商内衣/泳装海报这类图,躯干核心区无法分层,但文案、图标、饰品、腰腹、手臂都能拆——
这些恰好是电商图最需要单独编辑的部分(改文案、换图标、调卖点)。
1. 分区裁剪的两个必踩坑
① 模型会缩放输入图,bbox 必须换算!
返回的 data[0].size(底图尺寸)才是模型实际坐标系,不是你裁剪图的尺寸。
直接把 bbox 当原图坐标用会严重错位。正确做法:
bw, bh = (int(v) for v in base_item["size"].split("x")) # 模型坐标系
sx, sy = crop_w / bw, crop_h / bh # 缩放比
real_bbox = (round(bb[0]*sx)+offset_x, round(bb[1]*sy)+offset_y,
round(bb[2]*sx)+offset_x, round(bb[3]*sy)+offset_y)
实测:裁 1440×250 的图,模型返回 2400x416,缩放比 0.600;不换算会偏 40%。
② 裁剪后像素 < 262144 会被拒
报错形如 expected the pixel to be at least 262144px, but received a 430x590px image。
裁剪后先判断,不足则等比放大(放大不改变坐标换算逻辑,因为用 data[0].size 反推):
if cw * ch < 262144:
k = (262144 / (cw * ch)) ** 0.5 * 1.02
crop = crop.resize((int(cw*k), int(ch*k)), Image.LANCZOS)
③ 多区域合并时按「面积降序」添加
大图层(如腰腹主体、牛仔裤)先加、位于底层;小元素(耳环、爱心、文字)后加浮在上层,
否则大层会盖住小元素。实测按面积降序后合成效果与原图一致。
2. 型号必须带日期后缀
doubao-seedream-5-0-pro-260628 ✅ 正常分层;
无后缀的 doubao-seedream-5-0-pro ❌ 静默忽略 layer_decomposition,只回单图。
3. 只有 5.0 Pro 支持分层
5.0 Lite / 4.5 / 4.0 均不支持。
4. 可编辑 PPT 的关键:用 EMU 精确坐标
PPTX 里 1 px = 9525 EMU。用 EMU 设置 left/top/width/height,图层才能精确对齐拼回原图。
脚本用 slide.shapes.add_picture(path, Emu(left*9525), Emu(top*9525), Emu(w*9525), Emu(h*9525)),
并把 shape.name 设为中文图层名(显示在 PPT 选择窗格 / 动画窗格里)。
z_index 升序添加,PPT 中后添加的在更上层,层级天然正确。
> 已验证:PPTX 内嵌 15 个媒体文件,14 个 PNG 保留 alpha 通道;按 PPT 坐标反向合成可精确还原原图。
5. PSD 导出的两个坑
- 中文图层名必须用
layer.name = 中文名赋值(触发 setter 写luni块)。
直接create_pixel_layer(name="中文")会抛UnicodeEncodeError: ... 'macroman' codec。
另外psd.save()要显式传encoding="utf-8"。 psd_tools的PSDImage.composite()有 bug——只输出最底层,其余图层全丢。
实测最小复现:蓝底 + 居中纯红不透明方块,composite()返回全蓝,但各层numpy()数据正常。
这不是 PSD 坏了,Photoshop 打开正常。校验要自己按l.bbox用 PIL 手工叠加:
python
canvas = Image.new("RGBA", psd.size, (0, 0, 0, 0))
for l in reversed(list(psd)): # 列表是「顶→底」,倒过来自底向上叠
a = l.numpy() # float32 HWC, 0~1
im = Image.fromarray(np.dstack([(a[..., :3] * 255).astype("uint8"),
(a[..., 3] * 255).astype("uint8")]), "RGBA")
canvas.alpha_composite(im, (l.bbox[0], l.bbox[1]))
备选后端:火山方舟官方(图床挂掉时用)
方舟官方支持本地文件 base64 直传,不需要图床,最稳。
- 端点:
https://ark.cn-beijing.volces.com/api/v3/images/generations - 模型:
doubao-seedream-5-0-pro-260628 - Key:
.secrets/zijiekey.txt(格式apikey:ark-xxx,需剥前缀) - 请求体同智盒万象,
image字段可传data:image/jpeg;base64,... - 但仍要求像素 ≥ 262144
运行环境
- 托管 Python(无 Pillow):
C:\Users\wzx\.workbuddy\binaries\python\versions\3.13.12\python.exe - 要用带 Pillow 的 venv:
C:\Users\wzx\.workbuddy\binaries\python\envs\default\Scripts\python.exe - 依赖:
Pillow(已装)、python-pptx1.0.2(已装)、psd-tools1.20.0(已装,仅 PSD 需要) - 缺失时:
.../Scripts/python.exe -m pip install python-pptx psd-tools
商业价值提示
智盒万象可把此能力包装成「分层导出 / 可编辑 PPT」增值功能。后端接 doubao-seedream-5-0-pro-260628 + layer_decomposition,成本按图计。
注意:
1. 分层走 /v1/images/generations 且需要公网 URL(/v1/images/edits 才吃 base64 但不支持分层)。要做成产品需让代理在 /generations 上也支持 base64 透传。
2. 内容审核是硬约束:真人内衣/泳装/贴身服饰类整图会被 InputImageSensitiveContentDetected 拦截。电商平台若面向该品类,需要走「分区裁剪 + 坐标合并」的部分分层方案,或申请审核白名单。
部分分层脚本
D:\公司\AI聚合平台\AI工作区\2026-09-26-20-39-18\partial_layer.py(示例,按需复用)
—— 对审核拦截的图,分区裁剪 → 分别分层 → 坐标换算 → 面积降序合并成 PPT。
实测对内衣海报可拆出 22 个可编辑图层(文案/图标/饰品/腰腹/手臂),仅躯干核心区无法分层。