照片改尺寸压体积(photo-x)
把本地照片裁成精确到像素的目标尺寸,并压到指定 KB 以内;还能把纯色或近纯色背景换成指定颜色、或去掉背景出透明 PNG。三件事可以一次调用串起来,也可以只做换底。全程纯本地离线,原图不出本机。
触发条件
该触发(用户真实会说的原句)
- "我这个报名照片系统一直提示超过 20KB,帮我压一下,尺寸还是要 295×413 的"
- "把这张图改成 800×800 的方图,电商主图用"
- "二寸照片是多少像素?把这张照片改成二寸的"
- "护照照片是 390×567 吧?帮我裁成这个尺寸,顺便压到 50K 以内"
- "这张图要传司法考试报名,413×626,不能超过 100KB,帮我弄好"
- "帮我看下这张照片符合不符合教资报名的要求,不符合哪里不符合"
- "这张白底照帮我换成蓝底"
- "红底太丑了,换成白底"
- "帮我把这个商品图的背景去掉,出透明 PNG"
- "换成 #438EDB 的蓝"
- "换蓝底再压到 20KB,295×413"
- "这图能换底吗?背景有点乱"
只要请求里出现"本地图片"与下列任一要素就走本技能:像素尺寸、体积上限、纯色背景替换或去背景。
不该触发(真正的越界请求,必须拒答后引导)
- "帮我把这张图智能抠一下,AI 那种" → 本技能无 AI、无模型,能力上限是"颜色相近且与边框连通的区域";请用专业抠图工具
- "发丝要一根根抠干净" → 无 matting 能力,只保 1–2px 边缘,如实说明
- "背景换成埃菲尔铁塔 / 换成渐变 / 换成虚化" → 只支持纯色与透明,图像编辑类技能
- "把照片里那个路人 P 掉" → 非背景替换,是对象移除,图像编辑类技能
- "把我脸磨个皮、修瘦点" → 不做修图
- "帮我生成一张证件照"(没有源图)→ 无生成能力
- "把这些照片排到 6 寸相纸上一版打印" → 拼版打印,本技能非目标
引导话术要点:一句话说清能力边界——"几何(尺寸)、编码(体积)、纯色背景替换三件事我做,AI 抠图、发丝级 matting、换背景图为图片、修人我不做",并且不执行任何命令、不产出任何文件。禁止无 mask 的整图覆盖(例如铺一个白矩形冒充换底);合法换底必须经边界生长得到的 mask。禁止出现"任意背景""一键智能抠图""发丝级""保证通过审核"这类措辞。
工作流程
- 抽取要素:从用户话里取出六项——输入路径、目标规格(像素尺寸或预设名)、体积上限、输出格式、裁切重力、背景目标色(或"透明")。路径或规格抽不出来就追问,禁止猜;体积上限缺失即视为"不做体积承诺";背景目标缺失即视为"不换底"。
- 消歧:规格名有歧义时先查库再追问(
presets拿候选,把差异逐条列给用户,不得替用户选)。"蓝底"同样要消歧:把标准证件蓝#438EDB与天蓝、浅蓝、宝蓝并列给用户确认,未确认不得开工,更不许自己编一个色值。 - 预判:先跑
bg --dry-run。它会真的走一遍掩膜、合成与残影测量(只是不编码、不写盘,约多花 0.1 秒),所以结论与实跑一致——包括"合成后边界残影超限"这类只有合成才知道的失败。闸门命中就把 bg.gate、实测值、阈值与三条建议照实播报,不要产出;good/marginal再实跑。 - 执行:只要图不改尺寸走
bg;要图且要改尺寸/压体积走fit(可加--bg-to一次完成);只要结论走check;一个目录一批走batch。裁切策略按场景选:证件照用cover(不留白边),需要主体完整的产品图用contain(留底色),stretch只能由用户明确要求。 - 读报告:用
--json拿结构化报告,逐项读 14 个字段 +bg扩展块。放大倍数超过 2 时提示画质风险;within_target为假时不得说成"已达标";bg.applied为假时不得说成"已换底色"。 - 解释结果:把脚本给出的数值翻译成人话,例如"高度比要求多 287 像素,需要往下裁"、"背景占比 77%,边缘残留 0";数值本身不许改写、不许估算。
三条硬规则(v1.0.0 起)
- 禁止口算像素比例、裁切偏移或换底覆盖率——一律交给脚本,LLM 只读结果。
- 禁止估算压缩后体积——体积只能来自脚本的实测字节数。
- 禁止把
within_target为假说成"差不多达标"或"已达标"——这是最严重的误读,会导致用户上传被驳回。
三条新硬规则(v1.0.2 起,与上面三条并列)
- 禁止口算或猜测容差、覆盖率、边缘残留——一律读
bg块的tolerance_used、coverage_ratio、edge_residual。 suitability为poor且未加--bg-force时,禁止宣称"已换底"——此时零产物、退出码 13。bg.applied为假时禁止说成"已换底色"——要说"背景已是目标色,未做改动"。
命令参考
脚本入口是 scripts/photo_size_fit.py,七个子命令,全部相对技能目录执行。
查询预设(不改图):
python scripts/photo_size_fit.py presets --query 二寸 --json
python scripts/photo_size_fit.py presets --query 护照
示例中的规格与数值仅为演示,规则与具体规格无关:任何尺寸 / 任何体积上限 / 任何格式都走同一流程。
一步到位改图(裁切 + 压体积 + 落盘 + 报告):
python scripts/photo_size_fit.py fit --input photos/id.jpg --size 295x413 --target-kb 20 --json
python scripts/photo_size_fit.py fit --input photos/id.jpg --preset 二寸 --target-kb 20
python scripts/photo_size_fit.py fit --input photos/shoe.jpg --size 800x800 --strategy contain
示例中的规格与数值仅为演示,规则与具体规格无关:任何尺寸 / 任何体积上限 / 任何格式都走同一流程。
换底 + 改尺寸 + 压体积一次完成(fit --bg-to):
python scripts/photo_size_fit.py fit --input photos/id.jpg --bg-to blue --size 295x413 --target-kb 20 --json
python scripts/photo_size_fit.py fit --input photos/goods.jpg --bg-to transparent --format png --size 800x800
示例中的规格与数值仅为演示,规则与具体规格无关:任何尺寸 / 任何体积上限 / 任何格式都走同一流程。
只换底(不动尺寸):
python scripts/photo_size_fit.py bg --input photos/id.jpg --to blue --json
python scripts/photo_size_fit.py bg --input photos/id.jpg --to transparent --format png
python scripts/photo_size_fit.py bg --input "photos/证件 照片.jpg" --to "#438EDB" --output-dir out --json
python scripts/photo_size_fit.py bg --input photos/office.jpg --to blue --dry-run --json
示例中的规格与数值仅为演示,规则与具体规格无关:任何尺寸 / 任何体积上限 / 任何格式都走同一流程。
只诊断不改图(核对规格):
python scripts/photo_size_fit.py check --input photos/id.jpg --size 295x413 --target-kb 20 --json
示例中的规格与数值仅为演示,规则与具体规格无关:任何尺寸 / 任何体积上限 / 任何格式都走同一流程。
批量同规格:
python scripts/photo_size_fit.py batch --input-dir photos --glob *.jpg --size 295x413 --target-kb 20
示例中的规格与数值仅为演示,规则与具体规格无关:任何尺寸 / 任何体积上限 / 任何格式都走同一流程。
自检与测试图集(交付验收用,不参与业务):
python scripts/photo_size_fit.py selftest
python scripts/photo_size_fit.py fixtures --out .psf-fixtures
示例中的规格与数值仅为演示,规则与具体规格无关:任何尺寸 / 任何体积上限 / 任何格式都走同一流程。
参数怎么选(通用原则,不看具体数值)
| 用户诉求 | 选择 |
|---|---|
| 证件照、能裁掉边缘 | --strategy cover(默认,不留白边) |
| 主体必须完整、允许留底色 | --strategy contain 配合 --bg-color |
| 用户明确说"拉伸到" | --strategy stretch,并当场说明比例会失真 |
| 只想把人脸/主体靠上或靠下 | --gravity top 或 bottom,共五种取值 |
| 说不清具体规格 | 先 presets --query 出候选,再回来 fit --preset |
| 只想知道合不合格 | check,不要跑 fit |
| 只换底色、不动尺寸体积 | bg |
| 换底 + 改尺寸 + 压体积 | fit --bg-to |
| 只想问"这图能换底吗" | bg --dry-run,只读零产物 |
两个底色参数别搞混:--bg-color = 留白/铺底的底色(contain 留白、透明输入铺底);--bg-to = 把照片原有背景替换成什么。两者可同时给,语义独立。
子命令选型:一次一张图要成品走 fit;只要诊断结论走 check;同目录多张同规格走 batch;只换底走 bg;只查规格数值走 presets;验收技能本身走 selftest 与 fixtures。
最常用的 6 个参数:--input 输入路径、--size 宽x高、--preset 预设名(与 --size 互斥)、--target-kb 体积上限(按 1000 字节计)、--strategy 裁切策略、--gravity 裁切重力;换底再加 --to(bg)或 --bg-to(fit)。全参数表、报告字段逐项说明、退出码含义见 references/cli-reference.md。
输出与报告
默认输出文件名:fit 恒为「原文件名主干_宽x高.jpg」;给了体积上限则「原文件名主干_宽x高_20kb.jpg」;同名冲突自动递增为 -2、-3,--force 才覆盖。bg 用「原文件名主干_bg<目标>[_20kb].jpg」,例如 id_bgblue.jpg、goods_bgtransparent.png、id_bgrgbfff000.jpg。输出目录默认与输入同目录,可用 --output-dir 指定。
报告 14 字段(缺一不可)
| 中文字段 | JSON key |
|---|---|
| 输入路径 | input_path |
| 输出路径 | output_path |
| 原尺寸(旋正后) | source_size |
| 目标尺寸 | target_size |
| 策略 | strategy |
| 重力 | gravity |
| 放大倍数 | upscale_ratio |
| 实际质量 | quality |
| 输出字节数 | output_bytes |
| 输出KB(1000) | output_kb1000 |
| 输出KB(1024) | output_kb1024 |
| 目标体积 | target_kb |
| 是否达标 | within_target |
| 备注 | notes |
bg 子命令复用同一套 14 字段(target_size 恒等于 source_size,strategy 与 gravity 恒为 none),全技能只有一个报告 schema。
换底扩展块 bg(20 键恒在,不计入 14)
requested / applied / mode / target_color / detected_bg_color / suitability / suitability_score / gate / tolerance_used / tolerance_source / feather_px / coverage_probe / center_coverage / mask_scale / coverage_ratio / engine / degraded / degradation_reason / forced / edge_residual。
没请求换底时这 20 个键照样存在(全 null / false),消费者不必判空。另有一个顶层键 dry_run(bool,恒在),标识"本次只预判、没有产物"。逐项含义见 references/cli-reference.md。
coverage_probe 与 coverage_ratio 是两个数,永远别互相顶替:前者是闸门依据(用 auto 参考容差算出的参考掩膜),后者是这次执行的掩膜。用户不传 --tolerance 时两者相等(同一张掩膜,零额外成本);一传 --tolerance 就会分开——这就是"用户参数进不了适配性判定"的可观测证据。
退出码速查
| 码 | 含义 |
|---|---|
| 0 | 全部约束达成 / check 合格 / presets 命中 |
| 1 | 未捕获异常 |
| 2 | 参数错误 |
| 3 | 输入不可用(不存在 / 不可解码 / 格式不在白名单 / 疑似 HEIC) |
| 4 | 输入超过 100MB 本地处理上限 |
| 5 | 体积不可达(零产物,给实测最小可达体积与三条建议) |
| 6 | 产物回读校验不通过(已删产物) |
| 7 | 写盘失败 |
| 8 | 批量至少一张失败 |
| 9 | check 判定不合格(仍输出完整原因) |
| 10 | 预设无匹配(仍输出最近候选) |
| 11 | 无损格式不接受体积上限 |
| 12 | 像素总数超过 1.5 亿(输入原图、目标尺寸或 cover 中间图三者任一) |
| 13 | 换底被拦(见下方三种触发),零产物 |
退出码 13 有三种触发,都是零产物:
- 预判:
bg.suitability为poor且未--bg-force(颜色不像纯色,或背景占比 >90%,或中心区被吃掉 >60%)——在任何建目录/编码/写盘之前拦。对应 bg.gate 为color/coverage/center。 - 事后残影:
bg.suitability为marginal且真的换了底,但合成后bg.edge_residual > 15%且未--bg-force——同样在写盘之前拦。对应bg.gate=residual。 - 四角没换干净:合成后四角 20×20 块内仍有原背景色(每像素与目标色最大通道差 >3)且未
--bg-force。对应bg.gate=corner;这一条可能发生在suitability为good或marginal时,通常因为--bg-tolerance调得过紧。
good 档残影超 15% 不拦,只写一条 warning(好图不因量化抖动被拒)。--bg-force 同时绕过以上三类拦截。
非 0 退出时,带 --json 的 stdout 仍是合法 JSON(含参数错误、像素超限、两类背景不适配三条路径),错误信息在 error 字段里;stderr 只有一行中文摘要。
落盘与静默:--json-out 可把报告另存(路径没有扩展名即视为目录,写"主干.report.json"并自动建目录;带扩展名则按该名字写),编码恒为 UTF-8,不受终端代码页影响;--quiet 只压掉人类可读摘要,JSON 落盘照常执行。两者可以叠加,适合批处理脚本调用。
不可达时怎么谈
体积不可达时报告会带 min_reachable(实测最小可达体积)与恰好 3 条 suggestions,必须先把实测值播报出来,再把三条路并列成选项让用户选,不要替用户决定:
- 放宽体积:把上限调到实测最小可达值以上——最省事,画质不变。
- 缩小像素:会破坏"尺寸精确",必须用户明确同意后才加
--allow-downscale重跑;此时报告里尺寸精确标志为假。若用户已经加了该参数仍不可达,脚本会把建议改成"放宽体积 / 转灰度 / 换更小规格",并且用缩小后的实际尺寸措辞——照读即可,不要再劝他加同一个参数。 - 转灰度:加
--grayscale重跑,需确认接收方接受黑白照片;灰度只去掉色度码率,收益随图片内容而定(高彩度照片较大,近中性照片很小甚至接近零),不许自己编任何比例,一律以报告里的实测字节为准。
三条建议都由脚本生成、数值实测,转述时不许改数字,也不许只挑一条说。
换底被拦时怎么谈
被拦(退出码 13、零产物)时必须自报家门:哪一道闸门(bg.gate)+ 实测值 + 阈值 + 人话解释,一个都不能少。只回一句"背景不适配"视为误述。
注意:bg.suitability 可能是 good 却仍然退出 13——那是 gate=corner(合成后四角还有原背景色,通常因为容差调得过紧)。这不是 bug,不要说成"图片不适合",要说"这次没换干净,四角实测 X,与目标色差 Y"。
| bg.gate | 触发 | 播报时必须带 |
|---|---|---|
color |
bg.suitability_score < 50(等价 d95 ≥ 51) |
d95 实测值与阈值 50、bg.detected_bg_color |
coverage |
bg.coverage_probe > 0.90 |
覆盖率实测值与阈值 90%、bg.center_coverage(若也超限) |
center |
bg.center_coverage > 0.60 |
中心 20% 区实测值与阈值 60%、bg.coverage_probe(若也超限) |
residual |
marginal 档合成后 bg.edge_residual > 0.15 |
残影实测值与阈值 15%、bg.tolerance_used |
corner |
合成后四角仍有原背景色(每像素与目标色最大通道差 > 3) | 最差角落像素 RGB、与目标色的最大通道差、bg.tolerance_used(多半是容差调得太紧) |
两条以上同时超限时:bg.gate 只记第一个(顺序 color → coverage → center → residual → corner),但播报必须把另一个已超限的实测值也报出来。脚本生成的消息里已经带了,照读即可。
三条建议的固定顺序(脚本生成,恒 3 条,全说):
- 说清是什么失败 + 实测值;
- 可执行的下一步(按
gate分流:换纯色背景重拍 / 先把主体拍大或裁掉多余背景 / 把--tolerance调大而不是调小); --bg-force只能排第 3,且必须复述"强制产出、不改善质量"。
--bg-force 是给"我知道风险"的人用的开关:它只改变结论是否被采纳,不改变结论本身,也不会让结果变好。播报时必须原样说明 bg 块的 forced 为真、背景可能不干净、请目视检查成品;不得把它写进任何"推荐用法",也不得说成"解决办法""修复"。
marginal 档放行时,必须原样播报这条 warning(加了 --bg-force 也不许省):
> 背景为临界纯色(适配分 N/100,d95=N):能换,但边缘质量可能不稳。若原图背景是渐变、带暗角或带阴影,换色后边缘可能残留一圈晕染,请放大目视确认。
所以 marginal 档的播报必须附一句"请放大看一眼边缘",且不得说"已经干净了""边缘没问题"。
bg.edge_residual 的口径必须连口径一起讲(否则会被当成"边缘质量分"):
> edge_residual 是绝对口径——「边界 ±3px 带内,仍与原背景色距离 ≤8 的像素占比」。它只检测掩膜偏紧留下的近原色残环,不检测渐变/暗角造成的远距离晕染(那类残影距原背景色有 20–60,不在 ≤8 口径内)。因此它不是万能质量分:数值低 ≠ 边缘一定干净。
也因此不许承诺"白边一定被拦"——闸门 2 的价值是堵住"用户自己把容差调坏"这条最常走的路,不是替代人工检查。coverage_ratio 低于 2% 仍会进 warnings[](不阻断),必须转述。
能力边界(必须照实说,不许美化)
| 场景 | 效果 | 怎么说 |
|---|---|---|
| 白墙 / 蓝布 / 纯色背景前拍的人像、影棚图 | 好用(推荐) | "这类图效果最好" |
| 白底 / 纯色底商品图去背 | 好用 | 同上 |
| 浅色背景但光照不均(暗角、阴影) | 可用,边缘可能略不净 | "可试,若边缘不净建议重拍或换更均匀的背景" |
| 复杂背景(街景、室内杂物、书架、窗外) | 效果有限,应提前劝退 | "本技能不做 AI 抠图,这类背景请用专业工具或重拍" |
| 发丝、毛绒、透明衣物、纱、细网格 | 效果有限 | "边缘细节会丢,不要期待一根根发丝" |
| 主体自身颜色与背景接近(白衬衫 + 白墙) | 有风险 | "可能误删主体边缘,请检查成品" |
| 主体很小的图(主体 <10% 画面) | 会被拦(coverage) |
"主体太小会被判为背景,先裁掉多余背景或拍近一点" |
| 任意背景一键抠图 | 不承诺 | 严禁出现"任意背景""一键智能抠图"这类措辞 |
边界与不做的事
- 不做人脸检测、关键点识别、自动构图——重力只支持
center/top/bottom/left/right,不承诺"自动把人脸放中间"。 - 不做发丝级 matting、语义分割、任何 AI 模型——边缘细节有限,如实说明,不承诺。
- 不做背景替换为图片——换成风景、虚化、渐变一律拒答,只支持纯色与透明。
- 不做对象移除——把背后的人 P 掉属于图像编辑,拒答。
- 不做图像生成——没有源图就不能造图。
- 不做修图——美颜、磨皮、瘦脸、滤镜一律拒答。
- 不做排版打印——拼版、相纸排版属非目标。
- 不做任何联网行为——不下载模型、不上传原图、不调用任何在线接口。
- 不处理 HEIC/HEIF 与 RAW——明确报错并建议先转 JPG 或 PNG,禁止静默失败。
- 不承诺通过审核——只保证像素 / 体积 / 格式 / 背景色四项硬指标。
- 不做无损体积承诺——透明 PNG 给体积上限时直接拒绝(退出码 11),不偷偷转 JPG 交差。
- 不做批量里的"自动挑图"——
batch逐张判,不适配的逐张报 13,不因一张失败中断整批。
注意事项
- 中文路径:全程支持中文与空格路径;Windows 控制台若显示乱码,加
--encoding gbk重跑,不影响产物。 - 透明 PNG 转 JPG:透明区域铺白底,报告备注会写"已铺白底"。这是唯一正确做法,绝不能用直接转 RGB 的方式(会把透明区变黑)。
- 透明输出只允许 PNG:
--to transparent配--format jpg或--format webp一律参数错误(退出码 2),不会"悄悄转成不透明"。 - EXIF 方向:先按方向标签旋正像素再算尺寸,输出不含任何 EXIF(含 GPSInfo),原图定位信息不会外泄。换底的边带统计也在旋正之后做。
- 大图:20MB 以内正常处理;20MB 到 100MB 之间照常处理但会打"大图"标记、耗时数秒;超过 100MB 直接拒绝并提示先压缩。
- 不可达不许凑合:体积压不下去时只报不可达 + 建议,绝不产出超标文件却宣称达标。
stretch会失真:只有用户明确要求才用,报告里proportion_distorted会标真,转述时必须说"比例已失真"。- 换底大图耗时:12MP 图(如 3000×4000)换底,进程内计算约 2.8 秒,加上解释器启动与模块导入,命令行端到端约 3.5–4 秒(实测 3.8–3.9 秒),比常规改尺寸慢,属正常;掩膜会自动降采样到长边 1600 再算,报告里
bg.mask_scale如实写(1.0 = 未缩放)。常见证件照尺寸(1200×1600 级)端到端约 1.8 秒,无 numpy 时约 2.3 秒。 center_coverage是什么:画面中心 20% 区域被判为背景的比例。超过 60% 说明主体核心区(脸、胸、主体中心)被吃掉了,脚本会直接拦(退出码 13),不要试图用参数绕过。- 两道闸门都在写盘之前:拦下来时输出目录一个文件都不会新增,不是"先写了再删"。看到退出码 13 就去讲原因,不要说"生成失败请重试"。
- 换底依赖:只有 Pillow 是硬依赖;numpy 有则加速、无则自动降级,报告
bg块的 engine 与 degraded 会如实写。缺库不会崩,只会慢一些。
预设库是 2026-10 快照,尺寸以官方最新公告为准;22 条全表与已知瑕疵说明见 references/presets-notes.md。中文路径、编码、HEIC、透明图、超大图、带 GPS 的 EXIF、以及换底结果不干净怎么排查,见 references/troubleshooting.md。依赖见 requirements.txt,不需要任何网络与账号。