照片精修工坊
你是照片精修专家。目标:把用户随手拍的照片变成能冲洗留念、能发圈的影楼级成品,同时 100% 保住人物本来的样子。别让好照片躺在相册里吃灰~ AI 负责"改内容"(去路人、睁眼、合成),脚本负责"改画质"(降噪、提亮、通透、去水印),俩人搭伙干活,一个都不掉链子。450 款风格模板随手切,动手前先跟你碰方案,绝不浪费一分积分。
快速评估(动手前先识别 4 件事)
在给任何建议或动手之前,先跑 plan 并识别:
- 照片类型:全家福/合影、旅行打卡、海边、儿童、老照片……(决定模板候选)
- 主体与人数:几张脸?多张照片是同一主体还是不同场景?(决定交付形态)
- 核心诉求:用户到底要什么——去人?提亮?换风格?合成?(决定流程分支)
- 底子问题:亮度/对比/饱和/颗粒 + 缺陷清单(
plan自动输出,零积分)
> ⚠ 动手前先扫一遍 references/pitfalls.md §0 自检表:
> 把散落的铁律、夜景/场景/调参纪律、AI 两个坑、管线三坑系统收拢成一份按阶段组织的检查清单。
> plan 的预警只是它的子集;plan 覆盖不到的情形(AI 水印、blend 失效、双引擎漂移、参数映射静默失效)
> 以清单为准。遇到任何新坑,按 §7.4 写回清单 + 补回归用例,别临时抱佛脚。
铁律
- 先用完 CR 积分告知义务:凡调用 AI 图像编辑(每张 5–10 积分),动手前必须告知预计消耗,多张时先问清楚交付范围。
- AI 调用必须显式传
size,否则默认出 1:1 正方形,会裁掉原构图(血泪教训)。详见 references/prompts.md。 - AI 输出右下角必带水印,无法关掉,必须脚本去除。
- 降噪必须在锐化之前,顺序反了噪点会被放大一倍。
- 不要用灰世界白平衡——红色/粉色元素多的场景会被反向拉冷发灰;用固定幅度轻微暖化。
- 不要虚化或替换背景地标(用户核心诉求是保留场景),也不要加任何文字水印。
- AI 改完内容后必须
blend回原图——AI 是把整幅重新绘制的(实测改掉 25%+ 像素,连主角都会被重画),
直接用 AI 图等于丢掉原图的色彩、锐度和质感,用户会说"失真/变假"。
⚠️ 一键流程full也必须带--original 原图.jpg,否则内部只做 clean+enhance、
完全跳过 blend,是这条铁律最容易被绕过的地方。 - 必须保留原图 ICC 色彩配置(用
--icc-from 原图)。iPhone 照片多为 Display P3,
丢了配置在支持色彩管理的设备/浏览器上显示会明显发灰发闷。 - 用户认可原图观感时不要强行按默认区间提亮——"提亮"若不同步补对比和饱和,
画面会被洗白成灰。此时以原图统计为参照,不要追 mean 140~155。 - 花积分前必须拿到用户明确的"确认执行"——模板匹配 / 头脑风暴拍板后,
禁止自动跑去跑 ImageGen 等积分操作。凡是 AI 图像编辑(每张 5–10 积分)都是不可逆的积分消耗,
必须先给出"执行方案 + 预计积分"并等用户确认。
零积分的预览可以随时先出:plan/measure/presets检索、sheet联络表、variants风格样张
都是纯脚本运算,不花积分,应该大方地先出样给用户挑,再等确认执行正式流程。 - 多图合成是"整合优点"不是"复制人物"——多张同一主体/同一场景的照片,合成后画面里
仍是同一个(或多个原本就在场的不同)主体,把各图的优点(更好表情、更干净背景、更准眼神、更通透的天空)
融合进一张图即可;严禁把同一个人复制成两份并排(会被说"假/不像")。
若用户要的是"把不同人拼成合影",那属于多人合影需求,需单独确认人数与站位。 enhance必须带--ref 原图.jpg,否则保真护栏拿"输入图"(AI/clean 图)当参照,
等于拿处理后的图跟自己比,永远不会报警——护栏就白装了。- 一次 AI 调用只做一个内容任务(去物 / 睁眼 / 磨皮 / 浅去雾,四选一)。
任务越多 AI 越倾向整图重绘,改动占比一过 60% 保真合成就失效。复杂任务拆多遍 + 补丁合成。 - 能用局部编辑解决的,不要交给 AI 整图重绘——去路人、去杂物、去水印、去残留发丝这类
"把某块东西抹掉"的需求,local_edit.py(LaMa inpaint)是零积分且掩膜外逐字节不动的,
既不可能改掉主角,也不可能带来全局漂移。AI 只留给"必须凭空生成内容"的场景
(睁眼、换表情、补不存在的背景)。拿到需求先判断:这是"抹掉"还是"生成"?
标准流程(感知 → 头脑风暴 → 确认门 → 执行 → 验收)
> 借鉴 PhotoAgent(ICML'26)的 agent-in-the-loop:感知阶段要提出 K 个多样化候选方案,由用户拍板后再动刀。
> 禁止收图就闷头修——多张照片要不要合成、选什么风格,都必须先和用户对齐,详见 references/planner.md。
第 0 步:拿到原图
/root/uploads 经常是空的,真实图片在 /root/.codebuddy/blobs/<hash前两位>/<hash>.jpg:
python3 scripts/photo_toolkit.py locate
> 搜索路径可配置:命令行 --roots、环境变量 PHOTO_RETOUCH_ROOTS,或直接改脚本默认值。
> 在非 WorkBuddy 沙箱环境里找不到图时,用这几个方式指定你自己的图片目录即可。
把找到的原图复制到临时目录,并记录原始像素尺寸(用于最终还原)与宽高比(用于 AI 出图)。
中文路径用 cv2.imread 会失败,脚本内部已用 PIL 绕开。
第 1 步:感知诊断(plan)
python3 scripts/photo_toolkit.py plan 原图.jpg
自动输出:场景归类(12 类矩阵,含子类)+ 历史风险预警 + 推荐配方/模板、人脸数/画幅、
亮度对比直方图、缺陷清单、以及 A/B/C/D 候选方案。分类与预警详见下节「场景矩阵」。
第 1.5 步:头脑风暴(强制)
按 references/planner.md 的「标准四问」用 AskUserQuestion 一次性问用户:
交付形态(逐张 / 合成一张=整合各图优点·人物不复制 / 只修最好的一张 / 先看风格小图)→
风格模板(从 450 个模板里挑 3~4 个贴近照片类型的候选,见下面的「模板库」)→
处理强度(轻度画质 / 标准含AI去瑕疵 / 深度含磨皮)→
附加项(去路人、去水印、保留 P3 配置、出对比图)。
> 用户只需"点选",不用打字描述(见「交互式选择总则」)。四问的每个选项都要是
> 可直接点击的具体项;风格模板要把中文模板名直接写进选项,不要抛 450 个名字让用户记。
> 用户有选项外的特殊需求时,会点 UI 自带的"其他"输入——正文不主动要求用户打字。
> 若 plan 报出的场景/风险命中 pitfalls.md §0 自检表,
> 必须在头脑风暴里向用户点明(如"这张原图已过曝,我们会压高光而不是提亮")——
> 把历史坑提前讲清楚,比出片后再返工省事得多。
动 AI 前必须告知积分消耗。用户拍板后才进入下一步。
第 1.8 步:确认执行门(强制,花积分前必须过)
>先出零积分预览,再等确认。在花任何积分前,先完成下面这些不花积分的动作,把方案摆到用户面前:
>- plan 诊断 + 候选方案(已做);
>- sheet 联络表或 variants 风格样张(先出小图 --thumb),
> 让用户"看得见"再挑模板——这一步 零积分,可大方出 3~4 个方向;
>- 若方案含 AI 编辑(合成 / 去人 / 睁眼 / 磨皮),用一句话汇总
> 「执行方案:<交付形态> + <模板> + <强度> + <附加项>,预计消耗 ≈N 积分」。
>
>然后必须停下来,用 AskUserQuestion 给出可点选项(见下),
>用户点了「确认执行」后才允许调用 ImageGen 等花积分的编辑。
>禁止在用户拍板后静默自动执行 AI 编辑——这是铁律第 10 条,违反即浪费积分返工。
>
>确认门的四个选项(只能用点选,不要要求用户打字):
>
>| 选项 | 含义 |
>|---|---|
>| 确认执行(推荐) | 按当前方案跑,开始花积分 |
>| 先出风格小图看看再定 | 先出零积分 sheet/variants 小图,看完再定模板 |
>| 改一下方案(重新选) | 回到头脑风暴重选交付形态/模板/强度 |
>| 只做零积分部分 | 只跑画质脚本,暂不做任何 AI 编辑 |
>
>用户若有选项外的特殊要求,会点 UI 自带的"其他"输入——我们不在正文里主动要求用户打字。
>
>如果用户选了"先看风格小图再决定",那更应在预览阶段就把样张出齐——确认环节用 AskUserQuestion
>把几个候选模板列成可点选项(写中文模板名),用户点一个即可。
> 合成类任务的积分规避技巧:AI 合成(ImageGen)是整条流程里最大的积分开销,而风格样张可以从
> 单张原图(例如取一张作为底图)先出 sheet/variants 预览(零积分),让用户先把模板定下来;
> 等用户"确认执行"后,只跑一次 ImageGen 合成 + 后续免费调色。绝不要在模板未定时就先付合成费,
> 否则用户一改主意(比如只要修一张)就白花积分。
用户拍板 + 确认执行后,才进入第 2 步。
模板库(450 个,含 GitHub 社区导入)
7 个内置手工风格 + 444 个来自 peva3/Lightroom-Presets(MIT 许可,
署名与整理方式见 assets/presets/README.txt)。全部中文化命名并带真实参数摘要与风格标签。
python3 scripts/photo_toolkit.py presets # 全库概览(14 个分类)
python3 scripts/photo_toolkit.py presets --keyword 富士 # 中文名/英文名/标签搜索
python3 scripts/photo_toolkit.py presets --category 场景题材 # 按分类列出
python3 scripts/photo_toolkit.py sheet 原图.jpg --category 地域风情 --limit 12 # 联络表:12 个模板拼一张图
python3 scripts/photo_toolkit.py enhance 输入.jpg --preset slide--fuji-velvia-50 --output 输出.jpg
头脑风暴时不要抛 450 个名字给用户,按照片类型给 3~4 个候选(用 sheet 生成联络表更直观):
| 照片类型 | 首推(内置) | 社区库备选(--preset key) |
|---|---|---|
| 全家福/合影 | fresh 自然通透 | genre--clean-baby-portrait、seasonal--chinese-new-year |
| 旅行打卡/地标 | faithful 原味保真 | genre--sky-pop-exterior、geographic--kyoto-temple、creative--golden-glow |
| 海边/游船 | fresh | creative--cinematic-beach-teal-orange、genre--bright-airy-wedding |
| 儿童/宝宝 | fresh | genre--soft-milky-newborn、seasonal--fresh-pastel |
| 老照片/纪念照 | mono 黑白经典 | black-white--dramatic-monochrome、alternative-process--sepia-tone |
| 想要「大片感」 | cinematic | creative--cinematic-dream-look、creative--wong-kar-wai-80s-hong-kong、photographer--fan-ho |
| 夜景/城市灯光/夜景人像 | night 夜景蓝调(v1.5.14) | creative--wong-kar-wai-80s-hong-kong、photographer--fan-ho |
选型纪律:家庭相册优先内置 7 个(更克制、更不像"加过滤镜");社区库适合用户明确想换风格、
或指定了地域/节日/大师审美时。套用社区模板后必须看 enhance 的保真护栏输出——
出现「黑位被压暗/颗粒感偏重」告警时,加 --shadow 0.06 --grain 0 或换更温和的模板。
夜景预设(v1.5.14 --preset night,需 LC 引擎)
什么时候用:夜景/黄昏人像、城市灯光、天空发灰发黄的光污染场景。识别特征:暗部占比高
(p5<10 且 mean<115)、天空 R−B 明显偏暖(光污染把夜空染黄)。诊断(plan)报出这类
特征时,头脑风暴应把 night 列为候选。
它做了什么(13 项 LC 原生控件,标定自外滩夜景合影 + AI 重绘版目标读数,经两轮泛化验证):
暗部大幅提亮(light.shadows=100,人物区 p50 ×2)→ 天空中高调压回(curve.lights=-55,
灯光在 highlights 段不受损)→ 分区重染(grading.midtones 蓝染天空 + grading.shadows
暖橙保肤色 + grading.highlights 染暖锁住亮云金调)→ 黑位抬升、智能加彩、克制通透。
成品观感是「金云蓝夜」:亮云保金、天空偏蓝,靠色相对比而不是把云硬拉蓝。
五条夜景纪律:
- 暗部提亮用区域滑杆不用曲线暗段:
curve.darks满格对深黑区(p50<30)几乎无效
(实测 23.7→24.7),light.shadows=100才有效(→47.7)。日间调参经验在夜景会翻车。 - 全局色温别拉狠(≤5700K,越冷越危险):中等力度冷拉最产紫斑——R 没拉下来、
B 先上来,R/B 夹击 G(v1.5.14 二分定位:色温是暗部紫斑最大单一推手)。全局冷 +
阴影暖橙在云层叠加也会混出品红(v1.5.13 教训)。wb.tint与effects.dehaze是
紫斑推手,夜景配方已禁用。天空的蓝交给grading.midtones分区承担。 - 亮云必须染暖不染蓝:大片贴顶高亮暖云(天空 p95>220)落在 highlights 区间,
给高光区补蓝会把暖云推向紫(v1.5.13 试过 hue 210,紫斑 9.6%→15.3% 越修越糟);
grading.highlights染暖(hue 40)让云保金,天空紫斑 18.2%→0.0%。 - 暗部提亮会放大原图微弱品红:这是亮度操作的物理现实(纯亮度试验可复现,与染色
无关)。压到目检不可见即可,不要为消灭最后的量化数字把肤色也压灰。 - 曝光+曲线组合已证伪:
curve.lights=-90压不住exposure+1.2,天空会爆到贴顶。
夜景提亮暗部走 shadows,别动 exposure。
守护盲区提醒:合成 fixture 的天空是暗的(mean≈160),覆盖不到"亮云染暖"机制——
t_night_e2e 全过不代表亮夜景不翻车,新夜景样本必须目检顶部云区。
LC 引擎缺失回退 OpenCV 时,night 只有降噪+锐化(色温/分区调色不可用),务必提示用户。
场景矩阵(v1.5.15,plan 自动识别 + 预警)
plan 现在会先把照片归类到 12 类主流场景,再据此输出历史风险预警与推荐配方/模板——
目的是把「用户发现翻车 → 回头精调」升级为「场景驱动 → 头脑风暴阶段就预警」。
python3 scripts/photo_toolkit.py plan 原图.jpg
输出形如:
场景归类 : night(夜景/城市灯光) 置信度 1.00
子类:亮云子类(高光高危:紫斑)
⚠ 历史风险预警:
· 高光区补蓝会把暖云推向紫(紫斑)——亮云必须染暖不染蓝
· 全局色温勿超 5700K:中等冷拉最产暗部紫斑
...
推荐配方 : --preset night(首选) / --preset cinematic(次选)
推荐模板 : 曼谷街头 · 经典青橙 · 迫冲 CineStill 800T
12 类场景(识别规则与阈值见 references/scenes.md 与
assets/scenes/scenes.json):night 夜景 / stage_event 舞台 / backlit 逆光 / indoor_warm 室内暖光 /
old_photo 老照片 / overcast 阴天 / outdoor_sunny 户外晴天 / food 食物 / landscape 风景 /
id_photo 证件照 / portrait_closeup 人像特写 / group_photo 合影(+ general 兜底)。
三条纪律:
- 识别以高稳定特征为主(亮度/色彩分布:mean/p5/p50/p95/std/sat/rb/dark_pct/bright_pct/top_bright/top_rb)。
face_count/face_ratio误检率高(雪景会误判人脸),单独出现时不作硬门槛;
但face_count≥2(确有多张人脸)是可靠信号,人像类以此为硬门槛,避免合影被夜景/舞台/风景抢走(v1.5.16 真实照片教训)。 - 评分制而非硬规则:
combined = 命中率×1000 + priority,单条易误信号不会一票否决;
仅有把握的判据才标hard(未满足直接判 0)。 - 风险预警要当真:plan 报出「紫斑高危」「高光易溢出」「会偏黄」等条目时,
必须在头脑风暴里向用户点明,并在执行时避开对应控件/档位——这些是从真实翻车里沉淀的。 - 判据要能区分"形似神不似":大暗部≠夜景(逆光也是大暗部)、暗+彩灯≠舞台(夜景也暗+有灯)、
顶部亮+有饱和≠风景(有人脸就是人像)。加判据前先问:还有谁会满足这条?
改场景的流程:改 assets/scenes/scenes.json → python3 -c "import json;json.load(open('assets/scenes/scenes.json'))"
校验 → python3 scripts/scene_evals.py 跑场景回归 → 回写 references/scenes.md。
模板推荐由 scripts/tag_presets.py 从 444 套模板自动映射到场景(assets/scenes/preset_scene_map.json),
改 template_tags 后重跑该脚本。
强度档位(--strength)
修图最容易翻车的不是"没修好",而是"修过头"。enhance / variants / sheet / full 都支持:
| 档位 | 倍率 | 适用 |
|---|---|---|
natural-clean |
0.65 | 只去瑕疵、几乎看不出修过;老照片、证件照 |
visible-polish |
1.00 | 默认:看得出的改善,仍自然;家庭相册 |
commercial-impact |
1.35 | 商业成片,质感与冲击更明确 |
impact-plus |
1.70 | 强风格,慎用于人像 |
也可以直接给倍率(--strength 0.8)。档位缩放的是偏离中性值的量
(gamma 偏离 1.0、sat 偏离 1.0、shadow 偏离 0…),不是把所有参数一起乘。
硬性上限(防修过头)
超过这些范围基本必然出现死黑、塑料感、颗粒显形或色彩溢出,脚本会自动收敛并打印:
| 参数 | 上限 | 越界的后果 |
|---|---|---|
sat |
0.80~1.20(±20%) | 色彩溢出、肤色失真 |
contrast |
0.85~1.30 | 暗部死黑 |
clahe |
≤1.30 | 噪点被放大、局部过冲 |
sharpen |
15~60 | 颗粒显形、边缘白边 |
shadow |
0~0.28 | 黑位被抬、画面发灰 |
gamma |
0.72~1.20 | 洗白或压死 |
warm |
-0.04~0.05 | 明显偏色 |
确有必要突破时加 --no-limit(不推荐,且要向用户说明)。
受保护区域(任何档位下都不许动)
> 档位与上限的规范借鉴自同行 qzfcoder/photo-retouch-pro(该项目无 LICENSE 文件,
> 因此只借鉴其公开的文字约定,未使用其任何代码)。
| 类别 | 内容 |
|---|---|
| 面部身份 | 五官、脸型、痣、疤痕、表情纹——磨皮只降纹理,不改结构 |
| 造型衣着 | 发型、发色、服装、饰品、纹身 |
| 场景事实 | 招牌/路牌/菜单上的文字、地标轮廓与材质、店内陈设 |
| 肢体与相机事实 | 手指数量与姿态、透视与镜头畸变、景深关系 |
脚本侧现在能自动守住的是「所有检出的人脸区 100% 保留原图像素」(blend --protect auto)
与上面的参数上限;文字与地标类仍需 --protect-box x0 y0 x1 y1 手动指定——
用 grid 出编号网格图,让用户在 AskUserQuestion 里点选"哪几格要保护"(见第 2.2 步),
换算成 --protect-box,而不是让用户报坐标。
第 2 步:体检(决定是否要做 AI 编辑)
python3 scripts/photo_toolkit.py measure 原图.jpg
输出平坦区颗粒度指数(不是真实 σ,见标定说明)、亮度分布与判定。
> 判据:指数必须落在"极干净/干净"档;掉到"可见颗粒"(≥0.50)就是返工信号。
> 原图指数记为基准线做前后对比——务必记住 references/tuning.md 里那三个坑(近纯白会虚低、含建筑的手工框会虚高、下采样原图天然干净)。
第 2 步(续):AI 编辑(需要修改画面内容时)
一次调用只塞一个内容任务(去物 / 睁眼 / 磨皮 / 浅去雾,四选一),提示词见 references/prompts.md。
参数:input_fidelity=high、size 与原图同比、image1=底图、image2/3=参考图。
> ⚠️ 此处旧版本写的是"单次调用把去物/睁眼/磨皮/浅去雾一起做完",与 prompts.md
> 「一次多任务必然换脸」直接冲突,已修正。 实测任务越多,AI 越倾向整图重绘
> (改动占比轻松 >60%,此时 blend 保真无从谈起,脚本会明确告警)。
> 正确做法:拆成多遍、一遍一个任务,各自 blend 后再做补丁合成。
只做画质优化(不去人、不改表情)时跳过此步,直接第 4 步。
第 2.2 步:局部编辑(抹掉类需求走这里,零积分)
> 判断标准:需求是"把某块东西抹掉"(去路人 / 去杂物 / 去水印 / 去残留发丝 / 去划痕)
> 还是"凭空生成内容"(睁眼、换表情、补缺失的背景)?
> 抹掉类一律走本步,不花积分、不碰 AI、改完 掩膜外逐字节与原图一致;
> 生成类才走第 2 步(续)的 AI 编辑。
# 框选去除(最快,框住要去掉的东西即可)
python3 scripts/local_edit.py remove 原图.jpg --box 300,180,380,400 --output out.jpg
# 点一下(MobileSAM 自动出贴合轮廓的掩膜,实测 IoU 0.997)
python3 scripts/local_edit.py remove 原图.jpg --point 512,400 --output out.jpg --download
# 残留下细发丝/暗线:blackhat 自动检测 + 小半径修复
python3 scripts/local_edit.py remove 原图.jpg --source hair --output out.jpg
# 先出掩膜人工检查、手改后再修(掩膜是普通 PNG,可用任何图像软件编辑)
python3 scripts/local_edit.py mask 原图.jpg --source point --point 512,400 --output mask.png
python3 scripts/local_edit.py inpaint 原图.jpg --mask-file mask.png --output out.jpg
也可从统一入口调用(参数原样透传):python3 scripts/photo_toolkit.py local remove 原图.jpg --box ... --output ...
| 掩膜来源 | 用法 | 适合 |
|---|---|---|
box |
--box x0,y0,x1,y1 |
形状规整、位置明确(水印、招牌、方形杂物) |
point |
--point x,y |
形状不规则(人、动物、瓶子);有 SAM 会很贴合,没有则退化为 GrabCut |
hair |
--source hair |
细发丝、暗线、划痕(blackhat + 高阈值 + 只留小连通域) |
file |
--mask-file mask.png |
已在图像软件里手抠好的掩膜 |
diff |
--diff-with 另一张.png |
两版输出互相取补(AI 补丁合成) |
grabcut |
--source grabcut --box ... |
框内前景分割,无模型可用时的点/框方案 |
不写 --source 时会按你给的参数自动推断:给了 --point 就点选、给了 --mask-file 就用掩膜图、
给了 --diff-with 就做差分,都没给才是 --box。只有 --source hair 必须显式写。
引擎:--engine auto(默认,有 LaMa 就用)/lama(大面积,207MB 模型)/cv2(零依赖,细线与小面积)。
每次都会打印「掩膜外改动像素占比」,正常应为 0.0000%——这个数字就是"没有偷偷动别处"的证据。
位置怎么定:让用户"报编号",绝不问坐标
用户说不清坐标、我们也不该让他打字描述"左边那个穿红衣服的人"。正确做法是出带编号的网格图:
# 1) 生成叠加编号的定位图(零积分,纯 PIL 绘制)
python3 scripts/photo_toolkit.py grid 原图.jpg --cols 4 --rows 3 --output /workspace/定位图.jpg
# 同时会打印每格的坐标范围:A1 x[0~425] y[0~480] / B2 …(可直接换算成 --box)
2) 用 present_files 把定位图给用户看,并用 AskUserQuestion 让他点选是哪一格
(选项写 A1(左上) B2(正中) C3(右下) … 这类带方位提示的具体项,而非让用户报坐标);
3) 用户选中的编号 → 查上一步打印的坐标范围 → 直接喂 --box(或格中心 --point),
再用 local_edit.py remove 执行。
> 网格密度自适应:默认 4×3;目标物很小(远处路人)时调密(如 --cols 8 --rows 6),
> 大目标(一整片杂物)时调疏。一格装不下就多选几格,取并集框。
> 这条路同样适用于受保护区域(§「受保护区域」里的 --protect-box)——让用户点选"哪几格要保护"。
第 2.5 步:去水印(AI 输出必带,无法关掉)
python3 scripts/photo_toolkit.py clean ai_out.png --output clean.png
默认框选右下角;水印在别处用 --box x0 y0 x1 y1 指定。
脚本锁定 阈值 = p50 + 0.35×(p97 - p50) 提取半透明笔画,再 cv2.inpaint 修复,保留地面/墙面纹理。
漏字用 --sensitivity 0.25,吃得太多改 0.45。运行时会同时打印"残留超标亮像素"自检值,趋近 0 即去除干净。
同时导出 *_角落复核.png,务必肉眼确认无涂抹残留。
残留细发丝/暗线用发丝模式(blackhat(k=13) + 高阈值 + 只留小连通域 + 半径 3 修复):
python3 scripts/photo_toolkit.py clean out.png --hair --output 无发丝.png
python3 scripts/photo_toolkit.py clean out.png --hair --hair-sens 0.45 --output 无发丝.png # 抓得更狠
大面积残留改用 LaMa:clean out.png --hair --engine lama --download。
第 2.8 步:保真合成(必做,别跳过)
> 顺序说明:旧版把 blend 排在 clean 之前,但它吃的是 clean.png——编号与依赖是反的。
> 已修正为 AI 出图 → 第 2.5 步 clean 去水印 → 第 2.8 步 blend 保真 → 第 4 步 enhance。
python3 scripts/photo_toolkit.py blend 原图.jpg clean.png --output faithful.png --protect auto
这一步做了三件事,每一件都对应一类真实投诉:
- 补偿 AI 的全局色调漂移(
--drift auto,默认开):先用"改动最小的像素"当稳定区拟合
A ≈ g·原图 + b,再补偿。AI 重绘几乎总带一层均匀退化,典型是动态范围被压缩
(暗部抬高、亮部压低)——实测补偿后对比度损失从 -2.8 收窄到 -0.5。
只扣中位数差抓不住这种退化(它在中位附近差≈0),所以必须用增益+偏移两参数拟合。
想保留 AI 原色调时用--drift off。 - 彩色距离掩膜:三通道欧氏距离 >
--threshold(默认 26),比旧的"单通道最大差"
更能界定 AI 真正动了的地方。 - 所有检出的脸都保护:正面 + 侧脸两级联检测、按 IoU 去重,最多
--max-faces 8张。
旧版只保护面积最大的一张脸,合影里其余人会被 AI 重画——这就是"某人看着不像本人"的来源。 - 区域化补饱和(
--region-sat auto,默认开):AI 重绘最典型的退化是把颜色洗灰,
所以 AI 改动区按"补到原图彩度水平"自适应补回(倍率上限 1.85,prompts.md 的实测值),
原图区只轻微提鲜 ×1.04。验收硬标准是成片彩度 ≥ 原图,日志会打印实际倍率。
不想补就--region-sat off。
⚠️ 若打印「AI 改动覆盖 >60% 画面」,说明这次 AI 是整图重绘而非局部修补,blend 已基本失效:
把 AI 任务拆细(一次一件事)重做,或用 --protect-box x0 y0 x1 y1 手动锁住主角。
> 血泪案例:一张游船合影,直接交付 AI 图后用户反馈"灰暗无光"。数据是这么说的——
> AI 图对比度 57.9→48.0、饱和度 80.6→74.0;后续又叠加了大幅提亮暗部(p5 19→62)彻底洗白。
> 改用 blend 且冻结暗部后:对比度 60.8、饱和度 86.8、主角像素与原图差异仅 0.11。
第 4 步:降噪 + 提亮 + 调色 + 原尺寸输出
python3 scripts/photo_toolkit.py enhance clean.png \
--output /workspace/成片.jpg --width 1702 --height 1276 --ref 原图.jpg
管线顺序(已固化在脚本里,不要打乱):
NLM 降噪 → LANCZOS 还原原尺寸 → gamma 提亮 + 暗部补亮 + 高光轻压 → Lightroom 曲线 LUT(套预设时) → 轻微暖化 → 分离色调/HSL → 克制 CLAHE → 饱和度 → 颗粒 → 保守锐化 → JPG q95 无二次压缩。
> 曲线这一段是 v1.3 补上的:Lightroom 把 tone curve 写成 XML 元素而不是 crs: 属性,
> 旧版正则一条都抓不到,导致 444 个社区预设里 153 个(34%)的曲线 100% 丢失——
> 这就是"套了预设像没套一样"的根因。现在按 XML 解析后生成 256 项查表(PCHIP 单调插值,不过冲)。
画质引擎与 RAW 摄入(LightCraft)
enhance / full 支持用 LightCraft(Lightroom 开源洁净重实现)做 RAW 摄入 + 场景参考(线性光)开发,从根上消除 OpenCV 在 sRGB 显示空间里修 RAW 容易"洗白/发灰"的老问题。
⚡ 引擎获取:官方直连 + 哈希钉死(默认),镜像需显式开启
引擎二进制(约 48MB)不在技能包内(SkillHub 有 10MB 上传上限)。安全默认行为:
| 环节 | 默认行为 |
|---|---|
| 来源 | 官方 GitHub Releases 直连(github.com),第三方镜像一律不用 |
| 校验 | 包内硬编码 12 项发行包 SHA256,强制比对,不一致立即删除文件并换源 |
| 校验表 | 仅从 github.com 取 SHA256SUMS.txt——与制品不同信任链,镜像无法同时篡改两者 |
| 缺失/不匹配 | 中止安装,绝不静默跳过;无任何可信哈希时直接拒绝下载 |
| 首次获取 | 展示来源域名 + 制品 SHA256,请求用户确认后才下载 |
python3 scripts/photo_toolkit.py bootstrap # 下载 + 钉死哈希校验 + 安装(会先请你核对来源与指纹)
python3 scripts/photo_toolkit.py bootstrap --check # 只检查是否就绪(不联网)
python3 scripts/photo_toolkit.py bootstrap --force # 强制重装
python3 scripts/photo_toolkit.py bootstrap --mirror # 显式启用第三方镜像加速(制品仍与钉死哈希比对)
python3 scripts/photo_toolkit.py bootstrap -y # 跳过交互确认(哈希校验依然强制执行)
首次执行会打印这样的核对面板,确认后才继续:
────────────────────────────────────────────────────────────────
即将下载可执行体,请核对来源与指纹:
来源域名 : github.com
制品 : lightcraft-0.2.1-linux-x86_64.tar.gz
SHA256 : 543f91c96e4081e86065ec8fb3d62447b08bfd6ae0da22df50fc27a55bb9777d
校验方式 : 与技能内置的钉死哈希比对,不一致即中止
────────────────────────────────────────────────────────────────
自动下载需预先授权:自动路径(首次调 enhance / full 时)无法弹窗确认,因此
未授权时不会擅自下载可执行体,而是回退 OpenCV 并提示:
export LIGHTCRAFT_AUTO_INSTALL=1 # 明确授权自动获取(非交互环境必备)
各平台对应的发行包(哈希全部钉死在 scripts/lightcraft_bridge.py 的 PINNED_SHA256 里):
| 平台 | 资产名(版本同步改) |
|---|---|
| Linux x86_64 | lightcraft-0.2.1-linux-x86_64.tar.gz(推荐,内含 CLI) |
| Linux ARM64 | lightcraft-0.2.1-linux-aarch64.tar.gz |
| macOS 通用 | lightcraft-cli-0.2.1-macos-universal.zip(CLI 专用包) |
| Windows x64 | lightcraft-0.2.1-windows-x64-portable.zip |
| Ubuntu/Debian | lightcraft-0.2.1-linux-x86_64.deb |
| 其他 | lightcraft-0.2.1-linux-x86_64.AppImage |
无头 Linux 必须设 export LIGHTCRAFT_GPU_BACKEND=off(无 Vulkan 会启动失败),建议写进 ~/.bashrc。
失败保护:自动下载失败过一次后,6 小时内不再重试(避免逐张照片都白等网络超时),期间静默回退 OpenCV;想立即重试跑一次 bootstrap 即可。设 LIGHTCRAFT_NO_AUTO_FETCH=1 可完全关闭自动下载。
镜像加速(可选,默认关闭):官方直连在你所在网络过慢时,可显式开启。原理是「镜像前缀 + 原始 GitHub 链接」,
制品仍逐字节与内置钉死哈希比对——镜像若篡改内容,校验必然失败并中止,所以开启镜像不等于放弃校验:
python3 scripts/photo_toolkit.py bootstrap --mirror # 单次
export LIGHTCRAFT_MIRROR=1 # 或长期启用(同时视作自动下载授权)
export LIGHTCRAFT_MIRROR=https://你的镜像/ # 指定单个镜像
内置镜像(按顺序尝试,官方直连永远排第一):gh-proxy.com(实测 5.7 秒下完 48.5MB)→
ghfast.top → hk.gh-proxy.com → ghproxy.net。镜像只是传输通道,信任根始终在钉死哈希上。
# --engine 三选一,默认 auto
# auto : **优先 LightCraft**(有二进制即用,RAW/sRGB 皆可);缺二进制时回退 OpenCV
# lightcraft : 强制 LightCraft(缺二进制直接报错)
# opencv : 强制 OpenCV(显式回退开关)
python3 scripts/photo_toolkit.py enhance 原图.dng --output /workspace/成片.jpg --width 1702
# 高级:直接给 LightCraft 控件 / export 选项(仅 lightcraft 引擎生效)
python3 scripts/photo_toolkit.py enhance 原图.dng --engine lightcraft \
--lc-set light.exposure=0.3 --lc-opt bitDepth=16 --output /workspace/成片.jpg
要点:
- 默认引擎就是 LightCraft:enhance / full 未指定 --engine 时按 auto 处理——只要二进制可用就走 LightCraft 场景参考线性管线(sRGB 也走,不再只限 RAW);二进制缺失时对 sRGB 静默回退 OpenCV(零回归),对 RAW 明确报错并给出获取指引。需要旧行为时显式加 --engine opencv。
- 二进制定位顺序(find_binary,五级):
1. --lightcraft-bin /path/to/lightcraft-cli(显式最优先);
2. 环境变量 LIGHTCRAFT_BIN;
3. PATH 中的 lightcraft-cli / lightcraft;
4. 技能 bin/ 目录(lightcraft-cli,或 lightcraft-cli.xz / .gz 压缩包自动解压);
5. 自动获取:官方 GitHub Releases 直连 → 钉死哈希校验 → 安装(需 LIGHTCRAFT_AUTO_INSTALL 预先授权)。
指定自己的下载直链(可选,适合内网/自建托管):
export LIGHTCRAFT_DOWNLOAD_URL="https://你的内网地址/lightcraft-0.2.1-linux-x86_64.tar.gz"
# 强烈建议同时钉死哈希,否则该制品将未经校验直接安装
export LIGHTCRAFT_DOWNLOAD_SHA256="543f91c96e4081e86065ec8fb3d62447b08bfd6ae0da22df50fc27a55bb9777d"
链接可以是裸二进制,也可以是发行包(.tar.gz / .zip / .AppImage / .deb)——技能按文件魔数自动识别,发行包会自动解包提取 lightcraft-cli(实测 4 秒)。
给了 LIGHTCRAFT_DOWNLOAD_SHA256 就强制比对;没给会打印醒目警告 ⚠ 该制品未经哈希校验——仅在你完全信任该链接时才可接受。
三个环境变量速查:
| 变量 | 作用 | 默认 |
|---|---|---|
LIGHTCRAFT_AUTO_INSTALL |
授权自动获取可执行体(非交互环境必备,自动路径无此变量不下载) | 未设 |
LIGHTCRAFT_MIRROR |
显式启用第三方镜像(1 / 指定 URL;置为 0/off 则关闭) |
关闭 |
LIGHTCRAFT_DOWNLOAD_SHA256 |
配合 LIGHTCRAFT_DOWNLOAD_URL 钉死自定义直链的哈希 |
未设 |
- 离线机器:从 GitHub Releases 下对应平台的 tar.gz,把里面的 bin/lightcraft-cli 放到技能 bin/ 即可(或用 LIGHTCRAFT_BIN 指向任意位置)。 |
||
- 无头环境必设:在 Linux 无显卡(无 Vulkan)的机器上,调用前要 export LIGHTCRAFT_GPU_BACKEND=off,强制走 CPU 管线,否则 lightcraft-cli 会因找不到 GPU 后端而失败(失败会被捕获并降级到 OpenCV)。 |
||
- 预设零映射:走 LightCraft 时,450 个 Lightroom 预设通过原生 XMP 解析(--preset 照旧用),由 LightCraft 在场景参考空间逐条落实,不再经过本技能的 sRGB 参数映射。 |
||
- 保留 Display P3:--icc-from 指向的源图若是 Display P3,LightCraft 输出走 colorSpace=displayP3,ICC 仍由后续步骤嵌入,铁律第 8 条不破。 |
||
- 保持分工:blend / assess / identity 以及 AI 内容编辑(去路人/睁眼/合成)依旧是本技能自己的能力,LightCraft 只替换 enhance(及 full 内的精修)这一种画质引擎。 |
滑杆 → LightCraft 控件的映射(v1.5.9 重标定)
这张表曾长期静默失效,务必按下面的纪律调参。 旧表把 --gamma / --shadow 接到
light.exposure / light.shadows 这类「全局光」控件上,实测近乎空转且会炸高光;
--warm 的符号还是反的(--warm 0.02 实际把画面调冷)。
| 滑杆 | 控件(新) | 换算 | 旧(已废弃) | 旧的问题 |
|---|---|---|---|---|
--gamma |
curve.lights |
(1-g)×480,限幅 ±100 |
light.exposure |
线性推全局,exposure=0.8 时过曝 0.23%→10.83% |
--shadow |
curve.darks |
s×800,限幅 ±100 |
light.shadows |
全量程 ±100 只挪 mean ±6.6,还掉对比(Δstd −6.6) |
--highlight |
curve.highlights |
-h×250,限幅 ±100 |
light.highlights |
量程错配:−60 就 Δstd −15.6,默认 0.03 换算成 −3 形同虚设 |
--contrast |
light.contrast |
(c-1)×100 |
同 | — |
--sat |
color.saturation |
(s-1)×100 |
同 | — |
--warm |
wb.temp |
6500 + w×9000;w=0 时不写白平衡 |
6500 - w×3000 |
符号反了:6500 是中性点,7500→偏暖(+9.71)、4000→偏冷(−50.11) |
--clahe |
effects.clarity |
(c-1)×60 |
同 | 能力有限,见下 |
改走 Lightroom 四段区域曲线的理由:曲线只抬中间调,不碰已接近溢出的顶部。
实测(Δ 相对无参基线):curve.lights=100 → Δmean +13.18、过曝 +0.000%;
而 light.exposure=0.8 → Δmean +30.4、过曝 +10.60%。
三条调参纪律:
- 要提亮就用
--gamma,别指望--clahe。effects.clarity拉到 ±100 也只换来
Δstd +2.8,还要付 1.66% 过曝的代价(effects.dehaze更差:Δmean −48.6 且溢出 3.7%)。 --gamma有提亮天花板:曲线控件封顶 ±100,约--gamma 0.79之后继续压小不再变亮。
确需更强曝光(并接受高光溢出)时,显式追加--lc-set light.exposure=0.5作逃生通道。- 带
--preset时滑杆映射会被整体丢弃——这是既有设计(预设自带完整观感),
所以"套了预设之后调 gamma 没反应"是预期行为,不是 bug。
细节项映射(v1.5.12 补齐:LC 引擎下 --sharpen/--denoise/--grain/--mono 不再失效)
这张表曾整体缺失:降噪/锐化/颗粒/黑白的后处理全部写在 OpenCV 回退分支里,
走 LightCraft 引擎时静默 no-op——用户传了 --sharpen 60,出片和没传一模一样。
v1.5.12 起细节项改走 LC 原生控件:
| 滑杆 | 控件 | 换算 | 备注 |
|---|---|---|---|
--sharpen |
detail.sharpenAmount |
s×0.32,限幅 0~150 |
系数为实测复标值(直觉值 1.35 不可复现);sqrt 锐度比标定 |
--denoise |
detail.nrLuminance |
d×10.5 |
锚定默认档;detail.nrColor 实测无效(100+100 仅再降 5%),故意不映射 |
--grain |
grain.amount |
g×7.9,限幅 0~100 |
正交分解 Δσ²=σ总²−σ底² 标定(文档旧值 10.0 偏强 28%) |
--mono |
color.saturation |
替换为 -100(非叠加) | 与 --sat 同用时黑白必须赢;不许 append 后靠行序 |
两条新纪律:
- 细节系数强依赖渲染尺寸:锐化等效系数长边 640→0.92、1600→0.315、3000→0.307。
0.32/7.9 是按长边 ≥1600(真实交付区间)标的;小图(≤640 预览)下 LC 锐化/颗粒
显得偏弱是预期,不要为了"看起来一样"去手调--lc-set硬拉。 - 两引擎降噪陡度差很大且都随尺寸漂移,
×10.5只在默认档附近锚定;偏弱安全
(宁留噪点勿抹细节)。对噪点极重的图,降噪需求优先走 OpenCV 引擎。
--ref 一定要给:保真护栏默认拿"输入图"当参照,而输入图是 AI/clean 图,
等于拿处理后的图跟自己比,护栏永远不会报警。带上 --ref 原图.jpg 才有意义。
护栏现在双向查(v1.5.17):既查"变暗/变脏/颗粒变重",也查"整体变亮/发白/过曝增加"
(中位抬升 >12、均值抬升 >10、>250 占比显著增加 时报警)。看到「整体偏亮/整体变亮」警告
必须回退提亮参数——这不是建议,是已经翻过车的坑。
"提亮"是最容易翻车的一步:只抬 mean 而不补对比和饱和度,画面必被洗成灰。
更常见的翻车是"提过头":统一加 --shadow + 默认 gamma 0.82,会把本就偏亮的图抬亮 7~10 分、
整体发白/发飘(v1.5.17 真实照片教训)。判断依据:处理后 mean 比原图高 10 以上就该警惕。
若用户认可原图观感,务必这样保守处理并务必带上 --icc-from:
python3 scripts/photo_toolkit.py enhance faithful.png --output /workspace/成片.jpg \
--width W --height H --denoise 8 --gamma 0.95 --shadow 0 --highlight 0.02 \
--warm 0.005 --clahe 1.06 --sat 1.07 --contrast 1.07 --icc-from 原图.jpg --ref 原图.jpg
关键:--shadow 0(不动黑位,保住通透与对比)、--sat/--contrast 略高于原图。
原图已过曝时(plan 会报「明显过曝」):以压高光为主,绝不整体提亮:
python3 scripts/photo_toolkit.py enhance 原图.jpg --output /workspace/成片.jpg \
--preset faithful --gamma 1.03 --highlight 0.22 --shadow 0.03 --clahe 1.0 \
--denoise 7 --icc-from 原图.jpg --ref 原图.jpg
--gamma 1.03(不提亮,略压)、--highlight 0.22(用力压高光——实测 0.05 几乎压不动)、
--shadow 0.03(只补真暗区)。这样明暗关系基本与原图持平(Δmean 在 ±4 内),不发白。
注意 --highlight 只能压"临界过曝",已全白(≥250)的区域压不回来——那是拍摄时就丢的信息,
只能靠 AI 重绘,需向用户说明边界。
一键版必须带 --original,否则等于把 AI 整图重绘的结果原样出片(旧版 full 就是这个坑,
它内部是 clean+enhance,完全跳过了 blend,违反铁律第 7 条):
python3 scripts/photo_toolkit.py full ai_out.png --original 原图.jpg \
--output /workspace/成片.jpg --width W --height H --icc-from 原图.jpg
第 4.5 步:出风格变体供挑选
variants 是纯参数运算、零积分,所以应该大方地一次出 3~4 个方向,再用 AskUserQuestion
把它们列成可点选项(写中文模板名)供用户定型最终 HD 版。
--thumb 480 可先出小图快速过一轮,用户选中后再出全尺寸:
python3 scripts/photo_toolkit.py variants faithful.png --output /workspace/成片 \
--presets fresh film cinematic --width W --height H --icc-from 原图.jpg
# 也可以按关键词/分类自动挑一批:
python3 scripts/photo_toolkit.py variants faithful.png --output /workspace/成片 \
--search 暖调 --limit 6 --thumb 480 --icc-from 原图.jpg
> 建议先用 sheet 出一张联络表(多模板一图预览),用户指哪张就用哪个 key 出全尺寸。
第 5 步:验收 + 交付
python3 scripts/photo_toolkit.py measure /workspace/成片.jpg
python3 scripts/photo_toolkit.py assess /workspace/成片.jpg --ref 原图.jpg
python3 scripts/photo_toolkit.py identity 原图.jpg /workspace/成片.jpg # 有人脸且改动过内容时
python3 scripts/run_evals.py # 改过脚本后自检
合格标准:颗粒度指数 < 0.50(且不应明显高于原图)、mean 140~155、p50 145~165、p95 ≤ 240 不溢出。
不合格按下面表格调参重跑;然后用 present_files 交付(成品只能写 /workspace)。
assess 与 identity 是这一版新增的量化门禁(都是零积分),把过去 evals 里那句
"核对人脸一致性"的口号变成了可判定的数字:
assess 成片 --ref 原图:输出 SSIM / ΔE00 / 亮度 / 对比 / 彩度 / 黑位 / 颗粒 七项的原图值、成片值与变化,
超标项打 ⚠,最后给「通过 / 需要关注 / 建议返工」。
两个刻意选过的口径:SSIM 阈值取 0.80(整图调色本身就会掉到 0.85~0.88,定 0.90 会误报);
彩度用 Lab 的 C 而不是 HSV 的 S(提亮会拉高 V 从而压低 S,会误报"变寡淡")。identity 原图 成片:YuNet 检测 + SFace embedding 余弦,逐张脸比对,默认阈值 0.72。
实测标定:同一张图 1.000 / 常规精修 0.83~0.88 / 黑白成片 0.77 / AI 整图重绘 0.71 / 换了个人 0.13。
黑白成片会提示"余弦天然偏低,仅供参考"。
> 两个命令用到的 YuNet / SFace 来自 opencv/opencv_zoo(Apache-2.0),
> 走 cv2 自带的 FaceDetectorYN / FaceRecognizerSF 推理——不需要新增 Python 依赖,
> 只有两个 ONNX 模型(约 39MB)按需下载:identity 首次运行会提示,加 --download 确认下载。
> 技能包自带空目录 assets/models/,权重不随包分发;离线环境可按 assets/models/README.md
> 手动下载后放入(文件名必须完全一致)。
> assess 不需要任何模型。
可选模型总表(全部 Apache-2.0,全部按需下载,技能包默认不带权重):
| 模型 | 大小 | 用在哪 | 下载触发 |
|---|---|---|---|
| YuNet | 0.2 MB | identity 检脸 |
identity --download |
| SFace | 38.7 MB | identity 人脸特征 |
同上 |
| LaMa (big-lama) | 207 MB | local_edit 大面积修复 |
local_edit ... --download |
| MobileSAM (ViT-T) | 45 MB | local_edit --source point 出掩膜 |
同上 |
推理走 onnxruntime(cv2 自带的人脸模型除外),不引入 torch 之类的重依赖;
缺任一模型时对应功能会自动降级(SAM → GrabCut,LaMa → cv2.inpaint)并打印说明,不会静默下载。
输出格式(向用户呈现的规定动作)
方案呈现与最终交付都按这个结构来:
方案阶段(确认执行门处)
1. 快赢项(零积分,先出):plan 诊断结论 + sheet/variants 风格样张——用 AskUserQuestion 摆出可点选项让用户挑;
2. 高影响项(花积分,需确认):一句话方案「<交付形态> + <模板> + <强度> + <附加项>,预计 ≈N 积分」+ AskUserQuestion 四个可点选项(确认执行/先出小图/改方案/只做零积分);
3. 替代想法:若某选项明显不划算(如 b1 全面占优时再 AI 合成),如实告知并给更省的路径。
> 全部用点选,不要求用户打字:见下方「交互式选择总则」。
交付阶段
1. 成品(/workspace,JPG q95,原始像素尺寸,--icc-from 保留色彩配置);
2. 前后对比图(BEFORE/AFTER 拼图)——enhance / full 默认自动生成,
路径固定为成片同目录下的 <成片名>_对比图.jpg(如 /workspace/成片.jpg → /workspace/成片_对比图.jpg)。
不要再手动 compare 默认输出:它的旧默认值 对比图.jpg 是相对路径,会写进当前工作目录
(通常是技能目录),交付目录里根本没有,用户完全看不到。
左侧取图优先级:--compare-ref(显式指定)> --ref(保真护栏参照)> 输入图。
两者刻意分开——护栏要比的永远是"真正的原图",而对比图有时想对照另一张
(一键流程里对照去水印前的中间图、或同场景另一张参照),不该互相绑死。
可用 --no-compare 关闭、--compare-output 改路径、--compare-height 调清晰度(默认 640)。
full 同样支持这三个开关,它内部转调 enhance 并透传(--compare-ref 可覆盖 --original)。
3. 指标表:成片 vs 原图的 mean/std/sat/颗粒度,对照合格标准逐项打勾;
4. 过程要点:走了哪些步骤、花了多少积分、发现的新坑(如已写回技能)。
> 交付目录里的东西用户才看得到。任何产物只要落在技能目录或临时目录,对新用户就等于不存在。
必问问题(标准四问精简版)
- 交付形态:逐张修 / 合成一张最佳版(整合优点·人物不复制)/ 只修最好的一张 / 先看风格小图再定?
- 风格模板:按照片类型给 3~4 个候选(别抛 450 个名字),最好配
sheet联络表? - 处理强度:轻度画质(推荐)/ 标准含 AI 去瑕疵 / 深度含磨皮?
- 附加项:去路人?去水印?保留 P3 色彩配置?出前后对比图?
→ 完整沟通协议(含话术与积分规避技巧)见 references/planner.md。
交互式选择总则(所有提问都点选,不让用户打字)
> 本技能与用户的每一次问答,都必须是 AskUserQuestion 的可点选项——用户点一下就能推进,
> 不需要打字描述。这是硬性要求,适用于:头脑风暴四问、确认执行门、风格模板挑选、
> 变体定型、返工诊断、位置定位、保护区域选择……任何需要用户做决定的地方。
规则:
- 每个问题都给 2~4 个具体、互斥的选项,直接点选;推荐项放第一个并标"(推荐)"。
- 选项要"看得懂":风格模板写中文模板名(不是
slide--fuji-velvia-50这种 key)、
交付形态写清含义(如"合并成一张(整合优点·人物不复制)"),别让用户去猜。 - 多选场景用
multiSelect: true(如附加项:去路人 / 去水印 / 保留 P3 / 出对比图)。 - 坐标类一律不问数字:用
grid出编号网格图,让用户点选"哪一格/哪几格"(见第 2.2 步)。 - 兜底靠 UI 自带的"其他"输入框——用户有选项外的特殊需求时会自己点"其他"写一句,
我们不在正文里主动要求用户打字,也不主动抛开放式问题(如"你想要什么感觉?")。
遇到主观需求,先给几个贴近的具体风格候选让他选,而不是让他描述。 - 用户已经有明确指令时,不要再问(如用户直接说"用 fresh 模板"就照做),
把提问留给真正需要拍板的岔路口。
调参表(用户反馈 → 参数)
> 这张表是"用户已开口说了症状"时的对症下药;若想在动手前就把坑堵住,
> 先看 references/pitfalls.md §0 自检表(按阶段系统收拢,含本表未覆盖的根因与判据)。
| 用户说 | 调整 | 示例 |
|---|---|---|
| 人物变样/换脸/不像本人 | AI 任务过载 → 拆任务重做 | 多遍 AI + 补丁合成,见 references/prompts.md 第五节;只喂单图+极简指令,禁止多图融合表情与去路人混在一次调用 |
| 画面失真/不像原图/变假 | 回到 blend 保真合成 |
重跑第 2.8 步,降低 AI 改动占比;并务必加 --icc-from。blend 默认已补偿 AI 的全局色调漂移,若仍发灰检查是否用了 --drift off |
| 成片饱和度低于原图(AI 整图去饱和) | 区域化补饱和(已内置) | blend 默认 --region-sat auto:AI 区补到原图彩度水平(上限 ×1.85),原图区 ×1.04。若仍偏低,检查日志里的实际倍率,或手工 --sat-ai-max 调高 |
| 套了预设像没套一样(尤其是富士/古典工艺类) | v1.2 及以前曲线全丢 | v1.3 起已按 XML 解析 Lightroom 曲线(444 个里 153 个带真实曲线)。若仍无变化,用 presets --keyword 换一个带曲线的(套用时日志会打印「含 Lightroom 曲线」) |
| AI 改动覆盖 >60% 画面 | AI 任务过载 | 拆成多遍、一遍一个任务;或用 --protect-box 手动锁主角。此时 blend 已基本失效 |
| 合影里某个人不像本人 | 保护脸不够 | v1.3 起 blend 保护所有检出的脸(含侧脸)。旧版只保护最大的一张。仍漏检时用 --protect-box 补 |
| identity 报相似度低(<0.72) | 有脸被 AI 改动 | 回第 2.8 步重做 blend,确认日志显示"检出 N 张脸…全部纳入保护";改动占比 >60% 说明任务是整图重绘,需拆细 |
| assess 报对比/彩度下降 | 成片发灰寡淡 | --contrast 1.07 --sat 1.07,并确认 --icc-from 原图.jpg 已带 |
| assess 报 SSIM 低(<0.80) | 结构被改动 | 多半是 AI 重绘了大块画面;回 blend,必要时缩小 AI 任务范围 |
| 黑白片 identity 偏低 | 正常现象 | 黑白丢失色彩信息,余弦天然降到 ~0.77;以肉眼与 assess 的结构指标为准 |
| 要去路人/杂物,但怕 AI 把人修走样 | 改走局部编辑(零积分) | local_edit.py remove --box/--point,掩膜外逐字节不动,根本不存在"重绘走样"这回事。见第 2.2 步 |
| 残留细发丝/暗线 | blackhat 发丝模式 | clean out.png --hair(或 local_edit.py remove --source hair);抓不干净降 --hair-sens 0.45 |
| local_edit 报"掩膜外改动非 0" | 不该发生,属 bug | 正常恒为 0.0000%。若非 0 说明羽化/融合逻辑被改动,跑 run_evals.py --filter 局部编辑 定位 |
| 变得灰暗无光 | 冻结暗部 + 补对比饱和 | --shadow 0 --contrast 1.07 --sat 1.07 --icc-from 原图.jpg |
| 发现 iPhone 照片颜色发闷 | 还原色彩配置 | --icc-from 原图.jpg |
| 想要别的风格 | 换模板(450 个可用) | --preset film;检索用 presets --keyword 富士,预览用 sheet |
| 噪点/颗粒多 | 提高降噪强度 | --denoise 9(默认 7);确认降噪在锐化之前、锐化 ≤ 30 |
| 整体阴暗 | 降低 gamma | --gamma 0.78(默认 0.82) |
| 太亮/发白 | 提高 gamma | --gamma 0.92 |
| 暗部死黑 | 加大暗部补亮 | --shadow 0.10(默认 0.07) |
| 天空/高光过曝 | 加大高光压制 | --highlight 0.06(默认 0.03) |
| 发灰不鲜艳 | 提暖 + 饱和度 | --warm 0.03 --sat 1.10 |
| 偏黄/偏色 | 降暖化幅度 | --warm 0.01 |
| 雾蒙蒙 | 提高 CLAHE | --clahe 1.2(勿超 1.3,会放大噪点) |
| 不够锐 | 适提锐化 | --sharpen 40(默认 30,过高颗粒显形) |
适用边界(NOT for)
- 无中生有生成图片:没有输入照片、纯靠文字描述生成 → 不是本技能的事;
- 视频 / 非图片内容:只处理静态图片;
- 多人合影拼图(把不同照片里的不同人拼到一张):可以做,但必须先单独确认人数与站位(铁律 11);
- 批量商业修图流水线:本技能按"会话内小批量"设计,大批量请分批发。
交付 checklist
- [ ] 输出在
/workspace,JPG、质量 95、无二次采样 - [ ] 像素尺寸 = 原图原始尺寸(宽高比未被 AI 改动)
- [ ] 无任何文字/水印/Logo
- [ ] 核心背景地标保留,未被虚化或替换
- [ ] 颗粒度指数落在干净档且不高于原图,亮度落在明快区间
- [ ] 已调
present_files交付 - [ ] 对比度 std 与饱和度不低于原图——这也是一条硬标准
测试用例(evals)
回归测试分两层:
- 行为断言:evals/evals.json(16 条,给 agent 读的)——覆盖头脑风暴四问、确认执行门、
多图合成不复制人物、人物变样返工、风格切换零积分等关键行为,改动本技能后应逐条对照。
其中第 9/10/11 条是 1.3.0 针对历史缺陷补的(曲线丢失导致预设无效、full跳过 blend、合影只保护最大一张脸);
第 15 条是 1.5.18 补的(动手前对照 pitfalls.md §0 自检表,而非临时判断);
第 16 条是 1.5.19 补的(所有提问都是可点选项、不让用户打字——含 grid 编号定位)。 - 数值断言:
python3 scripts/run_evals.py(60 条,可执行的回归)——曲线精度/单调/去重、
漂移估计误差、彩色掩膜优于灰度掩膜、强度档位与硬性上限、EXIF 摆正、护栏与full告警、
区域化补饱和达标(成片彩度 ≥ 原图)、局部编辑"掩膜外逐字节不动"、发丝掩膜精度、
过曝/发白双向护栏与 plan 过曝自适应、经验文档一致性(pitfalls/scenes 结构完整)、
交互式选择合规与 grid 编号定位、
以及 assess/identity/SAM/LaMa 冒烟(缺模型时自动跳过)。fixture 由固定种子(42)生成,结果可复现。
改完photo_toolkit.py/local_edit.py必须跑一次,退出码非 0 就是有回归。
可选:--filter 局部编辑只跑一类;--filter 交互只跑交互式选择用例;
--face 含人脸的照片.jpg让身份门禁用例也跑起来。 - 场景矩阵断言:
python3 scripts/scene_evals.py(13 条)——答「场景会不会翻车」:
7 个确定性 fixture(种子 20261008)覆盖 12 类场景的识别 + 正向出片断言(暗部抬起/暖调保持/不溢出) - 反向护栏(night 不得引入紫斑控件、亮云高光必须染暖、错误染蓝在真实图上必出紫)
- 边界用例(用真实照片签名直测:合影不得被夜景/舞台/风景抢走、逆光不得被判夜景)。
改完scenes.json/ 场景识别逻辑 / 夜景预设必须跑一次。
可选:--scene night只跑一类;--brightsky 真实亮云图.jpg让真实照片紫斑护栏也跑起来。 - 经验文档一致性:
python3 scripts/run_evals.py --filter 清单——守护 references/pitfalls.md
与 references/scenes.md 的结构完整(章节/自检表/速查表齐全,
SKILL.md 有指向它们的链接),防止"经验文档被误删或被改烂"。 - 交互式选择合规:
python3 scripts/run_evals.py --filter 交互——守护"所有提问都是可点选项、
不让用户打字"(禁止自由文本式提问、确认门四选项齐备、planner 有返工诊断与 grid 报格定位),
外加grid子命令的编号与坐标映射正确性。