# Photo Distill

> 把一张照片蒸馏成极简 zine 纸质海报——不用图像生成模型，用手写 HTML/CSS/SVG 绘制、 无头 Chrome 导出 PNG。当用户给出一张自己拍的照片、想把它做成极简海报/版画/zine 风格作品时使用。 触发词：照片蒸馏、photo distill、把这张照片做成海报、极简海报、zine 海报、 蒸馏这张照片、做成版画那种、用代码画这张照片、纸质海报、海报系列再做一张。 即使用户只说"把这张照片处理成那个风格"、"这张也来一张"、"帮我把这照片抽象一下"， 只要是把摄影作品变成印刷符号，就应触发。 不用于 AI 生图海报（那用 gc-minimal-zine-poster）、照片修图调色、 或给现成图片套滤镜——本 skill 是从零绘制。

- Skill: `yangcodingmaster/photo-distill` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add yangcodingmaster/photo-distill`
- Raw SKILL.md: https://api.skillmd.com/api/skills/yangcodingmaster/photo-distill/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Design & Media
- Author: yangcodingmaster (https://skillmd.com/u/yangcodingmaster)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/yangcodingmaster/photo-distill

---


# Photo Distill · 照片蒸馏成极简纸质海报

**把照片蒸馏成三四个印刷符号——一块墨、一根线、一点色、一行小字，意思到了就停手。**

产出是一个纯本地 HTML 文件（双击可开）+ 一张 2x PNG。全程零 AI 生图、零照片像素。

---

## 一、核心方法论

### 蒸馏，不是再现

走过的三条弯路，按顺序踩过一遍就懂了：

1. ❌ **拼贴**：从照片裁像素、做旧、贴上纸 → "只是抠图，不美观"
2. ❌ **具象重绘**：画完整场景（夜空背景 + 发光路灯 + 雾树线）→ 违反反向约束，"太具象"
3. ✅ **抽象重绘**：纸上印抽象符号。
   例：路灯照片 = 米白纸 + 一小块竖长靛蓝墨块（夜）+ 一根细黑线穿过（灯柱）
   + 线顶琥珀圆点带套印错位（灯）+ 墨块下方琥珀横条（被照亮的路）。

第三种是基准。海报可以完全零照片像素。

### 再往深一层：反向的语义过程

蒸馏的尽头不是"把物体画简单"，而是**先把照片剥到只剩关系，再只画那个关系**。

正向语义是「看图 → 认出这是花和柏油」；
反向是「抽出关系（一片均质的场 + 三个异常点）→ 只画场与异常」。

> **判据：如果观者还在数花瓣，画的就仍是物体。**

某张走了三版才到位：① 印一块黑、挖出花的形状 → 仍是物体；
② 撤掉沥青质感模拟 → 仍是物体；
③ 柏油与花统一成**同一种符号（点）**，花 = 点阵让开的空处、场的边界自己稀疏掉 → 对了。

**物体可以消失，但事实不能改**——三处空处的坐标仍是从原片扫出来的那三个亮团中心。

---

## 二、工作节奏

### 开工自检：三个问题，实测回答，别"以为"

接到任务先测三件事。测不过就按对应的降级路径走，**不许装**：

1. **能把 HTML 渲成 PNG 吗？** 跑一次渲染命令试试（[pipeline.md](references/pipeline.md) §4
   有 macOS / Linux 两套路径）。不能 → 交 HTML 文件本体，让用户双击打开看
2. **能看见渲染出来的 PNG 吗？** 检验方式很具体：渲染后**用一句话说出画面里的三个东西**。
   说不出来＝你看不见＝立刻进入下面的盲画协议。假装看见比看不见更糟——
   用户拿你说的三个东西一对照就拆穿了
3. **能跑 Python + PIL 吗？** `python3 -c "from PIL import Image"` 试一下。
   不能 → 色值一个都不许编：把 design-system 的默认色板拿给用户选，或请用户直接报颜色

同一会话内已实测过的能力可**复用结果**，不必每张重测；换环境或换会话必须重测。

**盲画协议**（第 2 问答"否"时强制生效）：第一版按采样数据画完**立刻交给用户**，
并明说"我看不到渲染结果，请你看图告诉我哪里不对"；此后每一轮改动**只能由用户反馈驱动**——
一条反馈 → 一处修改 → 立刻再交；**自发迭代次数为零**。你看不见，盲改一版都是浪费。

---

**速度是第一指标：从拿到照片到用户看到第一张图，目标 5 分钟以内。**
一张一张做，不批量。让用户等 15 分钟再看到一张不满意的图，是这个 skill 最大的失败模式。

**解构，只取两三个元素。** 不需要把照片里所有东西都表现出来——
整张只选**两三个元素**，选哪几个**用户说了算**：

- 用户点名了（"突出黄色的光晕和城市的内透"）→ 这就是方案，直接开画
- 没点名 → 只问一句："这张里你最想留下的两三个元素是什么？"
- 用户让你定 → 自己挑两三个，开画前用一两行说清"什么变成什么、色锚是什么"，不等回复

选中元素之外的一切：**不采样、不绘制、不惋惜**。抽象程度由此自然落定——
元素少即抽象；用户想更具象，自然会多点几个元素。

1. **读事实**（半分钟）：比例、EXIF，一条命令出齐
2. **最小采样**（一个脚本，一分钟）：只采选中元素的**关系量**——中心位置、密度、
   一条分界 y、色相/饱和度，顺手算出色锚的目标面积（先算后画，别画完再测着调）。
   **绝不采轮廓**——轮廓是物体的形状，采了就会忍不住去画
   （实测：树梢的 141 个锯齿点，让一道横痕被画成了山）。
   透视三步法（[craft-rules.md](references/craft-rules.md) §A）只在选中元素含地面线/结构时才走
3. **直接带质感画**：从 [assets/template.html](assets/template.html) 起步一步到位。
   不出低保真草图——实测草图阶段会自己长出迭代循环，省不了时间
4. **截一张图 → 看图自检一轮 → 马上交给用户**。首版之后你**有且只有这一轮**自查
   （只修缩略图上看得见的错误）；**未经用户输入连改两版＝违反协议**。
   每次交付必须带三样：图本体、一句话说明这版改了什么、一个明确的问题（"哪里要调？"）。
   用户一句"黑的像山"顶你五轮自查；千分之几亮度差的像素级缺陷不追
5. **用户反馈 → 小改快出**：每轮改动一步到位，30 秒内重新出图，不憋大招
6. **用户说行才定稿**：跑 `scripts/finalize_poster.py`（渲染 2x + 四项实测一条命令，
   FAIL 即删，PASS 才有成品；用户确认过的偏离用 `--waive` 显式记录）→ git commit。
   非本机环境交付时附 6 行运行日志（[run-log-template.md](references/run-log-template.md)）

---

## 三、硬性规则

- **画幅 = 原片比例**（横竖都跟原片）。高度用脚本算，别估
- **不带任何文案短句**。只带真实拍摄参数——这是与文艺海报的分界线
- **每张一个独立的图形语言**，不与此前作品重复。做之前先翻一遍已有的清单
- **进成品的每个数字必须脚本算**（坐标映射、几何端点、透视落点、色锚面积）
- **色锚四条硬指标**：占画布 0.8–2.5%、缩略图下可见、绝不用 pale/muted 削弱、单一主色相
- **不做材质模拟**。沥青碎石、木纹、水波都是再现。唯一允许的质感是标准颗粒层
- **纸读作 70–90%，主体着墨 8–25%**（按实际着墨算，不按外框）
- 定稿前逐条对反向约束清单

### 定稿实测（脚本跑成品 PNG，四个数一次出，别靠眼估）

**唯一真身是 [scripts/finalize_poster.py](scripts/finalize_poster.py)**——渲染 + 实测 +
FAIL 即删（fail-closed），下表只是速览，阈值以脚本为准，别照表另写一份实现。

> **这四个数不是创作目标，是出厂检验。** 用户说行之前，禁止为了凑指标修改画面；
> 创作阶段调色看图、看采样值，不看百分比。把检验标准当目标函数，画面不会变好，
> 只会产出几版"数字不同、肉眼没差别"的空转。

| 指标 | 怎么算 | 目标 |
|---|---|---|
| 色锚面积 | `sat>0.35 且 v>0.12` 的像素占比 | 0.8–2.5% |
| 缩略图可见性 | 缩到 160px 宽后重算上一项 | 仍 ≳0.6% |
| 实际着墨 | 与纸色的 RGB 曼哈顿距离 >46 的像素占比 | 8–25%（纸 70–90%） |
| 色相集中度 | 色锚像素色相的中位数与 5–95 分位 | 分位跨度窄＝单一主色相 |

⚠️ **色相分桶会骗人**：210° 附近的蓝按 30° 分桶会被切成两半，看着像"两个色相"。
用中位数 + 分位，别用直方图桶。

---

## 四、可自定义的六个槽位

**最大的那个自定义维度是"抽象程度"，它不在下表里**——它由用户点名哪两三个元素
直接决定（见 §二）。下面六个是视觉常量，默认值是这套语言的原点，
用户要换时照表办，细节见 [design-system.md §6](references/design-system.md)。

| 槽位 | 默认 | 换的时候守什么 |
|---|---|---|
| 画幅 | 与原片同比例 | 一旦不跟原片，裁切本身就是蒸馏动作，要说明理由 |
| 字体 | Courier / Songti 栈 | **全站一个字体栈、禁用斜体**——这两条不放开 |
| 纸色 | 米白 `#e9e3d5` | 氛围优先进符号，不进纸。整张染成场景色 = 回到再现 |
| 色锚色板 | 按照片轮换 | 四条硬指标一条都不放宽 |
| 档案微字 | 三行真实参数 | 参数必须真实，宁可写 `GPS —` 也不编 |
| 编号系统 | `NO. XXX` + `+` 套准符 | 可换前缀、改位置、或关掉 |

**不可自定义**：噪点层参数、multiply 上纸、不做材质模拟、色锚四条硬指标、反向约束清单。

---

## 五、References

| 文件 | 内容 | 什么时候读 |
|---|---|---|
| [design-system.md](references/design-system.md) | 纸/墨/色锚/档案微字的全部常量与代码、反向约束清单、六个自定义槽位 | 搭骨架时、定稿对表时 |
| [filters.md](references/filters.md) | 可直接复制的 SVG 滤镜库（揉边/颗粒/无边界墨气/渗边/油画棒）、mask 四用法、符号词汇 | 决定"这个符号怎么画"时 |
| [craft-rules.md](references/craft-rules.md) | 透视三步法、材质语法、雾/凹陷/色块/硬边的做法与失败案例、定稿自查清单 | 动手前和定稿前各一次 |
| [pipeline.md](references/pipeline.md) | EXIF 提取、HEIC 方向坑、采样脚本约定、渲染与 2x 导出命令（macOS/Linux 双路径） | 开工第一步、导出时 |
| [examples/structural-index.md](examples/structural-index.md) | 结构类型 → 蒸馏手法的一行式索引（学那步棋，不抄那盘棋） | 给新照片选图形语言时**先读这个** |
| [examples/](examples/) | 八对「原片 → 成品」对照，每对说明蒸馏成了哪几个符号 | 索引命中后想看完整推演时 |
| [scripts/finalize_poster.py](scripts/finalize_poster.py) | fail-closed 定稿：渲染 2x + 四项实测一条命令 | 只在用户说行之后 |

---

## 六、已用过的图形语言（别重复）

海平线+墨影人+吊圆 · 色层析柱 · 测距图 · 透视梯形窗 · 标本盘吊线 · 对角线草坡 ·
体块与缝 · 夜的墨块 · 仰视弧+细索扇+舱串 · 色块场+映射点阵 · 条纹织物+透光 ·
运动痕迹 · 路线牌+挡风玻璃里的落日 · 漂浮窗阵 · 天际线频谱 · 融化的穹顶 · 风景钻芯 ·
能见度阶梯/雾吃掉油墨 · 均质点场+三处异常 · 等高层线场+地形下切+垂直异物 ·
无边界墨气+云隙色斑+唯一硬边 · 密度梯度场/线自己出现 · 雨痕帘+红灯流淌 ·
测距墨点人+油画棒湖水 · 色票柱+三笔人+倒影 · 一方冷+亮从里让开/近糊远清 ·
内透的碎光点场 vs 投射的暖光团/纸兼作夜空与暗地 · 窗格线+一格绿+洒地的光

做新一张时先扫一遍这份清单，确认要用的语言没被用过；
选语言前先查 [examples/structural-index.md](examples/structural-index.md)
认结构类型——学那步棋，不抄那盘棋。用掉一个新语言就往清单里补一条。

