照片滤镜工坊
给一张已有照片套上专业级调色滤镜,让它看起来像专业出片——只重做色彩与影调,不改画面内容。
| 项目 | 说明 |
|---|---|
| 输入 | 单张 JPEG、PNG 或 WebP(每通道 8 位) |
| 输出 | 新图片,默认命名「原名_滤镜名.原后缀」,与原图同目录 |
| 滤镜 | 18 款,分 8 组;强度 0—1 连续可调,默认 0.75 |
| 运行依赖 | Python 3.10+、Pillow、numpy |
| 网络 | 不需要,照片全程不出本机 |
| 原图 | 绝不覆盖、不移动、不删除 |
| 拍摄地点 | 默认随 EXIF 原样保留;用户要求时可只删定位信息 |
| 费用 | 零,纯本地计算 |
何时使用
用户提出下列诉求时启用:
- 给这张照片加滤镜 / 帮我加个滤镜
- 帮我调色 / 把这张照片调个色 / 换个色调 / 换个风格
- 调成专业照片 / 调得专业点 / 像专业摄影师拍的
- 调好看点 / 这照片太平了 / 太灰了 / 没感觉
- 加个日系、黑白、电影感、胶片样子的滤镜
- 有什么滤镜 / 有哪些滤镜 / 滤镜菜单
- 统一一下色调
下列诉求不启用,并向用户说明原因:
| 诉求 | 为什么不启用 |
|---|---|
| 修瑕疵、磨皮、美颜、抠图、去水印、去文字 | 属于图像修复与合成,本 Skill 只做调色 |
| 生成一张新照片、风格重绘、AI 换画风 | 属于图像生成,本 Skill 只处理已有照片 |
| 处理视频 | 本 Skill 只处理单张静态图片 |
| 一次处理整个文件夹 | 本版本按张处理,逐张执行、逐张汇报,没有整批事务 |
标准流程
用户给出照片后,按顺序走完,不要跳步:
- 检查环境。运行
python scripts/filter_engine.py --check。返回ready直接进入第 2 步;needs_setup时按@references/setup-guide.md装齐图像库后重跑,期间不处理照片。 - 分析照片。运行
python scripts/filter_engine.py analyze --input "<照片路径>"。这一步只读不写,不生成任何图片。 - 给建议并等确认。把分析结果翻译成人话讲给用户:这张照片有什么问题(偏色多少、偏暗偏亮、灰塌程度)、自动校正会动什么、推荐哪 2—3 款滤镜以及为什么。等用户选定后再出图,不要自作主张直接出。
- 套用滤镜。运行
python scripts/filter_engine.py apply --input "<照片路径>" --preset "<滤镜名>"。 - 汇报结果。用摘要里的数字说明改了什么:滤镜名、强度、校正是否开启、前后平均亮度与黑白场变化、元数据状态、原图未被修改。摘要里
location_note或quality_note非空时必须转告用户。 - 支持继续微调。用户说「再暖一点」「暗部再压一点」「淡一点」时,运行
adjust并把调整方向写进--delta。每一次都在原图上重算,不在上一版成品上叠加。
用户只想快速出图、不想听分析时,跳过第 2、3 步直接 apply,但在汇报里仍要说明用了哪款滤镜、强度多少、原图未动。
命令与参数
python scripts/filter_engine.py --check
python scripts/filter_engine.py --version
python scripts/filter_engine.py list
python scripts/filter_engine.py analyze --input "<照片路径>"
python scripts/filter_engine.py apply --input "<照片路径>" --preset "<滤镜名>"
python scripts/filter_engine.py apply --input "<照片路径>" --preset "<滤镜名>" --strength 0.65
python scripts/filter_engine.py adjust --input "<照片路径>" --preset "<滤镜名>" --delta "warm+2;shadow-1"
python scripts/filter_engine.py forget
| 参数 | 默认 | 说明 |
|---|---|---|
--input |
必填 | 单张 JPEG、PNG 或 WebP 的路径 |
--preset |
必填(adjust 可省略) |
滤镜中文名或英文 id;adjust 省略时用偏好记忆里最常用的一款 |
--strength |
0.75 | 强度 0—1,与原始像素线性插值;为 0 时原样输出,可逆 |
--delta |
无 | 微调指令,分号分隔,形如 warm+2、shadow-1、fade+1 |
--output |
原名_滤镜名.原后缀 | 显式输出路径。输出容器与输入格式保持一致(本 Skill 只调色、不转换格式),因此扩展名只能是 .jpg、.jpeg、.png、.webp 中与输入对应的那一个;目标已存在时拒绝覆盖,指向目录时按参数错误处理 |
--quality |
95 | JPEG 与 WebP 输出质量 1—100;PNG 一律无损,该值被忽略并在摘要中说明 |
--seed |
0 | 颗粒随机种子,取值范围 0 到 2^63-1;同种子结果逐字节可复现 |
--strip-location |
关 | 只删输出元数据里的拍摄地点,机型、光圈、快门等其余标签保留 |
--no-autocorrect |
关 | 关闭基础校正(白平衡、曝光与对比、去灰),风格直接叠在原图上 |
--no-remember |
关 | 本次不写入偏好记忆 |
所有参数错误都以结构化 JSON 返回,不抛技术栈回溯。--delta 可用项:warm、cool、magenta、green、bright、dark、shadow、highlight、contrast、fade、sat、clarity、grain、vignette,数字表示档数,正负表示方向。
强度怎么给:用户说「淡一点、自然一点」用 0.5—0.65;说「浓一点、更彻底」用 0.85—1.0;用户没表态就用默认值,不为此打断追问。
内置滤镜
| 组 | 滤镜 |
|---|---|
| 人像 | 通透奶油、暖肤、冷调高级灰 |
| 风景 | 通透蓝、浓郁青橙、朦胧雾感 |
| 美食 | 暖香 |
| 夜城 | 赛博霓虹、冷蓝夜 |
| 日系清新 | 日系清透、空气感 |
| 电影感 | 电影青橙、电影青绿、复古电影 |
| 黑白 | 经典黑白、棕调黑白 |
| 胶片 | 柯达暖、富士清冷 |
每款都是独立配方(影调曲线 + 选择性色彩 + 分离色调 + 肤色保护 + 双尺度微反差 + 颗粒与暗角),参数依据与适用场景见 @references/presets.md。
通过条件与失败分支
通过条件:摘要 ok 为 true;输出文件存在且像素尺寸与原图一致;原图的修改时间与内容均未变化。
| 情况 | 判定 | 处理 |
|---|---|---|
| 退出码 0、输出存在、分辨率一致 | 通过 | 按第 5 步汇报 |
退出码 2、error_code 为 input_error |
输入错误 | 按返回的可读原因提示:路径是目录、文件不存在、扩展名不支持、扩展名与文件内容不一致、16 位或非 8 位、像素数据被截断、解码失败或并非图片 |
退出码 2、error_code 为 size_limit |
尺寸超限 | 说明是像素数超限并非文件损坏,请先缩小尺寸再调色,不生成文件 |
退出码 2、error_code 为 unknown_preset |
滤镜名错误 | 运行 list 展示可用滤镜名,让用户重选 |
退出码 2、error_code 为 invalid_arguments |
命令行参数无法解析 | 按返回的提示改成合法写法后重跑;这一项也保证不输出技术栈回溯 |
退出码 2、error_code 为 invalid_strength、invalid_quality、invalid_seed 或 invalid_delta |
参数错误 | 按返回提示改成合法值后重跑 |
退出码 2、error_code 为 invalid_output |
输出参数错误 | 输出扩展名不合法、与目标格式不一致,或输出路径是目录;改成合法路径后重跑 |
退出码 2、error_code 为 output_exists |
冲突 | 换输出名,或先移走旧文件;默认命名撞名时同样处理 |
退出码 2、error_code 为 alpha_vs_jpeg |
格式与透明通道冲突 | 输出改成 .png,不要静默丢透明度 |
退出码 2、error_code 为 libraries_missing |
依赖缺失 | 按 install 字段给出的命令装齐后重跑,不生成任何图片 |
退出码 3、error_code 为 memory |
失败 | 说明读取时内存不足,请先缩小尺寸;原图未被修改 |
退出码 3、error_code 为 write_failed 或 processing_failed |
失败 | 保留原图,如实报告错误原因,不声称成功 |
退出码 3、error_code 为 runtime_error |
运行前提不满足 | 例如解释器版本低于 3.10;按提示处理后重跑 |
退出码 3、error_code 为 interrupted |
被中断 | 操作被中断,原图未被修改;可直接重跑 |
退出码 3、error_code 为 preference_write_failed |
记忆写入失败 | 偏好记忆无法写入,不影响出图;可修复目录权限或直接忽略 |
环境检查
运行 python scripts/filter_engine.py --check,按 status 路由:
ready:跳过安装说明,直接调色。needs_setup:只展示install字段缺哪几条,装齐后重跑自检。
判断图像库是否就位一律以 --check 为准。 python scripts/check_environment.py 校验的是依赖清单完整性与 Python 版本,它含图像库相关的配置说明但不做模块导入探测,报 ready 不等于 Pillow 与 numpy 已就位。需要复核依赖清单声明时再运行它。
降级范围
- 图像库缺失:不执行调色,只输出安装指引,不生成图片,照片保持原样。
- 扩展名必须与文件内容一致:只有扩展名是
.jpg、.jpeg、.png、.webp才会交给解码库,且解码结果必须与扩展名对应。把 PNG 改名成.jpg这类做法会被拒绝,本 Skill 不做格式转换——因为输出格式与解码结果必须一致,静默转码会悄悄改变位深与透明度。 - RAW、HEIC 与每通道超过 8 位:拒绝处理并说明只支持 8 位 JPEG、PNG、WebP;16 位 PNG 会被明确识别,不会被悄悄截断。
- 基础校正关闭或失败:直接在原图上叠风格,结果会保留原片的偏色与灰塌;汇报时说明本次未做基础校正。
- 分析失败:跳过推荐,请用户直接点名滤镜;此时不给推荐理由。
- 元数据不可信或写入失败:照常输出调色结果,摘要中说明元数据未保留,不声称失败。
- 只删定位失败:定位结构不可信时不动任何元数据,照常输出,
location_stripped记为 false,并如实告知拍摄地点仍在输出里。 - 像素数据被截断或不完整:判为输入错误(
input_error),退出码 2,不生成任何文件,也不会抛出技术栈回溯。 - 原图无 EXIF:正常调色,摘要中
exif_kept记为 false。 - 拍摄地点:默认随其余元数据原样写出;
location_note非空时必须主动提醒用户并提供--strip-location。 - PNG 输出时传了
--quality:该参数对 PNG 无效,quality_note会写明,应当转告用户。 - 原图带透明通道:输出保留透明通道;被要求输出为 JPEG 时先报错,不静默丢透明度。
- 超大图:处理前告知用户需要等待。耗时随像素数上升,实测 6000 万像素约 1 分钟量级;超出解码库防护上限时按
size_limit处理,说明是尺寸超限而非文件损坏。 - 偏好记忆不可写:跳过记忆,正常出图,摘要中说明记忆未启用。
- 用户拒绝安装依赖:停止调色流程,说明继续所需条件,不用低质量结果顶替。
边界与禁止
- 不覆盖、不移动、不删除任何原图;输出目标已存在时直接拒绝。输出以独占方式创建,同名并发写入只有一个能成功。
- 不改动画面内容:不磨皮、不合成、不重绘、不改构图、不增减元素、不丢透明通道、不做位深降级。
- 不擅自增减元数据:默认把 EXIF(含拍摄地点)原样写出;只有用户要求时才加
--strip-location,且只删定位。 - 不联网、不上传照片、不写输出目录以外的位置,唯一的例外是本 Skill 自己的偏好记忆文件。
- 不做目录级批量事务:不整批读取、不整批写出、不整批回滚,也不移动或删除任何文件。
- RAW、HEIC 与视频不处理。
- 用户要求风格重绘时,说明本版本只做已有照片的调色,不擅自扩权。
偏好记忆
- 位置:用户主目录下的
~/.workbuddy/photo-filter-craft/preferences.json,放在 Skill 包外,更新 Skill 时不会被清掉。 - 内容:只有三项——用过的滤镜名与次数、近 20 次的强度取值、微调方向计数。不记录照片路径,也不记录照片内容。
- 用途:
analyze的推荐会优先照顾用户惯用的滤镜;adjust省略--preset时用最常用的一款。 - 关闭与清空:单次不记用
--no-remember,彻底清空用forget。
质量自查
汇报前逐条确认:
- 输出文件已生成,分辨率与原图一致
- 原图修改时间与内容未变化
- 摘要
after中黑位不低于 5、白点不高于 250,未出现纯黑或纯白像素 - 摘要
after中阴影与高光的剪切比例没有明显上升 - 画面有肤色时,
after.skin_hue_deg落在 5—46 度之间 - 原图带透明通道时输出仍带透明通道
location_note非空时已把「输出带拍摄地点」告知用户,并给出--strip-locationquality_note非空时已说明--quality对本次 PNG 输出无效memory_note非空时已说明偏好记忆未启用- 汇报中写清了滤镜名、强度,以及「只调色、未改内容、未覆盖原图」
更完整的验收口径与判定方法见 @references/quality-standard.md。
参考资料
@references/presets.md:18 款滤镜的配方依据、适用场景、可调项与未覆盖范围@references/quality-standard.md:可量化专业标准、验收清单与判定方法@references/setup-guide.md:依赖安装、解释器选择、验证方式与撤销方法