poster-to-psd一句话生产带独立图层的可编辑 PSD 海报

SKILL内容创作
内容创作 未标注权限 许可证不明确
安装这个技能

普通文生图只能得到「一张扁平图」:文字烤死、元素焊死,设计师无法二次修改。 poster-to-psd 把「一句话海报需求」变成:多图层 Photoshop 文件(背景 / 元素 / 文字各自成层).文字层支持两种: 真文字层(editable: true):Photoshop 里双击可改字 光栅化文字层(默认):不能改字,但兼容性最好,可移动 / 改色 / 加效果

一句话选型结论

按资料字段整理

poster-to-psd一句话生产带独立图层的可编辑 PSD 海报是内容创作类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类内容创作 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_3eb3e208/poster-to-psd-daq
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

内容创作
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_3eb3e208仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验未标注上游最后核验时间 · 数据更新 1791682675
正文体量55.9 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
写文章与脚本

按主题产出初稿与分镜脚本。

改稿与润色

把草稿改顺、统一语气。

多平台文案改写

同一内容按平台改成长短不同的版本。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在内容创作场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
poster-to-psd一句话生产带独立图层的可编辑 PSD 海报当前 未标注权限 未标注 未标注 未标注 有
AI 视频生成 调用外部服务 GitHub 仓库 431.3 万 Codex、Claude、Cursor 有
视频帧 联网访问 ClawHub 市场 262.1 万 OpenClaw 有
Markdown 转换器 写入文件 ClawHub 市场 135.3 万 OpenClaw 有
哔哩哔哩全合一 接触敏感数据 ClawHub 市场 64.1 万 OpenClaw 有
Remotion video toolkit 命令行 浏览器控制 ClawHub 市场 63.9 万 OpenClaw 有

SKILL.md 全文

55.9 KB
模型实际读到的说明书原文,来自技能仓库

Poster to PSD — 海报转分层 PSD 生成器

把「用户一句话描述的海报」变成「带可编辑图层的 Photoshop PSD」。
分两步,中间留一个关键提示词审阅 checkpoint(用户可控、可改词)。

触发关键词(什么情况下用我)

用户说出下列任意一类话,就应调用本 skill(命中一条即可):

类别 触发示例
直接要海报 「做一张海报」「生成海报」「设计个海报」「出张促销海报」「帮我做个活动海报」「来个科技感海报」
要分层 / 可编辑 「海报 PSD」「分层 PSD」「可编辑海报」「能在 PS 里改字的海报」「图层分开的海报」「把插画拆成图层」
参考图驱动 「把这张图里的蛋糕换成奶茶」「做类似这样的海报」「参考这张图生成海报」「照这个排版来」「按我发的图排版」
按题材 「中秋/春节/国庆海报」「国风海报」「电商促销海报」「产品宣传图」「品牌视觉」「课程封面」
转格式 「图片转 PSD」「海报转 PSD」

判定边界:凡涉及「要一张能在 Photoshop 里逐层移动/修改的海报或宣传图」,都归本 skill。纯对话问答、与海报/PSD/图层无关的需求不触发。

两步工作流

第 1 步:描述 → 完整精准关键提示词(LLM 产出,不调模型)

用户给自然语言,尽量包含:主题/用途、尺寸(如 1920x800)、风格、要写的文字、主要元素。
你产出:
1. 海报设计纲要:画布尺寸、配色、版式分区、图层规划表(每层叫什么、什么效果)。
2. ★ 完整精准的关键提示词清单(用户 2026-09-17 硬性要求,缺一不可,且必须精准):
- ① 全局风格提示词(STYLE_SUFFIX):一条,定义画质(8k/photorealistic)、光线(soft diffused studio lighting)、镜头(shallow DOF / sharp focus)、色彩倾向、构图约束、以及去背约束(isolated on PURE WHITE BACKGROUND, no shadow, no reflection, no text)。所有元素提示词都拼这个后缀,保证风格统一。
- ② 每个素材元素一条专属提示词:主视觉、每个装饰件、背景底图、每个图标,各写一条。每条必须写清:主体是什么、材质、颜色、角度/朝向、在画面中的占比或构图位置、四周留白要求(避免主体贴边导致泛洪去背失败)。
- ③ 文字设计感说明:每层文字用什么字体、什么特效(描边/阴影/渐变/底衬),不能只写"放个标题"。
3. 文案占位:标题/副标题/印章文字。
4. 落盘:写成 关键提示词.md 随交付物一起给出,元素与提示词一一对应,方便逐条改词。
> 这一步是给用户审阅、修改的「关键提示词」。用户改完回「就按这个生成 / go」进入第 2 步。
> ★ 绝不跳过:没有先给出提示词清单就直接生图 = 违反规则。

第 2 步:确认 → 生成 PSD

  1. 用 scripts/gen_asset.py 调 Agnes 生成:主视觉插画(1920x800 等目标尺寸,提示词里写 no text/no words 以免和文字层冲突)+ 各独立元素贴纸(isolated on white background)。
  2. 贴纸类素材去白底 → 透明 PNG:优先用 scripts/cut_sticker.py(从画布四边泛洪,只去外部白底、保留元素内部的白色图案——桶/瓶/盒类正面常有白色图标,全局阈值的 remove_bg.py 会把它们掏空;且只模糊 alpha、RGB 用原图,边缘不留白圈)。只有素材内部确实没有白色图案时,才退回 scripts/remove_bg.py。务必先量一下去背后的实际像素尺寸(python -c "from PIL import Image;print(Image.open('x.png').size)"),布局时用真尺寸算坐标,别凭感觉。
  3. 写一份 JSON 规格(见下),用 scripts/assemble_psd.py 拼装 PSD:背景渐变层 + 每个元素独立层 + 文字层(默认光栅化;需要 PS 双击改字时加 editable:true,见「真文字层」节) + 调色层(blend 模式)。
  4. 用 scripts/render_preview.py 出忠实预览(能正确处理真文字层,mock_preview.py 不能)。
  5. 交付:xxx.psd + 预览.png + 图层清单说明。

图层完全独立的要求(用户高频诉求,必须满足)

用户常明确要求「文字是文字、每个元素各自独立图层、背景是单独的背景色/渐变、祥云等不能和背景合并、人物单独出来」。落地做法:
- 背景用 gradient 或 color 整层铺满,绝不把任何元素烤进背景。
- 每个元素单独一次 Agnes 生成 → 单独去背 → 单独一层。
- 文字默认光栅化(独立像素层:PS 里能移动/改色/加效果,只是不能双击改字)。用户明确要「在 PS 里双击改字」时才用 editable: true。TySh 真文字层现已按真实 Photoshop 文件逐字节校正(详见文末「真文字层」节);但本机无 PS 可实测,仍建议交付真文字层 + 光栅化兜底两份。
- 交付前用 kind= 逐层打印自检,确认没有任何层是被合并的。

规格 JSON 格式(assemble_psd.py 输入)

{
  "width": 1920, "height": 800,
  "output": "poster.psd",
  "layers": [
    {"type":"gradient","name":"背景渐变","angle":"diagonal",
     "stops":[["#8f1318",0.0],["#5c0a0f",0.55],["#2a0406",1.0]],"opacity":100},
    {"type":"image","src":"moon_cut.png","name":"明月","left":1390,"top":70,"scale":0.30},
    {"type":"image","src":"cloud_cut.png","name":"金缎带(文字底衬)","left":0,"top":130,"scale":0.80},
    {"type":"image","src":"chang_e_cut.png","name":"嫦娥","left":1360,"top":100,"scale":0.46},
    {"type":"color","name":"印章底色","x":70,"y":690,"w":104,"h":104,"radius":18,
     "color":"#C0392B","opacity":95},
    {"type":"text","editable":true,"text":"中秋","name":"印章文字","font":"simhei.ttf",
     "font_ps":"SimHei","font_size":46,"color":"#FFFFFF","anchor":"center","x":122,"y":742},
    {"type":"text","editable":true,"text":"中秋节快乐","name":"主标题","font":"STXINGKA.TTF",
     "font_ps":"STXingkai","font_size":168,"color":"#7a0c12","anchor":"center","x":610,"y":388},
    {"type":"color","name":"调色-暖色叠加","color":"#FFC46B","opacity":12,"blend":"OVERLAY"},
    {"type":"color","name":"调色-光晕","color":"#FFF3D4","opacity":8,"blend":"SOFT_LIGHT"}
  ]
}

图层类型

type 用途 关键字段
gradient 独立背景层:多段渐变,numpy 渲染 stops:[[hex,pos]...]、angle: vertical/horizontal/diagonal
image 图片元素 cover:true 铺满画布;否则 left/top/scale 精确定位(图层像素就是元素本身)
text + editable:true 真 Photoshop 文字层(TySh),PS 里可双击改字;已按真实 PS 文件逐字节校正,但本机无 PS 实测,建议配一份光栅化兜底 font(PIL 测量用) + font_ps(PS 字体 PostScript 名) + font_size + color + anchor + x/y(锚点)
text(默认,不写 editable) 光栅化文字层(推荐,兼容性最好):不能改字,但能移动/改色/加效果 同上,可加 stroke/stroke_width/shadow
color 整层调色(配 blend);或给 x/y/w/h/radius 当圆角色块(印章底) color、opacity、blend
seal 红底白字印章(光栅化) text、x、y、size、color

anchor: center / left / right(决定 x/y 指哪一点)。

运行命令(跨平台 · 零手动装依赖 · 无硬编码路径)

> ✅ 依赖通过 scripts/run.py 自动安装,功能与原版一致。本 skill 自带引导器:
> 首次运行会自动在 skill 目录内建隔离环境 .venv 并装好 Pillow/psd-tools/requests
> (若存在 wheels/ 优先离线安装;本 SkillHub 发布包为控制体积不含离线大包,自动退回 PyPI 安装同一套 requirements.txt 锁定依赖),之后每次直接跑。
> 你只要设置一个变量 S(脚本目录),其余全自动。素材与 PSD 放在你自己的任意工作目录。

# —— 1) 设置脚本目录(按本 skill 实际安装位置改这一行)——
# S = <skill 根目录>/scripts
S="$HOME/.config/mimocode/skills/poster-to-psd/scripts"            # MiMo 全局 / 类 Unix
# S="$USERPROFILE\.config\mimocode\skills\poster-to-psd\scripts"   # Windows PowerShell
# SkillHub 或其它宿主安装后:把 S 指到解压出的 poster-to-psd/scripts 即可

# —— 2) 进入你的【工作目录】(素材与 PSD 都放这里,任意盘均可)——
cd /path/to/your/project        # 例:D:/hello_autumn_poster

# —— 3) 干活:所有命令统一走 run.py(首次自动建环境+装依赖,之后秒跑)——
"$(command -v python3 || command -v python)" "$S/run.py" gen_asset.py --prompt "海报主视觉, ..." --size 1920x800 --out ./assets/hero.png
"$(command -v python3 || command -v python)" "$S/run.py" gen_asset.py --prompt "..., isolated on white background, sticker style" --size 1024x1024 --out ./assets/rabbit.png
# 2a) 白底贴纸 → 去白底(阈值从 236 起试,feather 0.8~1.5)
"$(command -v python3 || command -v python)" "$S/run.py" remove_bg.py ./assets/rabbit.png ./assets/rabbit_cut.png --thr 240 --feather 1.5
# 2b) ★发光元素:同色深底生成 + 亮度色键(白底会把光晕切成硬白圈)
"$(command -v python3 || command -v python)" "$S/run.py" gen_asset.py --prompt "..., solid deep dark red background #7a1019 filling the entire frame, no white anywhere" --out ./assets/moon.png
"$(command -v python3 || command -v python)" "$S/run.py" key_dark_bg.py ./assets/moon.png ./assets/moon_cut.png --span 100 --gamma 0.9
# 3) 拼装 PSD(spec.json 用你工作目录下的路径)
"$(command -v python3 || command -v python)" "$S/run.py" assemble_psd.py ./spec.json
# 4) 忠实预览(必须用这个,mock_preview 无法渲染真文字层)
"$(command -v python3 || command -v python)" "$S/run.py" render_preview.py ./poster.psd ./spec.json ./预览.png
# 5) 结构自检
"$(command -v python3 || command -v python)" "$S/run.py" assemble_psd.py --selftest ./poster.psd
# 6) 边缘质检:白底素材去白边 / 深底素材去图层描边 / 逐层报告
"$(command -v python3 || command -v python)" "$S/run.py" defringe.py ./assets/frame_cut.png ./assets/frame_cut.png --lmin 190 --sat 22
"$(command -v python3 || command -v python)" "$S/run.py" qa_edges.py ./assets/frame_cut.png

> 也可显式一次性装好依赖(等价于首次自动安装,适合先验证环境):
> "$(command -v python3 || command -v python)" "$S/bootstrap.py" —— 仅建 .venv 并打印其 python 路径。
> 注意:.venv 是运行时生成的,不要把它打进发布包(见「发布必改」第 11 节)。

> 提示:所有 scripts/*.py 都用 __file__ 自定位,内部依赖(agnes_api.py、mock_preview.py、
> make_text_layer.py、模板 assets/engine_template.bin)全部随包分发且按相对路径解析,
> 不依赖其它 skill、不依赖特定盘符。直接 cd 进 scripts/ 目录运行同目录脚本也一样可用。

★ 发光元素抠图:白底必败,深底色键才对

这是本次最大的坑。 明月、灯笼这类带发光光晕的元素,用「白底生成 + remove_bg.py」必然留下一圈硬白边:光晕是从亮到白的连续渐变,白色阈值一截就是断崖。

正确做法(已验证):
1. 提示词里把背景指定成和海报主色一致的颜色,例如 solid deep dark red background #7a1019 filling the entire frame, no white anywhere, no text。
2. 用 key_dark_bg.py 做亮度色键:alpha = clip((亮度 − 背景亮度) / span)。光晕的渐变被完整保留成平滑半透明,叠回深红底上无缝。
3. span 必须先量再定:测背景亮度 L_bg 和主体最暗处亮度,让「主体最暗处刚好 ≈ 全不透明」。跨度定小了会把背景也带上 alpha(曾出现 98% 像素被保留);定大了主体会变半透明。本次实测:明月(背景 L=20,主体 L=190)用 span=100;祥云(L=43 → 198)用 span=60;灯笼本身偏暗红(L=37 → 114)只能 span=45。
4. 加反预乘(rgb = (pixel − bg*(1−a)) / a)去掉半透明像素里混入的背景色,否则边缘带红边。
5. 验证指标:四角区域平均 alpha 应 < 10/255,且半透明像素占比要小(祥云 0.3%、灯笼 4.8% 属正常)。
6. --gamma 是控制光晕扩散的旋钮:默认 0.85~0.9(<1)会放大弱光晕——大面积 L 40~60 的淡光晕被一起留下,明月实测保留 86.4% 像素、整幅泛白。想收紧就设 --gamma 1.5(>1 抑制弱值):同一张明月降到 49.2%、四角 alpha=0.00,光晕收成干净的月晕。先按默认跑一遍看覆盖率,>60% 就调大 gamma。

顺带的好处:深底生成的元素边缘也顺带更干净——白底贴纸风格会自带一圈白色描边(sticker outline)+ 投影,叠在深红底上很明显;深底生成时直接在提示词里加 no white border, no sticker outline, no drop shadow,出来的元素边缘直接是干净的。

★ 深色主体(毛笔字/墨迹)去白底:反向亮度键 key_light_bg.py

上面两种键控都假定主体比背景亮(亮主体 + 暗底)。毛笔书法、墨迹、深色剪影是反过来的:主体深、背景白。
- ❌ 用 key_dark_bg.py(亮主体)→ 主体全被切掉。
- ❌ 用 remove_bg.py 全局阈值 → 把枯笔、飞白、淡墨的中间过渡层一刀切成硬边,失去毛笔的柔边质感。
- ✅ key_light_bg.py(本次新增,已验证):alpha = clip((背景亮度 − 像素亮度) / span)。墨越浓 → alpha 越接近 1;白底 → 0;中间灰(飞白)→ 平滑半透明。
bash python scripts/key_light_bg.py in.png out.png --span 200 --floor 18 # span 先量:背景亮度 L_bg(白底≈250)与最浓墨处亮度,让最浓处刚好≈全不透明
同样带反预乘(去白边)。实测毛笔「秋分」两字:内部近白 0.0%、柔边 2.0%,飞白完整保留。
- 判断法:主体亮度中位数 < 背景亮度 → 走 key_light_bg.py;反之走 key_dark_bg.py。

★ AI 把主体抠好了、却自作主张加了一圈「彩色光晕」

AI 生成的白色/浅色主体(白兔、白花、白云)常在主体外围补一圈暖色光晕(如奶橙色),单独看很美,但叠到海报上会盖住后面的元素(实测:白兔的橙晕把背后的虚焦大柿子糊掉)。
- 判据:主体本身近中性(R≈G≈B,spread = max−min 小),光晕是有彩色(spread 大)。
- 做法:按「饱和度」给 alpha 加权——近中性保留、有彩色滤掉:
python spread = rgb.max(2) - rgb.min(2) w = 1.0 - np.clip((spread - 40.0) / 80.0, 0, 1) # spread&lt;40 保留;&gt;120 全滤 alpha = alpha * w
实测白兔橙晕残留从 1% → 0.1%,兔毛(近中性)无损失。
- ⚠️ 别滤过头:若主体本身是有色的(粉兔、黄花),这个权重会连主体一起削 → 只对近中性主体用;主体有色的光晕改用「按半径的径向 alpha 衰减」或直接把光晕区裁剪掉。

★ 复刻「水彩虚化」参考图:AI 出的锐利主体要主动柔化

参考图是水彩/虚焦风格时(例:秋分海报里的虚化白兔、虚焦大柿子),AI 出的主体往往是锐利卡通风格,直接叠上去风格突兀。
- 做法:对素材做 GaussianBlur(10~18) + alpha 乘 0.82~0.9(降不透明度),变成「幽灵状/水彩晕染」质感,与参考图的虚化感对齐。
- 程序化虚焦大色块(参考图里那团虚化柿子/光斑):用 numpy 生成径向渐变椭圆 → GaussianBlur(20~30),核心区保持不透明(rr&lt;0.7→alpha=1)、边缘才柔化,比从头 AI 生成更可控、颜色能精确对齐参考图。
- 核对方法:分区块算 mean / orange% / dark%,与参考图同区块对比。实测秋分中部大柿子区块成品与参考图均色完全一致 [245,173,127]。

★ 商品海报:AI 投影必须用「中性亮色」算法清除

AI 生成产品图(礼盒/实物)几乎一定会附带一块投影,而且提示词里写 no drop shadow 也挡不住(实测重生成仍带)。
- 投影的特征:淡灰、低饱和(max−min < ~24)、亮度中高(L 140~238)。
- 为什么阈值法不行:它是渐变的——remove_bg.py --thr 240 只能削掉外层 240+ 的部分,靠近产品的 200~238 灰带会留下(甚至因恰好 <240 而整块不透明残留);调高 --thr 又可能啃掉产品本身的浅色部位。
- 正确做法:投影是「低饱和中性亮色」,产品是「高饱和暖色」,两者在饱和度上天然分离。直接清零 alpha:
python spread = rgb.max(2) - rgb.min(2); lum = rgb @ [0.299, 0.587, 0.114] a[(spread &lt; 24) &amp; (lum &gt; 140)] = 0 # 只杀中性亮像素;金色月饼/奶油内衬(spread 大)安全
实测一步清干净,肉眼与 qa_edges.py 均通过。
- 顺位:remove_bg.py 去白底 → defringe.py 去白边 → 再跑这个中性亮色清零。
- ⚠️ 反例:白/浅色主体千万别用这招。如果主体本身就是白或浅色中性(白玉兔、白瓷盘、奶油蛋糕、云朵),它和「中性亮色投影」在饱和度上无法分离——上面这步会把主体自己一起清零。实测:白玉兔跑完只剩耳朵内粉、手里月饼和一圈描边,身子全没了。
此类素材改用「纯黑底生成 + 深色键」:提示词写 isolated on a solid pure black background filling the entire frame, no shadow, no ground shadow,主体亮、背景黑(亮度天然分离),再 key_dark_bg.py --span 200 --gamma 1.0 --edgefade 24 --floor 8,一次得到边缘干净、主体完整的抠图。判断法:主体亮度中位数是否落在 140~240 且饱和 <24?是 → 走黑底深色键,不要走中性亮色清零。

★ 把一张「扁平插画」重建为分层 PSD(用户说"我要的是图层"时)

用户常先要一张扁平插画(文生图),再补一句「没有图层,我要的是图层」。此时不要试图去抠那张成品图——前景/背景往往是同色系(例:绿叶前景 + 绿色背景),无法分割。正确做法是按图层需要,分别重新生成素材:

  1. 逐层生成,而不是逐层抠:背景一张、主体一张(白底或黑底贴纸风)、前景元素一张。生成时就指定好画布同比例的尺寸(如竖版用 --size 1024x1536),避免 cover 铺满时被拉伸变形。
  2. 主体底色要选对:浅色/暖色主体(人像肤色)用纯白底 + remove_bg.py;若生成出来是"暖奶油渐变底"(很常见),直接抠会啃掉浅色皮肤 → 重新生成为 isolated on pure white background, sticker style 再抠。
  3. "谁在谁背后"用图层顺序解决,不用抠遮挡:想让「人脸在绿叶背后」,就把女孩层放下面、叶簇层放上面,叶子自然盖住脸——不需要把被遮住的脸补全。
  4. 小元素(蚂蚁、蜜蜂、碎石)单独成层:按「中性深色」判据提取 —— ants = (a&gt;100) &amp; (lum&lt;95) &amp; (spread&lt;48);提取后把原图上该位置用周围颜色重模糊填平(GaussianBlur(6) 取色回填),这样隐藏小元素层时底图上不会有黑点孔洞。
  5. 必做校验:把小元素层与底图层 alpha_composite 叠回去,与原图逐像素比,平均色差应 ≈ 0.00;若不为 0 说明遮罩漏了或多了。
  6. 再写 spec → assemble_psd.py → render_preview.py。注意:初版前景元素常"位置偏低、没压住主体",预览后要把前景层上移放大,才有"从后面探头"的遮挡感。
  7. 文字层:TySh 真文字层现已做到与真实 Photoshop 逐字节一致(见下文「真文字层」节的字节级基准),结构上不再有已知缺陷;但本机无法启动 PS 验证是否闪退,所以默认仍是双份交付(真文字层 + 光栅化兜底)。
  8. ★「人物手持道具」必须让道具独立成层,别指望一次生成就分层:直接写「男生手里拿着某物」,模型几乎必然把道具烤进人物层(实测:写"holding a small book"→ 生成结果人物已抱着一本书,道具无法单独移动)。正确做法:人物层单独生成,提示词里显式写 both hands cupped together in front, holding nothing, empty hands, no book, no object between the hands,得到"双手空托"的姿势层;道具另行生成(发光道具走黑底 + key_dark_bg.py,普通道具走白底 + remove_bg.py)→ 各自裁到 bbox → 在 spec 里用 left/top/scale 把道具层叠到手掌位置。实测这样人物 + 道具是两张干净独立层。
  9. 生成尺寸可能被"就近吸附"到受支持的画幅:请求 --size 1024x1280(4:5)实测返回 864×1152(3:4)。所以生成后第一件事是量实际像素(Image.open(f).size),并以实际背景尺寸作为画布尺寸(背景 cover 才不会拉伸),别拿请求尺寸去算坐标。

边框定位:先量像素再排文字

金边框之类的装饰层,不要凭感觉留边。用 numpy 扫一遍边框素材的 alpha 行分布,算出「双线在哪一行 → 放大到画布后对应 y」,再反推文字安全区。本次实测:边框 1568x672 底部双在线源图第 606-627 行 → 拉伸到 800 高后是 y=721-747,所以所有文字底部必须 ≤ 715。

排文字前先量文字盒:ImageDraw.textbbox() 实测 STXINGKA.TTF 122px &quot;中秋节快乐&quot; = 610x121、simhei.ttf 32px &quot;花好月圆 · 阖家团圆&quot; = 320x31,拿真数做竖轴预算,别估。

★ 白底贴纸去背(cut_sticker.py):内部有白色图案时,别用全局阈值

remove_bg.py 是全局阈值——凡 R/G/B 同时 ≥ thr 的像素统统置透明。
对「内部本身就有白色图案」的素材(垃圾桶正面的回收箭头、警示三角、苹果果肉,
瓶子/盒子上的白色图标或文字牌),它会把内部白色一起掏空,成品背景直接透出来。

正确做法:用 scripts/cut_sticker.py(从画布四边泛洪)

python $S/cut_sticker.py bin.png bin_cut.png --thr 232 --feather 1.0 --erode 2

它一次解决两个坑:

坑 症状 解法
全局阈值掏空内部白色 桶正面白色图标变透明,透出背景 只泛洪「与画布四边连通的近白」,被深色轮廓包住的内部白色自动豁免
边缘残留一圈白色描边 RGBA 整体高斯模糊,把背景白色的 RGB 混进了边缘像素 ① 保留区先腐蚀 1~2px,切掉混了背景色的外层;② 只模糊 alpha 通道,RGB 一律用原图

自检:脚本会打印「外部白底置透明 px | 内部保留白色 px」(后者应远大于 0);
再统计边缘带里「不透明且偏白(亮度>200)」的占比,正常应接近 0%
(残留若偏高,先确认是不是主体自身的浅色高光,例如浅绿桶身的高光条)。

⚠️ 纯白元素不能放在白底生成(会整个消失)

白底贴纸里若元素本身就是纯白(白云、白花、白瓷盘、白纸牌),cut_sticker 会把它的白色
当成「外部白底」一起清掉 —— 元素整个消失(实测:纯白云朵去背后报「内部保留白色 0 px」,
只剩一圈若有若无的边)。判据:素材主体是不是「纯白 + 无描边」?是 → 必须重生成,
提示词给它一个有色填充或描边(如 soft sky-blue cloud with a slightly darker blue outline),
去背后主体才完整、边缘才干净。

⚠️ 主体就是奶白/瓷盘(食品类)去边别用 defringe 直接删 alpha

包子/馒头/白瓷盘装食物这类主体本身亮白,和"白边"判据相同,defringe.py 的"低饱和亮点删 alpha"
会把主体一起删掉。正确做法(本次包子海报实测):
1. 生成用黑底(agnes 黑底 + key_dark_bg.py 键控):白盘主体在黑底上是亮白、边缘只剩半透明光晕;
2. 去光晕用"最近内部实心色填充":scipy distance_transform_edt(~solid, return_indices=True)
取最近盘面色填进半透明/白圈像素,保留奶白主体、只清外圈光晕——绝不用"删 alpha",否则主体被掏空;
弱化白圈可把该带 alpha 乘 0.7~0.75。
3. 裁顶部蒸汽:AI 生图常在食物顶上画上升蒸汽/烟雾,在红/深底上呈灰雾 → 直接裁掉
(本次 im.crop((0,160,W,H)) 最干净);别用"最近色填充"(会和底色不一致、留下可见矩形色块)。

⚠️ AI 没给白底、反而生成「深蓝近黑底」→ 改用 cut_dark.py(2026-09-17 科技海报)

科技/夜景/发光题材里,提示词即使写了 PURE WHITE BACKGROUND,AI 也可能整张生成深蓝近黑底
(本次 globe、icon_smart 两张就中了)。此时 cut_sticker 的四边白底泛洪完全失效——
边缘没有近白像素 → keep 全 True → 整张不透明,出现黑方块。
判据:cut_sticker 打印「外部白底 0 px 置透明」=中了这个坑。
对策:改用 scripts/cut_dark.py——只抠「蓝通道占优且整体偏暗」的像素,明亮霓虹线条(青 #22D3EE / 紫 #A855F7)自动保留:

python $S/cut_dark.py neon.png neon_cut.png --max_thr 72 --feather 1.0 --erode 1

细线条 --erode 别超过 1,否则会把发光线蚀断。

★ 边缘质检三连(去背之后必做)

去背 ≠ 干净。三份脚本,按顺序用:

# A) 白底贴纸素材:压掉边缘的中性白边(保留金色高光/粉色主体)
python $S/defringe.py v5_frame3_cut.png v5_frame3_cut.png --lmin 190 --sat 22
# B) 深底色键素材:清掉图层边界残留的半透明&quot;方框描边&quot;(见下)
python $S/key_dark_bg.py moon.png moon_cut.png --span 100 --gamma 0.9 --edgefade 32 --floor 10 --trim-edge 4
# C) 出图后复查:逐层报「中性白边 / 底色残留 / 主体内近白像素」占比
python $S/qa_edges.py v5_frame3_cut.png

三个新增参数(key_dark_bg.py):

参数 作用 何时用
--edgefade N 把 alpha 乘以「到图片边缘的距离」蒙版,让 alpha 在图层边界自然归零 光晕素材。光晕外沿的极低 alpha(1~15/255)会渲染成一条隐约的矩形描边,贴在渐变底上肉眼可见。明月加 --edgefade 32 后,边缘 8px 带平均 alpha 从 11.84 降到 0.00
--floor N alpha × 255 < N 的像素直接置 0 配 edgefade 一起用,清掉归零前的最后一点尾数
--trim-edge N 删掉「贴在最外 1 圈、但向内 N 像素已透明」的孤立像素 AI 生图会在素材最外圈画一条满亮度线(灯笼素材最右列 1152/1152 像素全不透明,RGB≈(180,71,72)),叠到海报上就是一道竖缝。trim 后灯笼删掉 1151 个孤立像素、祥云 734 个

三种色键模式(--mode)—— 按主体与底色的明暗关系选:

模式 alpha 计算 适用场景
luma(默认) clip((亮度 − 底色亮度) / span) 主体比底色亮。明月、金线波浪、祥云、金色灯笼
dist clip(色距 / span),色距 = √Σ(RGB 差²) 主体比底色暗且底色干净均匀。深棕枝条 L≈45 vs 底色 L≈32
wb clip(((R−B) − axis) / span) 主体比底色暗且底色带渐变/噪点。dist 会因底色不均而全图糊一层半透明

wb(暖蓝差)模式实测最实用:强蓝底色 R−B 为负(−76),暖色主体(棕枝/黄花/橙果/绿叶)R−B 为正(枝条 +51),两簇之间断层很干净。基线 axis 默认取 (R−B) 的 p75(背景簇上沿),也可 --axis 手动指定;该模式下背景色改由「最蓝的深底像素」重估,四角均值会被主体污染。

--bg R,G,B 必用场景:素材被裁过、四角不再全是底色时(例如祥云裁掉装饰金框后,底部四角是云朵),四角均值估出的底色会是奶油色(L≈172)而不是天空紫(L≈18.6),span 全部算错。显式传 --bg 30,4,64 即可。

AI 生图会在素材四周画装饰金框:祥云素材被模型加了顶/底/左/右金线框(顶 y18-21、底 y645+、左 x19-24、右 x1543-1548)。用逐行/逐列亮度扫描定位后 crop 掉,别靠肉眼估。裁完再用 --bg 传真实底色。

qa_edges.py 的判定必须带饱和度门槛:先测过用 R&gt;225 &amp; G&gt;225 &amp; B&gt;215 判"白边",结果把金边框的高光(RGB 235,226,188,R−B=47)误报成白边,47.6% 的边框边缘被标记。正确判定是「亮 且 低饱和」:(亮度 &gt; 195) &amp; (max−min &lt; 24)。金色高光、粉色山峦顶都会自动豁免。

别漏反预乘:色键后必须 rgb = (pixel − bg*(1−a)) / a,否则半透明边缘混着底色,叠在别的层上会有色偏。

★ 缝线扫描的误判(血泪教训)

对整图做逐列/逐行亮度梯度扫描找"图层边界伪影"时,梯度尖峰不一定是你要找的层。

实例:扫描报出 x=1211 处梯度 5.14(邻近均值 1.15,比值 4.47),位置落在「明月」层 bbox(x=704..1216)内,一度怀疑是明月渐隐没做干净。逐层拆解才发现:明月在该点 alpha 严格为 0,真正的来源是「底部山峦波浪」层的山脊线穿过该处——山峦在明月之上叠着、明月 alpha 为 0,山峦的粉脊上沿就露出了。

正确排查顺序:逐层把该点的 (alpha, RGB, 等效亮度 = L*a + L_bg*(1-a)) 打出来,看哪一层在贡献,再决定改谁。不要凭 bbox 范围猜。

★ 透明杯 / 塑料杯去背:杯壁会被泛洪「掏空」(2026-09-17 草莓啵啵奶茶海报)

症状:白底生成的透明塑料杯,去背后杯壁整圈消失,只剩中间的液体和珍珠,像"液体悬浮在空中";
qa 看起来四边 alpha=0 很干净,但杯子不成形。
原因:透明杯壁的像素本身就是近纯白,与白底同色 → 四边泛洪直接穿透杯壁进到杯内。
解法(按优先级):
1. 提示词里给杯壁一个颜色:frosted translucent cup wall with a light pink tint so the cup silhouette stays clearly visible + avoid a floating liquid look。杯壁一旦带淡粉(R>G>B),泛洪阈值就吃不掉它。
2. 仍被穿透时,用「杯身轮廓外推 + 水平镜像」修:
- 用杯身下部完好行的右边界 R(y) 做一次线性拟合,按锥形趋势 Wid = Wmed + (Y_REF1-y)*0.11 反推左边界 Ls = Rs - Wid;
- 对 y ∈ [杯口下, 杯底]、x ∈ [Ls-8, Rs+8] 的空虚处先做镜像填充(镜像位置也要是实心的才采用,否则 cv2.inpaint(radius 12) 兜底);
- 最后把左半边整体替换成右半边的镜像(只在杯身宽度内)→ 左侧白带彻底消失且左右对称。
- 别用「大半径 inpaint」硬吃宽白带:mask 外一圈全是白时,inpaint 会把白色从另一侧又传播回来。
3. 杯底那道浅灰横带是 AI 投影:把杯底范围放宽(Y_CUP1 取 0.93×h),再用「低饱和亮色清零」清掉。

★ 「低饱和亮色清零」的禁区(同批)

a[(spread&lt;28) &amp; (lum&gt;130)] = 0 这招清 AI 投影很好用,但主体本身是白色/奶白时会连主体一起清。
实测:奶茶整杯跑完 → 奶油顶整块消失、透明杯壁也没了。
判据:素材里有白色/奶白实体(奶油、奶泡、白瓷、云朵)→ 该层禁用这招;
只对「有色主体 + 浅灰投影」的素材(草莓、樱桃、有色水果)使用。

铁律(血泪经验,必须遵守)

  • 必须透明图层(用户 2026-09-17 最终要求):所有 AI 生图素材键控去底后保持透明底,不能带白边或其他元素残留,每个元素都是独立的透明 PNG 图层。
  • 全元素 AI 生成 + 文字设计感(2026-09-17 新增):海报里每一张图、每一个图案/装饰件/图标都必须用 AI 生图生成,严禁用 PIL 程序画像素图作为画面主体元素。文字不能只是干巴巴放上去的标准字体 —— 必须有设计感(艺术字特效、渐变/描边/发光、装饰背景等)。无论是否有参考图,这条每次执行。
  • 键控必须彻底,边缘干净,无背景色残留。
  • PSD 必须 PSDImage.new(&quot;RGBA&quot;, (W,H)),绝不用 RGB(否则透明变蒙版)。
  • 不透明度 0-255,不是 0-100;op(pct)=round(pct*255/100)。
  • psd.composite() 忽略 opacity/blend/group —— 预览一律用 render_preview.py,自检直接读 layer.opacity/blend_mode。
  • 图层组不能设 position。

真文字层(make_text_layer.py)— EngineData 必须"抄真 PS 的模板"

> ### ★★★ 2026-09-16 结案:PS 26.0 闪退的真凶是 EngineData(不是 TySh 外壳)
>
> 用户在 Photoshop 26.0 上实测:editable:true 的版本打开即闪退,光栅化兜底版正常。
> 因为两版只差 TySh,所以问题锁定在文字层;再逐字段比对用户桌面上真 PS 文件(PS 2021 写出,
> Desktop/原来桌面/未标题-1.psd 等)后,定位到旧版 3 处硬缺陷:
>
> | # | 缺陷 | 真 Photoshop | 旧版 |
> |---|---|---|---|
> | 1 | EngineData 结构残缺(根因) | EngineDict 7 键:Editor / ParagraphRun / StyleRun / GridInfo / AntiAlias / UseFractionalGlyphWidths / Rendered;ResourceDict 12 键;顶层还有 DocumentResources | EngineDict 2 键;ResourceDict 8 键;无 DocumentResources |
> | 2 | 颜色值顺序反了 | Values = [alpha, R, G, B] | [R, G, B, alpha] |
> | 3 | 正文结尾 / 索引 | Editor.Text 结尾必带段落符 \r;TextIndex = 0 | 无 \r;TextIndex = 1 |
>
> 颜色顺序的实证方法(别猜,用像素反推):真 PS 文字层自带光栅化字形像素,
> 量它 bbox 内不透明像素的中位色,与 EngineData 的 FillColor.Values 对齐:
> &gt; 学校名 Values[1.0,0,0,0] -&gt; 实测 (0,0,0) =&gt; 解释 [A,R,G,B] &gt; 金字 Values[1.0,1.0,0.69966,0.17647] -&gt; 实测 (255,178,45) &gt; 红字 Values[1.0,1.0,0,0] -&gt; 实测 (255,0,0) &gt;
>
> 修法(现行实现,勿退回手搓):不再凭文档手搓 EngineData。用
> scripts/extract_engine_template.py 从真 PS 文件里抽一份原生 EngineData 当模板
> (assets/engine_template.bin,8481 字节),生成文字层时只改 4 处:
> 正文(Editor.Text = 文本 + \r)、字号、颜色、字体(FontSet[0].Name),
> 其余结构 100% 保持 Photoshop 自己的原样。所有键名兼容 str/bytes(_key()/_get()/_put())。
> make_type_layer(..., engine_mode=&quot;legacy&quot;) 可切回旧写法,仅用于复现闪退做对照。
>
> 交付约定:用户要"文字就是文字"时,一次给两份:
> xxx.psd(editable:true 真文字层)+ xxx_光栅化兜底.psd(保证能打开)。
> 两份 spec(spec_xxx.json / spec_xxx_raster.json)内容一致、只差文字层写法。
> 若真文字层仍崩,退路是给用户一个 .jsx 脚本:在 PS 里 文件 → 脚本 → 浏览 执行,
> 由 Photoshop 自己把文字层建出来 —— 这条绝对不崩,只是需要用户点一下。
>
> 本机没有 Photoshop,无法实测。所以每次改文字层后,都要产出「探针 PSD」让用户点一下验证,
> 别等做完一整张海报才发现还崩。探针生成脚本参考 D:/anti_bully/probe/make_probes.py:
> ①新版写法 ②新版去掉 Rendered ③把真 PS 文件用 psd-tools 原样重存(判定容器是否兼容)。

> 用户明确要求「文字就是文字、不要变成图层」时:不要争论兼容性——照做,一次给两份
> (见上一节「交付约定」)。
>
> 真文字层的描边:真文字层不能"画"描边,必须写进 EngineData 样式数据。
> make_type_layer(..., stroke_rgb=(r,g,b) 0..1, stroke_weight=px) 写 StyleSheetData 的
> StrokeColor / StrokeFlag / OutlineWidth;assemble_psd.py 的 editable 分支自动从 spec
> 读 stroke + stroke_width 传入。

TySh 字节级基准(真实 Photoshop 实测,勿改)

TySh.version=1  text_version=50  warp_version=1  left/top/right/bottom = 0,0,0,0
Transform      = (1, 0, 0, 1, em_left, em_top + ascent)      # 1:1 缩放 + 文档坐标平移
text_data      = DescriptorBlock(version=16, classID=&#x27;TxLr&#x27;)  # 恰好 8 项
  &#x27;Txt &#x27;             String        &#x27;文字\x00&#x27;                  # 必须带终止符
  &#x27;textGridding&#x27;     Enumerated    typeID=&#x27;textGridding&#x27; enum=&#x27;None&#x27;
  &#x27;Ornt&#x27;             Enumerated    typeID=&#x27;Ornt&#x27;         enum=&#x27;Hrzn&#x27;
  &#x27;AntA&#x27;             Enumerated    typeID=&#x27;Annt&#x27;         enum=&#x27;antiAliasSharp&#x27;
  &#x27;bounds&#x27;           Descriptor    classID=&#x27;bounds&#x27;       {Left,Top ,Rght,Btom}
  &#x27;boundingBox&#x27;      Descriptor    classID=&#x27;boundingBox&#x27;  {Left,Top ,Rght,Btom}
  &#x27;TextIndex&#x27;        Integer       0        # ★ 真 PS 是 0,写 1 会出问题
  &#x27;EngineData&#x27;       RawData       &lt;Adobe engine-data bytes&gt;
warp           = DescriptorBlock(version=16, classID=&#x27;warp&#x27;)  # 恰好 5 项
  &#x27;warpStyle&#x27;            Enumerated typeID=&#x27;warpStyle&#x27; enum=&#x27;warpNone&#x27;
  &#x27;warpValue&#x27;            Double 0
  &#x27;warpPerspective&#x27;      Double 0
  &#x27;warpPerspectiveOther&#x27; Double 0
  &#x27;warpRotate&#x27;           Enumerated typeID=&#x27;Ornt&#x27;      enum=&#x27;Hrzn&#x27;
  • bounds 是文本本地 EM 盒(基线 y=0):x: 0→W,y: -ascent→descent(descent 取负值)。
  • boundingBox 是墨迹盒在本地坐标下的位置(用 PIL getbbox() 得)。
  • 所有 Descriptor 的 name 字段必须是 &quot;\x00&quot;,不能是 &quot;&quot;——这是 Photoshop 的怪癖,
    写成空串会让 warp 序列化多/少 1 字节,无法与真实文件逐字节对齐。这是最容易漏的一步。
  • TextIndex 必须是 Integer 型,EngineData 必须是 RawData 型。
  • 不要写 substitutesUsed:12 个真实文字层里只有 1 个有它,规范形态是 8 项(不加)。

手写 TySh 的 5 个坑

  1. ResourceDict 在 EngineData 顶层,不在 EngineDict 里面。放错 → typesetting._build_fonts 报 AttributeError: &#x27;NoneType&#x27;。
  2. 图层名不能在建层时传中文:LayerRecord.name 是 macroman 编码,传中文直接 UnicodeEncodeError。先传 ASCII 名建层,再 layer.name = &quot;中文&quot;(setter 把 record 写成 ?、真串写进 luni unicode 标签)。
  3. 别用 tagged_blocks.set_data():它会走校验,text_version 默认 1 而校验器只认 50。直接赋值 layer._record.tagged_blocks[Tag.TYPE_TOOL_OBJECT_SETTING] = TaggedBlock(key=..., data=ttsh)。
  4. pixel_data_irrelevant 必须是 False(不是 True):真实 Type 层仍带一层空白像素通道。写成 True 会让 PS 当作无数据层处理。
  5. engine_data 模块有自己的 Bool,和 psd_tools.psd.descriptor.Bool 不是同一个类。样式数据里的布尔值要用前者,混用会序列化失败。
  6. engine_data.String / Integer / Float / Bool 都不是 Python 内建类型的子类(继承自 ValueElement):
    它们没有 len()、没有 endswith(),且 str(x) 返回的是带引号的 repr。
    取值一律用 getattr(x, &quot;value&quot;, x),否则校验会误报(已踩:以为 \r 丢了,其实是 str() 把引号也带出来了)。

EngineData 一律走模板,不要手搓。真 PS 的结构是这样(Adobe 标记法文本,UTF-16BE 字符串带 BOM):

&lt;&lt;
  /EngineDict                             # 7 键
    /Editor      &lt;&lt; /Text (…正文…\r) &gt;&gt;   # 结尾必须有 \r
    /ParagraphRun  &lt;&lt; /DefaultRunData … /RunArray [ 段落属性 ] /RunLengthArray [ n+1 ] /IsJoinable 1 &gt;&gt;
    /StyleRun      &lt;&lt; /DefaultRunData … /RunArray [ run0 run1 ] /RunLengthArray [ n 1 ] /IsJoinable 2 &gt;&gt;
    /GridInfo /AntiAlias /UseFractionalGlyphWidths /Rendered   # Rendered 是通用空壳,不是字形缓存
  /ResourceDict                           # 12 键
    /KinsokuSet /MojiKumiSet /TheNormalStyleSheet /TheNormalParagraphSheet
    /ParagraphSheetSet /StyleSheetSet /FontSet /Superscript*(4) /SmallCapSize
  /DocumentResources                      # 与 ResourceDict 同构的另一份
&gt;&gt;
  • StyleRun.RunLengthArray = [n, 1]:真 PS 是「正文一个 run + 段落符单独一个 run」,两个 run 同样式。
  • 样式键只能从 PS 认识的那 34 个里挑(Font/FontSize/FauxBold/FauxItalic/AutoLeading/Leading/
    HorizontalScale/VerticalScale/Tracking/AutoKerning/Kerning/BaselineShift/FontCaps/
    FontBaseline/Underline/Strikethrough/Ligatures/DLigatures/BaselineDirection/Tsume/
    StyleRunAlignment/Language/NoBreak/FillColor/StrokeColor/FillFlag/StrokeFlag/FillFirst/
    YUnderline/OutlineWidth/CharacterDirection/HindiNumbers/Kashida/DiacriticPos)。
    不要写 StrokeWeight / StrokeOffset / FontSynthetic —— 真 PS 的 StyleSheetData 里没有这几个键,
    描边宽度用 OutlineWidth。
  • psd_tools.psd.engine_data 里的类名是 Integer(不是 Int)、Float、Bool、String、Dict、List;
    顶层键是 str,嵌套键可能是 bytes,所以取值要 _key()/_get()/_put() 两边都试。
  • 抽模板:python scripts/extract_engine_template.py &lt;真PS文件.psd&gt; &lt;输出.bin&gt; [文字内容]。

验证方法(交付前必跑)

1) TySh 外壳:拿任意一份真实 Photoshop PSD 当基准,比对 warp 序列化字节:

import io
from psd_tools import PSDImage
from psd_tools.constants import Tag
def warp_hex(path):
    for L in PSDImage.open(path).descendants():
        if L.kind == &quot;type&quot;:
            b = io.BytesIO()
            L._record.tagged_blocks[Tag.TYPE_TOOL_OBJECT_SETTING].data.warp.write(b)
            return b.getvalue()
assert warp_hex(&quot;真实PS.psd&quot;) == warp_hex(&quot;产物.psd&quot;)

2) EngineData 结构(新版必查,这是 PS 26 闪退的根因所在):

from psd_tools import PSDImage
from psd_tools.constants import Tag
def _k(d,n):
    for k in (n, n.encode()):
        if k in d: return d[k]
    raise KeyError(n)
def _txt(x):                       # engine_data 的 String 不是 str 子类!
    return getattr(x, &quot;value&quot;, x)  # str(x) 会连引号一起返回,必须取 .value
for L in PSDImage.open(&quot;产物.psd&quot;).descendants():
    if L.kind != &quot;type&quot;: continue
    td = L._record.tagged_blocks[Tag.TYPE_TOOL_OBJECT_SETTING].data.text_data
    ed = td[b&quot;EngineData&quot;].value
    eng = _k(ed, &quot;EngineDict&quot;)
    ssd = _k(_k(_k(_k(eng,&quot;StyleRun&quot;),&quot;RunArray&quot;)[0],&quot;StyleSheet&quot;),&quot;StyleSheetData&quot;)
    fv  = [_txt(x) for x in _k(_k(ssd,&quot;FillColor&quot;),&quot;Values&quot;)]
    assert len(list(eng.keys())) &gt;= 6, &quot;EngineDict 必须 6~7 键(去 Rendered 实验时为 6)&quot;
    assert len(list(_k(ed,&quot;ResourceDict&quot;).keys())) == 12, &quot;ResourceDict 必须 12 键&quot;
    assert len(list(ed.keys())) == 3, &quot;顶层必须 EngineDict/ResourceDict/DocumentResources&quot;
    assert _txt(_k(_k(eng,&quot;Editor&quot;),&quot;Text&quot;)).endswith(&quot;\r&quot;), &quot;正文结尾必须带 \\r&quot;
    assert _txt(td[b&quot;TextIndex&quot;]) == 0, &quot;TextIndex 必须 0&quot;
    assert fv[0] == 1.0, &quot;颜色必须是 [alpha,R,G,B]&quot;
    print(&quot;EngineData 结构 OK&quot;, L.text, fv)

通过标准:上面两条全过 + text_data 8 项 + warp 5 项 + version/text_version/warp_version = 1/50/1
+ l=t=r=b=0 + pixel_data_irrelevant is False。
> ⚠️ pixel_data_irrelevant 在 layer._record.flags.pixel_data_irrelevant 上,
> 不在 TySh 的 data 里 —— 写成 tt.data.pixel_data_irrelevant 会 AttributeError(已踩)。
3) 探针实测(唯一能确认"PS 真的不崩"的办法,本机没有 PS):生成 2~3 个小探针 PSD 交用户点开,
参考 D:/anti_bully/probe/make_probes.py。

布局定位陷阱(双偏移)

image 非 cover 模式下,图层像素就是缩放后的元素本身,靠 layer.left/top 定位即可。如果把元素先贴到整张透明画布上再设 left/top,元素会落到 (2×left, 2×top) —— 表现为「人物/元素整体飞出画面」。这是 v3 版人物消失的根因。

⚠️⚠️ 漏写 scale = 所有素材按原始尺寸(1024×1024)粘贴(2026-09-17「你好秋天」海报,排版全乱的真凶)

assemble_psd.py 的 image 层只有 spec 里给了 scale 才缩放(if &quot;scale&quot; in layer:),
否则直接把素材原图贴上去。而生成的素材多是 1024×1024 画布、内容只占中间一小块,
于是「素材按 1024px 贴、left/top 却按缩小后的尺寸算」→ 尺寸全错、位置全偏。
外在表现就是用户说的「排版很乱」「某元素变成一大团糊在画面中间」
(本次「胶囊底衬」就这样变成正中一团 1024px 的淡金色大块)。
排查口诀:预览里出现"不该那么大的元素"或"元素集体偏移",先查 spec 的 image 层有没有 scale。

★★ 复刻参考图版式的正解:按素材「实际内容框」(ink bbox) 对位

素材四周都有大片透明留白,按画布尺寸缩放定位必然偏。正确做法是先量 ink bbox 再算 scale:

a = np.asarray(Image.open(p).convert(&quot;RGBA&quot;))[:, :, 3]
ys, xs = np.nonzero(a &gt; 12)                     # 素材真正有像素的范围
ix, iy, iw, ih = xs.min(), ys.min(), xs.max()-xs.min()+1, ys.max()-ys.min()+1
scale = (H * h_frac) / ih                       # 让 ink 高度 = 目标高度(或按宽)
layer = {&quot;type&quot;: &quot;image&quot;, &quot;src&quot;: p,
         &quot;left&quot;: round(cx*W - ix*scale - iw*scale/2),   # 用 ink 中心对齐
         &quot;top&quot;:  round(cy*H - iy*scale - ih*scale/2),
         &quot;scale&quot;: round(scale, 6)}

即 left = 目标X − ix*scale,用 ink 偏移抵消素材自身的透明留白。
配套姿势(本次实测有效):
1. 先量参考图(_ref_poster.png)—— 依次扫描出标题/枫叶/女孩/徽章/底部色条的归一化 bbox,
不是凭感觉摆。
2. 写一个 compare.py:把「参考图 | 新预览 | 50% 叠加」拼成三联图自己看。
叠加图能一眼看出谁偏了、偏多少,比单看预览可靠得多。
3. 逐个元素「对号入座」后再微调,一次就能收敛。
4. 同一素材可复制多份(不同角度/位置)当独立图层用,如 ginkgo_front 同时当左下/右下前景叶。

装饰元素必须落在「没被其它层盖住」的位置:初版把 2 朵装饰云放在 y=86(在卡片层的下面),
预览里完全看不见 —— 等于白做两层。摆放装饰前先想清楚该处有没有更上层的图层覆盖;
放完一定要看预览确认它真的露出来了。堆叠顺序:背景渐变 → 装饰 → 卡片/容器 → 内容 → 文字。

高密度多栏填充(两遍布局法)

用户给的参考图如果是「每栏塞满、到边到角」的密排版式(如 4 栏防欺凌海报、宣传单),
固定 y + 固定间距的写法必然在短栏底部留出大片空白(踩过:第 1/4 栏各空 ~100px)。
正确做法是两遍布局:

  1. 把每栏的内容拆成「块」,每块记录 (自然高度, emit 闭包),先不真正落点,只累计 total。
  2. 算全局间距:gap = clamp((avail - total) / (n-1), 10, 38)。
    - avail = 卡片内可用高度(卡片底 − 卡片顶 − 上下内边距)。
    - 间距下限 10 防挤死、上限 38 防散架;最满的那栏决定全局字号/行高,其余栏用 gap 吸收余量。
  3. 第二遍按 top = top0 + 累计高度 + i*gap 逐块 emit,各栏自然填到卡片底。

兜底填充:若某栏内容仍不足(算出来 gap 触顶 38 还填不满),往该栏追加「提示框 / 补充段」块,
不要留白。装饰(标题弧带、白卡、迷你卡底、序号圆、方块)一律用 PIL 程序化生成,
风格统一、边缘干净,别交给 AI(AI 出的装饰常带硬边/不一致)。

实测:avail=786 的 4 栏(各 518 宽),两遍法后 130 层、68 真文字层,四栏全部填到卡片底,无空白。

预览(render_preview.py)

psd-tools 无法光栅化文字层,所以预览做法是:合成所有 pixel/gradient/color 层(带 opacity + blend)→ 再用 PIL 叠字。
定位直接读 TySh.transform 的 (e, f) —— e = 首字左侧的文档 x,f = 首字基线的文档 y,这才是与 PS 像素级对齐的正确做法(读不到 transform 时才退回 top + (bottom-top)*0.85 的近似算法)。
真文字层要带描边:d.text(..., stroke_width=int(t.get(&quot;stroke_width&quot;,0) or 0), stroke_fill=t.get(&quot;stroke&quot;))——否则规里有描边的标题,预览会"看不见描边"、和 PS 内观感不一致(已修)。

★ 文字绝不能被画两遍(2026-09-17 修):psd_tools 保存 PSD 时会把文字层的像素数据一并写进文件,
mock_preview.render() 合成像素层时会把它们也叠进来。此时若再叠一次 PIL 画的文字,
西文/数字(字距度量与中文字体差异大)就会明显「重影/错位」——
中文因字面方正常常只表现为轻微发糊,极易漏掉(本次「HUIDONG…」和徽章「2020/NEW」双影即此)。
render_preview.py 现行做法:合成像素层前先把所有 kind==&quot;type&quot; 的层 visible=False,
合成完再恢复,然后统一用 PIL 按 TySh.transform 画一次:

type_layers_all = [l for l in psd.descendants() if l.kind == &quot;type&quot;]
for l in type_layers_all: l.visible = False
img = render(psd)
for l in type_layers_all: l.visible = True
d = ImageDraw.Draw(img)     # 之后照旧叠字

自查方法:单独跑 mock_preview.render(psd)(不叠字)存一张,若里面已经有文字,
说明文字层带像素数据 —— 必须走上面的隐藏流程。

字体

font(PIL 测量)用系统 TTF 文件名,font_ps 用 PostScript 名写进 FontSet:SimHei(黑体)、STXingkai(华文行楷,喜庆标题好看)、STXinwei(华文魏楷)、Microsoft YaHei、SimSun。PS 里找不到同名字体时会自动替换成中文字体,不会出豆腐块。

竖排中文(节气/国风海报高频)

  • 多行竖排:spec 里把 text 写成 每字一行(&quot;夏\n意\n犹\n未\n尽&quot;)+ line_height(≈字号×1.13),anchor:center。assemble_psd.py 的 render_text 已支持 \n 多行(向后兼容单行),一行=一列,从右往左排需要手工指定每列的 x。
  • ★ 传统竖排必须从右往左读:第一列在最右。例:「夏意犹未尽,忽觉秋意来」→ 右列「夏意犹未尽」、左列「忽觉秋意来」(写反了意思就错了)。排的时候把先读的那列放到更大的 x。
  • 字体:竖排诗句/对联用毛笔行楷(STXINGKA.TTF = 华文行楷)才对味,用雅黑会显得很"通知"。参考图若是书法体,务必换行楷/魏楷。
  • 竖排多行建议走光栅化层(editable 不设 true):真文字层的多段落结构在 PS 里未验证,规避闪退风险;单行文字仍用真文字层。

限制与建议

  • AI 生图产出的是「合成好的一张图」,不是天然分层。要可编辑图层,靠「分元素分别生成 → 去背 → 叠成层」最稳;纯插画从零创作也可走 RGBA 拼装 + 文字/调色层。
  • 嫦娥/玉兔等人物若生成在「主视觉」整图里,则属于同一层;若需其独立可移动,单独生成贴纸并去背成独立层。
  • 透明底质量取决于「白底贴纸」提示词,偶有白边需微调阈值。
  • 真文字层的 TySh + EngineData 现已是「真 PS 外壳 + 真 PS EngineData 模板」,
    结构上已与 Photoshop 自己写出的文件对齐(warp 逐字节相同、EngineDict 7 键 / ResourceDict 12 键、
    颜色 [A,R,G,B]、正文带 \r、TextIndex=0)。但本机无 PS,无法自证不闪退 ——
    所以:① 交付仍双份(真文字层 + 光栅化兜底);② 改完文字层后先出探针 PSD 让用户点一下。
    兼容性优先时请使用光栅化文字层。
  • 交付 PSD 后建议同时给预览 PNG,因为部分看图软件不支持文字层。
  • 部署/分享:PSD 是二进制,建议本地交付或压缩后分享;不要在公开页面预填真实隐私。

依赖

  • Python 3.10+ 即可(系统自带或任意 venv 均可)。
  • 依赖已随包提供,无需手动 pip install。skill 自带离线 wheel(wheels/,已锁定版本)与引导器(scripts/run.py / scripts/bootstrap.py):
    首次运行任意命令时,run.py 会在 skill 目录内自动建隔离环境 .venv 并离线装好 Pillow/psd-tools/requests/numpy/aggdraw 等全部依赖,之后秒跑;
    若离线 wheel 与本机 Python 版本不符(罕见),自动退回 PyPI 在线安装。
  • 也可显式一次性装好:python3 scripts/bootstrap.py(建好 .venv 并打印其 python 路径,等价首次自动安装)。
  • AI 生图认证(AGNES_API_KEY)⚠️ 安装后使用前必配:本 skill 已内置 scripts/agnes_api.py,不再依赖 agnes-image-gen skill。
  • 功能分两类,请分清:① 拼装 PSD / 预览 / 示例验证 → 无需 Key,纯本地离线即用;② AI 生图(生成海报素材) → 必须配 AGNES_API_KEY,否则生图会被拦下。
  • Key 免费申请(每月 500 张免费额度):登录 https://agnes-ai.com →「API Key 管理」→ 创建并复制。
  • 配置方式二选一:① 在 skill 根目录(放 SKILL.md 的文件夹)建 .env 写 AGNES_API_KEY=你的key(脚本自动读取,参考同目录 .env.example);② 或导出环境变量 export AGNES_API_KEY=你的key。
  • 没配 Key 时不会崩溃/静默失败:gen_asset.py 会打印一段中文指引(去哪申请、怎么配 .env),配好再跑即可。安装说明.md 顶部「⚠️ 安装前必读」已用表格把这条讲清楚,别人安装时一眼可见。
  • 本 skill 完全自包含:scripts/ 下已内置 agnes_api.py、mock_preview.py、make_text_layer.py 等全部依赖,
    模板 assets/engine_template.bin 随包分发,无需安装 image-to-psd / agnes-image-gen 即可独立运行。
  • 字体:中文字体(SimHei / STXingkai / STXinwei / Microsoft YaHei / SimSun)按系统目录自动探测
    (Windows C:/Windows/Fonts、macOS /System/Library/Fonts、Linux /usr/share/fonts),缺字时降级到默认字体,不会报错。

常见问题

5 问
答案由本站依据条目字段整理
poster-to-psd一句话生产带独立图层的可编辑 PSD 海报怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_3eb3e208/poster-to-psd-daq。安装方式为未标注。装完刷新技能列表或重开客户端。

poster-to-psd一句话生产带独立图层的可编辑 PSD 海报支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

poster-to-psd一句话生产带独立图层的可编辑 PSD 海报权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

poster-to-psd一句话生产带独立图层的可编辑 PSD 海报收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

poster-to-psd一句话生产带独立图层的可编辑 PSD 海报用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 未标注。