@dsh-gis/dsh-video-studio-skill 0.9.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +424 -0
  2. package/README.md +287 -327
  3. package/THIRD_PARTY_NOTICES.md +1 -1
  4. package/package.json +9 -10
  5. package/schemas/design.schema.json +722 -0
  6. package/schemas/film.schema.json +4 -15
  7. package/schemas/proposal.schema.json +133 -0
  8. package/skills/video-studio/SKILL.md +1316 -1123
  9. package/skills/video-studio/assets/design_fixture.json +141 -0
  10. package/skills/video-studio/assets/film.json +1 -2
  11. package/skills/video-studio/assets/golden_frames.json +11 -8
  12. package/skills/video-studio/assets/scenes.py +67 -22
  13. package/skills/video-studio/assets/voice_loudness.json +16 -0
  14. package/skills/video-studio/references/audio-variation.md +15 -11
  15. package/skills/video-studio/references/audio.md +36 -34
  16. package/skills/video-studio/references/change-impact.md +3 -3
  17. package/skills/video-studio/references/frame-qa.md +2 -2
  18. package/skills/video-studio/references/proposal.md +177 -0
  19. package/skills/video-studio/references/quality-gates.md +1 -1
  20. package/skills/video-studio/references/scene-grammar.md +28 -23
  21. package/skills/video-studio/references/sfx-design.md +1 -1
  22. package/skills/video-studio/references/style-packs.md +95 -257
  23. package/skills/video-studio/references/v08-quality.md +8 -8
  24. package/skills/video-studio/references/web-engine.md +1 -1
  25. package/skills/video-studio/references/word-timeline.md +2 -2
  26. package/skills/video-studio/scripts/designkit.py +2120 -0
  27. package/skills/video-studio/scripts/filmcheck.py +7 -33
  28. package/skills/video-studio/scripts/filmkit.py +153 -78
  29. package/skills/video-studio/scripts/grammar.py +67 -7
  30. package/skills/video-studio/scripts/musicgen.py +2043 -0
  31. package/skills/video-studio/scripts/preflight.py +10 -3
  32. package/skills/video-studio/scripts/previewkit.py +849 -0
  33. package/skills/video-studio/scripts/propose.py +825 -0
  34. package/skills/video-studio/scripts/qa_frames.py +45 -10
  35. package/skills/video-studio/scripts/qavmaf.py +77 -11
  36. package/skills/video-studio/scripts/render.py +472 -154
  37. package/skills/video-studio/scripts/scorekit.py +50 -1748
  38. package/skills/video-studio/scripts/selfcheck.py +658 -131
  39. package/skills/video-studio/scripts/stagewatch.py +21 -5
  40. package/skills/video-studio/scripts/stylekit.py +265 -741
  41. package/skills/video-studio/scripts/voicekit.py +502 -380
  42. package/skills/video-studio/scripts/webengine.py +15 -14
  43. package/tools/audio_similarity.py +57 -30
  44. package/tools/ci_check.py +7 -1
  45. package/tools/estimate.py +14 -8
  46. package/tools/golden_frames.py +56 -11
  47. package/tools/silhouette_qa.py +521 -168
  48. package/docs/demo-pil-sheet.png +0 -0
  49. package/docs/styles/demo-results.json +0 -76
  50. package/docs/styles/sheet-8-styles.png +0 -0
  51. package/docs/styles/sheet-ai.png +0 -0
  52. package/docs/styles/sheet-before-after.png +0 -0
  53. package/docs/styles/sheet-craft.png +0 -0
  54. package/docs/styles/sheet-night.png +0 -0
  55. package/skills/video-studio/assets/score_demo.json +0 -307
@@ -1,283 +1,121 @@
1
- # 风格包(Style Packs):让每部片的画风 / 配乐 / 旁白都不一样
1
+ # 外观定制:设计 DSL 实操手册(v1.0.2)
2
2
 
3
- > v0.5.0 新增。读这一篇之前先读 SKILL.md 的 §5.6。
4
- > 一句话:**一个 style id 同时决定画风、配乐、旁白声音和节奏**,不能三样各自乱配。
3
+ > **这篇不是「8 套风格选一套」。** v1.0.1 起仓库里没有任何内置风格表——
4
+ > 没有 `STYLES` / `THEMES` / `VISUAL_LANGUAGES` / `presets/`,也没有 `style` 字段。
5
+ > 外观由 `work/design.json` 决定,而它是 `propose.py` 现场采样 + 你确认之后冻结的。
6
+ > 这篇讲的是:**想控制画面的哪一块,就改 design.json 的哪个字段,怎么验证它真的生效。**
5
7
 
6
- ## 0. 为什么需要它(v0.4 的病)
8
+ ## 0. 三条正确姿势
7
9
 
8
- v0.4 及以前,所有"身份参数"都是模块级常量:
9
-
10
- | 层 | 被写死的地方 | 结果 |
10
+ | 你想要 | 怎么做 | 不要这样做 |
11
11
  |---|---|---|
12
- | 画风 | assets/scenes.py 的 THEME 字典 + 3 个绘制函数 | 每部片都是深蓝底 + 橙强调 |
13
- | 配乐 | scorekit 的 4 个小调 + 3 套和弦进行 + 固定动机 | 每部片都是 Dm / 120BPM 那一套 |
14
- | 旁白 | render.py 的单个 voice/rate/pitch | 每部片都是同一个声音、同一语速 |
15
- | 流程 | SKILL.md 教的是 "cp 模板 -> 改 lines[].text" | 模型自然只会改文案 |
16
-
17
- 结果就是:**内容换了,气质没换**。用户的原话是"背景音乐风格都差不多、旁白声音都差不多、整体画风也差不多"。
18
-
19
- ## 1. 三层结构
20
-
21
- film.json style: "auto" | "tech-deep" | ... | "legacy"
22
- |
23
- v
24
- stylekit.resolve_script_style(script) <- 纯函数,确定性
25
- | +- 关键词打分(title/chapter/text/items)
26
- | +- 三级内容指纹(full / tempo / style)
27
- | +- 显式覆盖(visual.theme / music.style / voice.map / pacing)
28
- v
29
- work/style.json <- 审计产物:能看到"为什么是这套风格"
30
- | +- style / theme_id / chapter_layout
31
- | +- music{bpm,key,mood,seed_effective}
32
- | +- voice{map,policy}
33
- | +- pacing{lead,tail,min_dur,item_stagger}
34
- v
35
- 画面(scenes.py 读主题) 配乐(scorekit 读风格) 旁白(voicekit 读 persona/tone)
36
-
37
- ## 2. 8 套内置风格
38
-
39
- | id | 中文 | 画风 | 配乐 | 旁白 | 适合内容 |
40
- |---|---|---|---|---|---|
41
- | tech-deep | 科技深蓝 | 深蓝渐变 + 橙 | 小调 100–124 / 琶音 + 铃声 | 男声叙述 + 青年开场 | AI、工程、数据、科普 |
42
- | magazine | 杂志暖白 | 米白 + 朱红,衬线体 | 大调 88–100 / 电钢 | 女声叙述 + 权威男声引用 | 报告、复盘、研究 |
43
- | paper | 手绘纸感 | 牛皮纸 + 靛青,楷体 | 大调 84–92 / 马林巴 | 软糯女声 + 男声引用 | 手工、自然、历史 |
44
- | vivid | 活力撞色 | 深紫 + 柠檬黄,大字 | 大调 120–132 / 短促和弦 | 活泼女声开场 | 活动、营销、上新 |
45
- | mono | 极简黑白 | 纯白 + 黑,超宽字距 | 极简 60–68 / 钢片琴 | 沉稳男声 + 女声引用 | 观点、演讲、思辨 |
46
- | cinema | 电影青橙 | 近黑 + 青橙,扫描线 | 小调 76–84 / 弦乐铺底 | 纪录片男声 + 女声引用 | 纪录、叙事、城市 |
47
- | lo-fi | 暖灰薄荷 | 暖灰 + 薄荷绿 | 大调 72–84 / 电钢 + 轻鼓 | 温柔女声 | 日常、学习、专注 |
48
- | epic | 深黑鎏金 | 近黑 + 金,衬线体 | 小调 88–96 / 铜管 + 人声铺 | 有力男声 + 男声开场 | 史诗、宣传、高潮 |
49
-
50
- 外加 legacy:不写 style 字段时的兼容档,画风/配乐/旁白全部等于 v0.4(配乐逐样本一致)。
51
-
52
- 查表:
53
-
54
- python stylekit.py --list # 8 套风格 + 主题 + 对比度
55
- python stylekit.py --show cinema # 单套风格的全部字段
56
- python stylekit.py --lint # 主题对比度门禁(CI 会跑)
57
-
58
- ## 3. film.json 怎么配
59
-
60
- 最小配置(推荐,几乎不用写):
12
+ | 换一种气质 | `propose.py --revise "更冷一点,少装饰,换女声"` 重出方案 | 去找"第 9 套风格"——没有这种目录 |
13
+ | 微调某个细节 | 手改 `work/design.json` 的字段 → `designkit.py --lint` 校验 → `render.py --stage stills` 看 | 直接改 `filmkit.py` 里的常量 |
14
+ | 换整个画面语言 | 写自己的 `scenes.py`(放剧本同目录),实现 `draw_frame(scene, t_local, dur, t_global, script, frame)`(完整签名见 SKILL.md §1) | 期望 `--engine web` 跟随 design(它不会) |
61
15
 
62
- {"style": "auto"}
16
+ ## 1. design.json 的六块
63
17
 
64
- 自动模式:命中题材关键词就锁定对应风格;零命中时按内容指纹稳定落桶
65
- (同一份稿子永远同一个风格,不同稿子大概率不同)。
66
-
67
- 想锁定:
68
-
69
- {"style": "cinema"}
70
-
71
- 想只改某一块(其余跟着风格走):
72
-
73
- {
74
- "style": "magazine",
75
- "visual": {
76
- "theme": "paper",
77
- "layout": "split-right",
78
- "theme_overrides": {"vignette": 0.18, "grain": 8.0}
79
- },
80
- "music": {
81
- "style": "lo-fi",
82
- "bpm": "auto", "key": "auto", "mood": "calm",
83
- "seed": 7, "seed_from_content": true
84
- },
85
- "voice": {
86
- "map": {"hook": "bright_female", "body": "narrator_female"},
87
- "jitter": 0.35,
88
- "max_distinct": 3
89
- },
90
- "pacing": {"lead": 0.6, "tail": 0.9, "item_stagger": 0.3}
91
- }
92
-
93
- 逐句覆盖(最高优先级):
18
+ | 块 | 决定什么 | 字段 |
19
+ |---|---|---|
20
+ | `structure` | 几段、目标时长 | `segments` / `beats` / `hold_policy` / `target_sec` |
21
+ | `visual` | 画面全部外观 | 见 §2 |
22
+ | `audio` | 配乐 / 声线 / 音效 | `music`(scale/bpm/key/mood/seed)/ `voice`(id/map/jitter/max_distinct)/ `sfx`(enabled/density/gain_db) |
23
+ | `pacing` | 节奏 | `lead` / `tail` / `min_dur` / `hold` / `item_stagger` |
24
+ | `id` | 指纹 | `design:<hash>`,由 `designkit.design_fingerprint()` 算出 |
25
+ | `label` / `pitch` | 中文标签 / 立意 | 提案卡上显示的那一句话 |
94
26
 
95
- {"id": "l03", "kind": "body", "text": "他说:「这不可能」",
96
- "voice": "authority_male", "tone": "quote", "lead": 0.8}
27
+ 校验:`python <SKILL_DIR>/scripts/designkit.py --lint work/design.json`;
28
+ 契约:`<REPO>/schemas/design.schema.json`。**fail-closed**:缺字段/越界/对比度不达标一律报错,
29
+ 不会静默回落(这是对 v0.5–v0.9「改了不生效」的直接反思)。
97
30
 
98
- ### 字段速查
31
+ ## 2. visual 字段表(能控制什么)
99
32
 
100
- | 字段 | 默认 | 说明 |
33
+ | 字段 | 取值 | 改它会发生什么 |
101
34
  |---|---|---|
102
- | style | 无(= legacy) | 风格 id / "auto" / "legacy" |
103
- | visual.theme | — | 等价于锁 style;与 style 1:1 |
104
- | visual.layout | 风格默认 | left-stack / center-stack / split-right |
105
- | visual.theme_overrides | — | 深合并覆盖主题单字段 |
106
- | music.style | 跟随 style | 只换配乐风格 |
107
- | music.bpm | "auto" | 数字或 "auto"(按风格梯度 + 指纹) |
108
- | music.key | "auto" | 调名或 "auto"(按风格调池 + 指纹) |
109
- | music.mood | 风格默认 | calm/reflective/hopeful/steady/driving/tense/uplifting |
110
- | music.seed_from_content | 非 legacy 时 true | 音色细节由内容指纹派生 |
111
- | voice.map | 风格默认 | kind -> persona 名 |
112
- | voice.jitter | 0.35 | 语速/音高微扰,0 = 关 |
113
- | voice.max_distinct | 3 | 全片最多几把声音 |
114
- | pacing.* | 风格默认 | 语前留白/语后留白/最短段长/条目落位步长 |
115
- | lines[].persona / lines[].voice / lines[].tone | — | 逐句覆盖(最高优先级,voicekit 真的读这三个) |
116
- | lines[].lead | — | 逐句语前留白(filmkit.build_timeline 读) |
117
- | lines[].rate / pitch / volume / tail | — | **当前不生效**(保留字段):voicekit 只认 persona/voice/tone,filmcheck 会对这三个报"未知键";要改语速/音高请换 persona 或 tone,或在顶层写 rate/pitch/volume |
118
-
119
- ## 4. 三级指纹:改错别字不会换整首曲子
35
+ | `palette` | 11 个颜色角色(bg/bg2/surface/ink/muted/dim/accent/accent2/good/bad/rule),每个 `[r,g,b]` | 整片色调。它不是"从 8 套配色里挑",而是**连续色相空间**里的一点:改 `bg` 的色相,其余角色会按对比度网格重新求解 |
36
+ | `family` | `sans` / `serif` / `kai` / `round` | 全片字族(标题优先,字幕默认跟随) |
37
+ | `typography` | `tracking`(字距)/ `line_gap`(行距)/ `number`(编号写法,8 种)/ `ratio`(字号比例)/ `align` | 文字气质;`number` 决定"01 / 壹 / I / 1.0"这类编号外观 |
38
+ | `motif` | `kind`(8 种:brackets / hairlines / handdrawn / slabs / minimal / letterbox / signal / rays)× `density`(0.55–1.00) | 装饰语言:四角括线 / 细线页眉 / 手绘框 / 色块 / 极简 / 遮幅 / 信号条 / 放射线 |
39
+ | `transition` | `kind`(dip / wash / paper / wipe / cut / iris)+ `dur`(秒) | 段与段之间的转场 |
40
+ | `motion` | `ease`(out3/out5/inOut3/outBack)/ `enter`(dx,dy) / `push`(镜头缓推)/ `breathe_hz` | 入场与镜头运动 |
41
+ | `bias` | `x` / `y` / `inset` | 整屏重心偏移与安全区收缩 |
42
+ | `layout` | `left-stack` / `center-stack` / `split-right` | 默认版式 |
43
+ | `layout_cycle` | 版式数组(≥2 种不同) | **v1.0.2**:按章节轮换版式;不写则自动派生 2–3 种 |
44
+ | `hud` | `chip-left` / `corner-line` / `none` | 页眉/角落装饰 |
45
+ | `size` / `fps` | `1920x1080` / `30` | 只支持 16:9 横屏(编码时可下采样到 720p) |
46
+ | `subtitle` | 见 §3 | **v1.0.2**:字幕字号/位置/对齐/背板/圆角/字族/颜色/行数 |
47
+
48
+ > **能组合出多少种画面?** 离散维度是 8 母题 × 6 转场 × 4 缓动 × 8 编号 × 3 版式 × 3 HUD × 4 字族
49
+ > × 7 情绪 × 2 调式(= 774,144 种),再乘配色这个连续量(色相 0–360° 任意取值)。
50
+ > 这个数字只说明"字段可组合空间足够大",**不是推荐你从中挑**——好看与否仍由对比度体检、
51
+ > 分镜语法和你的目检决定。
52
+
53
+ ## 3. 字幕样式(visual.subtitle,v1.0.2)
54
+
55
+ 以前字幕的字号 / 位置 / 背板 / 圆角是 `filmkit` 的写死常量,所以**任何两条片子**的字幕带一模一样。
56
+ 现在进设计 DSL;**不写 = 默认值,与过去逐像素一致**(老 `design.json` 不受影响)。
57
+
58
+ ```json
59
+ "subtitle": {"size": 46, "y": 560, "align": "left", "plate_alpha": 170,
60
+ "radius": 10, "family": "sans", "color": [245, 245, 245], "max_lines": 2}
61
+ ```
62
+
63
+ | 字段 | 默认 | 取值 | 备注 |
64
+ |---|---|---|---|
65
+ | `size` | 46 | 20–96 | 采样器默认跑 40–56 |
66
+ | `y` | null | 0–1030 | null = 贴底安全区;采样器 1/3 概率抬高到 560–880 |
67
+ | `align` | center | center / left | |
68
+ | `plate_alpha` | 170 | 0–255 | 采样器默认 96–194 |
69
+ | `radius` | 10 | 0–48 | 采样器默认 2–20 |
70
+ | `family` | null | sans / serif / kai / round | null = 跟随大标题字族 |
71
+ | `color` | null | [r,g,b] 或 #rrggbb | null = 默认墨色 |
72
+ | `max_lines` | 2 | 1–3 | 决定了折行上限 |
120
73
 
121
- | 指纹 | 由什么算 | 决定 |
122
- |---|---|---|
123
- | full | 标题 + 全部台词 | 音色细节(seed 派生) |
124
- | tempo | 标题 + kind 序列 + 体量桶 | BPM 与调性 |
125
- | style | 标题(配合关键词评分破平) | 风格 id |
74
+ 验证:`render.py --stage style` 会打印一行「字幕: ... 对齐=... 背板α=... 圆角=... 最多N行」,
75
+ 改 design 后重跑这一行就能看重算结果,不用等渲完。
126
76
 
127
- 所以:改一个错别字 -> full 变(音色纹理微变),tempo/style 不变(还是同一首曲子、同一个调);
128
- 换一部片 -> 三个都变。`music.seed_from_content: false` 只锁住 **seed 派生**(音色细节);
129
- `full` 指纹仍会传给 scorekit 参与结构变奏(和弦替换/动机偏移/鼓型 ghost,见
130
- [audio-variation.md](audio-variation.md) §3.3)。要把声音完全锁回 v0.4 行为,只能不写 `style`(legacy)。
77
+ ## 4. 版式轮换(visual.layout_cycle,v1.0.2)
131
78
 
132
- ## 5. 片内变化:整体是一套,但每段有呼吸
79
+ ```json
80
+ "layout_cycle": ["split-right", "left-stack", "center-stack"]
81
+ ```
133
82
 
134
- **锁三件事**(全片一致,否则会"花"):
83
+ - 元素取 `left-stack` / `center-stack` / `split-right`;**至少 2 种不同版式**,只写一种会被校验拒绝。
84
+ - 由 `grammar.plan_scenes` 按**章节**轮换:同一章内固定,换章才换(观众视角一章是一段整体)。
85
+ - 不写时 `design_to_plan` 按设计指纹自动派生 2–3 种,**默认就不是全片一张版式**。
86
+ - 审计:`--stage layout` 打印每段构图;`work/storyboard.json` 每段都有 `layout` 字段。
135
87
 
136
- 1. 明暗族(全深色 or 全浅色)
137
- 2. accent 色相
138
- 3. 字体族与字号阶梯
88
+ ## 5. 配色怎么改才不出事
139
89
 
140
- **其余可以按章节变**:
90
+ 配色不是"挑一个好看的颜色",而是 11 个角色要同时满足对比度:
91
+ `theme_lint` 的门槛是 ink≥7 / muted≥4.5 / dim≥3 / accent≥4.5 / accent2≥4.5(good/bad≥3),
92
+ 另外要求 bg 与 bg2 有可感知的亮度差(防纯色底)。
141
93
 
142
- - 版式骨架轮换(同一 chapter 内不变):left-stack / center-stack / split-right
143
- - 背景明度微移、纹理强度、accent 出现的面积
144
- - 配乐:逐小节能量弧线、配器进出(1 小节渐入渐出)、主奏轮换、动机变形
145
- - 旁白:hook/body/quote/outro 的语气档(语速、音高、停顿)
94
+ 推荐两条路:
146
95
 
147
- **禁止**:同一部片里跨明暗族换主题(深蓝段直接切暖白段)。实测这会精确触发
148
- qa_frames 的单帧闪烁门禁(阈值 6.0 灰度,整屏换底可达 100+ 级),而且观众看着像"换了一部片"。
149
- 真需要强对比,把它做成两个片子,或登记成带 visual_at 的视觉事件。
96
+ 1. **让采样器算**(默认):采样器在 (hue, s, v) 网格里找满足全部门槛的点;
97
+ 实测纯随机取色通过率 35%,网格求解 100%。
98
+ 2. **手改色相**:只改 `palette.bg` 的色相/饱和度,然后跑 `designkit.py --lint`;
99
+ 过不了就调一档明度——报错会直接点名哪个角色不达标。
150
100
 
151
- ## 6. 门禁:主题必须过对比度体检
101
+ > 片内**不要跨明暗族**:同一部片锁一个明暗族,段界跨族会整屏换底、触发闪烁门禁,观众也看着像换片。
102
+ > 这条在采样器里是硬约束,手改 design 时请自己守住。
152
103
 
153
- stylekit.theme_lint(theme) 在 --stage style 就拦,不用等渲完 300 帧:
104
+ ## 6. 片内不重复:三层一起用
154
105
 
155
- | 角色 | 对背景的最低对比度 | 理由 |
106
+ | 层 | 机制 | 防的是什么 |
156
107
  |---|---|---|
157
- | ink(正文/标题) | 7.0 | WCAG AAA 正文线 |
158
- | muted(副标) | 4.5 | WCAG AA |
159
- | dim(HUD 小字) | 3.0 | 装饰级 |
160
- | accent / accent2 | 4.5 | 强调色常当文字用 |
161
- | good / bad | 3.0(对卡片底) | 只承载图形,靠位置+颜色双编码 |
162
- | bg 与 bg2 亮度差 | >= 0.002 | 防纯色底 |
163
-
164
- 加第 9 套风格时:先写 _theme(...),再跑 python stylekit.py --lint,过了再进 STYLES。
165
-
166
- ## 7. 加一套自己的风格(5 步)
108
+ | 段内 | `lines[].type`(10 类构图,grammar 自动选) | 每段都长一个样 |
109
+ | 章内 | `layout_cycle`(v1.0.2,按章节轮换) | 全片一张版式 |
110
+ | 片间 | 采样 nonce + 候选 ≥2 维 + 底色色相分叉 + **片间去重台账** | 不同片撞衫 |
167
111
 
168
- 1. stylekit.THEMES["my-style"] = _theme(...)(配色 + 材质 + 排版 + 版式 + 动态 + HUD)
169
- 2. stylekit.STYLES["my-style"] = {...}(theme / layouts / music{bpm_ladder,keys,mood} / voice / pacing)
170
- 3. STYLE_IDS 元组末尾追加(只追加不改顺序,否则指纹映射会漂)
171
- 4. python stylekit.py --lint 全绿;配乐侧在 scorekit.MUSIC_STYLES 里加同名条目
172
- 5. 用 3 段样片跑 --stage stills 亲眼看(铁律 6:能出图 != 没问题)
173
-
174
- ## 8. 常见误用
112
+ ## 7. 常见误用
175
113
 
176
114
  | 现象 | 原因 | 修法 |
177
115
  |---|---|---|
178
- | 画面换了、配乐还是老的 | 只改了 visual.theme 没改 music.style | 直接改 style,或两个都写 |
179
- | 同一部片前后像两部片 | 段界跨了明暗族 | 用同族主题;或拆成两部 |
180
- | 改台词后配乐变了 | 改了标题或 kind 结构 -> tempo 指纹变(BPM/调性跟着换) | 写死 music.key / music.bpm(`seed_from_content:false` 只管音色 seed,压不住 BPM/调性变化) |
181
- | 旁白听感像广播剧 | max_distinct 太大 / 相邻句频繁换人 | 降到 2,靠 tone 而不是换人做变化 |
182
- | 字幕在浅色主题上看不清 | 用了 v0.4 的黑底白字 | 传 theme= 给 draw_subtitle(render.py 已自动传) |
183
-
184
- ## 9. 视觉语言包(v0.9):结构签名,不只是配色
185
-
186
- ### 9.1 为什么还要这一层
187
-
188
- v0.5 的 `STYLES` / `THEMES` 管的是**皮肤**:配色、字体族、纹理、颗粒、字距、缓动、HUD、
189
- 3 种版式几何。8 套风格共用同一批绘制器与同一套装饰,于是"换风格"在画面上只剩换色——
190
- 灰度量出来的构图骨架(前景占比 / 重心 / 边缘占用 / 左右对称)几乎不动。
191
-
192
- `stylekit.VISUAL_LANGUAGES` 补的是**结构签名**:同一段内容在两套风格下,剪影必须能区分。
193
- 它是纯数据(不 import PIL / filmkit,否则与 `scenes.py` 成环),与 `STYLES` 同层,
194
- `--stage style` 一起体检。
195
-
196
- ### 9.2 五组签名字段
197
-
198
- | 组 | 字段 | 取值 / 例子 | 谁读它 |
199
- |---|---|---|---|
200
- | `motion` | `ease` | `out3` / `out5` / `inOut3` / `outBack` | `scenes.py`(所有绘制器的入场缓动) |
201
- | | `breathe_hz` | 0.28–1.10(Hz) | `scenes.py`(强调线/下划线/光点呼吸频率) |
202
- | | `enter` | `(起, 止)` 秒,如 `(0.70, 1.20)` | **已声明、visual_lint 会查、绘制器暂未读** |
203
- | | `push` | 0–0.16(背景缓推幅度) | **已声明、visual_lint 会查、绘制器暂未读**(主题自带的 `push` 仍生效) |
204
- | `motif` | `kind` | `brackets` / `hairlines` / `handdrawn` / `slabs` / `minimal` / `letterbox` / `signal` / `rays` | `scenes._motif_layer` 烘焙母题、`scenes._plate` 合成进背景底版;v0.9.1 起逐帧只 copy/EXTENT(约 1.3–2.0 ms),不再逐帧 paste(实测 5.6 ms) |
205
- | | `density` | 0–1(母题浓度;0 只允许 `minimal`) | `scenes._decor_scale`(再乘片内密度档 0.55/0.75/1.0) |
206
- | | `detail` | 母题专属参数(`corner`/`rule_step`/`wobble`/`top`/`bars`/`rays` …) | 同上 |
207
- | `transition` | `kind` | `dip` / `wash` / `paper` / `wipe` / `cut` / `iris` | `scenes._apply_veil`(段首 ≤0.45 s 内生效) |
208
- | | `dur` | 0–0.60 秒 | 同上;超过 0.60 s 会糊掉 3–6 s 节奏下限 |
209
- | `typography` | `tracking` | 0–8 px(字距;与主题自带字距相加) | `scenes._tracking` |
210
- | | `line_gap` | 1.15–1.55(行高倍数) | `scenes._gap` |
211
- | | `number` | `dim` / `serif` / `cn` / `block` / `roman` / `timecode` / `dot` / `gilt` | `scenes._number_text`(章节编号、列表序号、流程节点号) |
212
- | | `align` / `ratio` | `left`/`center`/`right`;display:body 字号比 1.0–3.6 | **已声明、visual_lint 会查、绘制器暂未读** |
213
- | `bias` | `x` / `y` / `inset` | px,如 `{"x": -150, "y": 80, "inset": 40}` | `scenes._bias`(整体重心偏移;`inset` 只作用于左对齐起点) |
214
-
215
- 两条设计约束(改的时候别破):
216
-
217
- 1. **每条签名都要"能被剪影看出差别"**:只改颜色/字重/描边的项不算签名。
218
- 2. **受单帧预算约束**:装饰必须在烘焙阶段合成进底版(`_plate`),逐帧只剩 copy/EXTENT;
219
- 禁每帧全屏 GaussianBlur(实测 30–60 ms)、全屏双线性 resize(29 ms)与全屏 RGBA paste
220
- (实测 5.6 ms——0.9.0 就是栽在这,0.9.1 才改掉),预算 25 ms。
221
-
222
- ### 9.3 8 套风格的签名摘要
223
-
224
- | 风格 | motion(ease / 呼吸) | motif(母题 / 密度) | transition | typography(字距 / 行高 / 编号) | bias(x,y,inset) |
225
- |---|---|---|---|---|---|
226
- | tech-deep | out3 / 0.85 | brackets / 1.00(四角 L 括角 + 刻度尺 + 扫描线) | dip 0.28 s | 0 / 1.30 / dim | (0, 0, 0) |
227
- | magazine | out5 / 0.50 | hairlines / 0.85(双横线页眉 + 栏间线 + 页码位) | wash 0.35 s | 2 / 1.34 / serif | (+60, −20, 0) |
228
- | paper | inOut3 / 0.42 | handdrawn / 1.00(手绘内框 + 胶带 + 印章) | paper 0.35 s | 1 / 1.36 / cn(一、二、三) | (−150, +80, 40) |
229
- | vivid | outBack / 1.10 | slabs / 1.00(粗色块 + 斜切 + 贴纸) | wipe 0.20 s | 1 / 1.26 / block | (0, +40, 0) |
230
- | mono | inOut3 / 0.35 | minimal / 0.55(极细线 + 直角框) | cut 0.00 s | 6 / 1.40 / roman | (0, 0, 0) |
231
- | cinema | inOut3 / 0.30 | letterbox / 1.00(上下遮幅 + 光斑) | dip 0.45 s | 3 / 1.38 / timecode | (0, +30, 0) |
232
- | lo-fi | out3 / 0.60 | signal / 0.90(信号条 + 时间戳) | wash 0.30 s | 1 / 1.36 / dot | (+60, −70, 0) |
233
- | epic | out5 / 0.28 | rays / 1.00(放射射线 + 金线框) | iris 0.40 s | 4 / 1.32 / gilt | (0, −30, 0) |
234
- | legacy | out3 / 0.85 | brackets / 1.00(detail 为空) | cut 0.00 s | 0 / 1.30 / dim | 不参与(走老绘制器) |
235
-
236
- `layout_cycle` 字段也写在每套里(版式轮换顺序),但**当前没有消费者**——
237
- 按章节轮换版式的逻辑在 `STYLES[style]["layouts"]` 那一层(见 §5 与 SKILL §5.5)。
238
-
239
- ### 9.4 `visual_lint` 查什么
240
-
241
- 在 `--stage style` 就拦(`render.py` 解析风格时调用,坏配置直接 `SystemExit`),
242
- 不用等渲到第 300 帧才发现"每段转场糊 1 秒":
243
-
244
- | 检查 | 线 |
245
- |---|---|
246
- | `motif.kind` | 必须在 `VISUAL_MOTIFS` 的 8 个里 |
247
- | `transition.kind` | 必须在 `VISUAL_TRANSITIONS` 的 6 个里 |
248
- | `transition.dur` | 0–0.60 s |
249
- | `typography.number` | 必须在 `NUMBER_STYLES` 的 8 个里 |
250
- | `typography.ratio` | 1.0–3.6 |
251
- | `typography.tracking` | 0–8 px |
252
- | `typography.line_gap` | 1.15–1.55 |
253
- | `motif.density` | 0–1;为 0 只允许 `minimal` |
254
- | `motion.enter` | `(起, 止)` 且 0 < 起 ≤ 止 ≤ 1.6 s |
255
- | `motion.push` | 0–0.16(超过会把内容推出安全区) |
256
-
257
- `selfcheck` 第 53 条另查两件 `visual_lint` 查不到的:8 套签名齐全(motion / typography 都有),
258
- 且**母题 / 转场 / 重心三元组不重复**(全同 = 这套语言只是复制粘贴)。
259
-
260
- 剪影是否真的可区分,由 `tools/silhouette_qa.py`(门禁 G13)量:同内容同段落在两套风格下
261
- 各渲 3 帧 → 灰度 → 480×270 → 四角亮度中位数三值化 → 比前景占比 F / 质心 (Cx,Cy) /
262
- 四边覆盖率 B / 左右对称度 S;五项里任一项达标即 PASS(ΔF≥6pp、|ΔCx|≥8pp、|ΔCy|≥8pp、
263
- ΔB≥14pp、ΔS≥0.20),同一指标必须 3 帧全达标。当前实测 **28 对全过(同明暗族 12/12)**;
264
- 改造前基线同族只过 8/12。
265
-
266
- ### 9.5 加第 9 套风格时,视觉语言要补什么
267
-
268
- 主题与风格包的 5 步见 §7;v0.9 起还要多做四步,**否则新风格在剪影上仍然只是换色**:
269
-
270
- 1. **`stylekit.VISUAL_LANGUAGES["my-style"]` 写全五组**:motion(ease / enter / push /
271
- breathe_hz)、motif(kind + density + detail)、transition(kind + dur)、
272
- typography(tracking / line_gap / align / number / ratio)、bias(x / y / inset)。
273
- 照抄一套现有签名再改配色 = 白做,第 53 条会报"母题/转场/重心完全相同"。
274
- 2. **母题不要临时发明新名字**:`motif.kind` 必须是 `VISUAL_MOTIFS` 里的 8 个之一。
275
- 确实需要新图形语言时,先在 `scenes._motif_layer` 里实现一个分支、加进 `VISUAL_MOTIFS`,
276
- 再在风格里引用——顺序反了会 `visual_lint` 直接拦。
277
- 3. **跑 `python stylekit.py --lint`**(主题对比度)与**单独跑 `visual_lint`**
278
- (写个三行的 `.py` 调 `stylekit.visual_lint("my-style")`,或直接 `--stage style`)。
279
- 4. **跑剪影门禁**:`python tools/silhouette_qa.py --styles my-style,tech-deep,...`,
280
- 确认新风格与其余 8 套两两可区分;再跑 `selfcheck`(第 53 条会要求三元组不重复)。
281
- 5. **看片**:`--stage stills` 渲 3 段亲眼确认母题/转场/字距不是"看着像另一套"——
282
- 数字过了不等于观感对了(铁律 6)。
283
-
116
+ | 改了 `design.json` 画面没变 | 没重跑阶段 / 没重签核 | `render.py --stage explain` 看哪些阶段过时 |
117
+ | `--stage style` 报「找不到设计」 | 工程里没有 `work/design.json` | 先 `propose.py --brief ... --script film.json --work work` 再 `--confirm` |
118
+ | 手改 design 后 `--confirm` 报校验失败 | 字段越界/对比度不达标 | 按报错逐条修;或干脆 `--revise` 重出 |
119
+ | 想"用某套预设当起点" | v1.0.1 已删除全部预设 | 没有这条路:现场采样 + `--revise` 表达要求,要复现用 `--seed` |
120
+ | `render.py --preset x` 报错 | `--preset` 已删除 | 用 `--brief` 或直接写 design.json |
121
+ | `stylekit.py --list` 只有一段说明、exit 2 | 内置风格表已删除(预期行为) | 看 `designkit.py --list-palettes` 的配色采样示例(那不是可选清单) |
@@ -80,7 +80,7 @@ python preflight.py --script film.json --work work --engine pil --json
80
80
 
81
81
  ### 2.3 阈值 92 / 96 是怎么来的(**只有一份权威定义**)
82
82
 
83
- 判据来自 qavmaf 作者对**同一部模板片**(参考帧含字幕与片头片尾淡入淡出)的三组实测:
83
+ 判据来自 qavmaf 作者对**同一部示例片**(参考帧含字幕与片头片尾淡入淡出)的三组实测:
84
84
  crf17 → **97.24**(pass)、crf30 → **95.74**(pass + warning)、二次编码 + 缩放坏片 → **29.45**(fail)。
85
85
 
86
86
  **阈值只有一份**:权威定义是 `qavmaf.py` 的 `VMAF_PASS=92.0` / `VMAF_WARN=96.0`
@@ -124,7 +124,7 @@ qavmaf 会在报告里打 warning 提醒这一点,但**不因此改判**——
124
124
 
125
125
  | 项 | 实测 |
126
126
  |---|---|
127
- | 29.011 s 模板片全片(870 帧,1080p 交付) | **14.2 s**(同机负载高时同一份代码测到过 28.8 s,见下"成本口径") |
127
+ | 29.011 s 示例片全片(870 帧,1080p 交付) | **14.2 s**(同机负载高时同一份代码测到过 28.8 s,见下"成本口径") |
128
128
  | 720p@24 交付(696 帧) | **7.2 s**(帧数少 + libvmaf 比较分辨率低) |
129
129
  | 分项吞吐 | 参考帧渲染受 ffmpeg 消费速度限制;libvmaf threads=min(8, CPU) 时约 124–160 帧/秒(threads=0 只有约 20 帧/秒) |
130
130
  | 抽样模式 | `--vmaf sample`(每 5 帧取 1)实测 8.0 s 跑完 184 帧,VMAF 与全片差 0.34 分 |
@@ -136,7 +136,7 @@ qavmaf 会在报告里打 warning 提醒这一点,但**不因此改判**——
136
136
  所以这里只写"这一跑"的数,不写承诺。纯 libvmaf 比较(参考帧已就绪)约 1.2× 实时——
137
137
  **别把"纯比较的 1.2×"当成端到端成本**。
138
138
 
139
- **v0.8 之后 qa 比渲染还贵**:29 s 模板片 `--stage all --cost` 实测总 **47.3 s**,
139
+ **v0.8 之后 qa 比渲染还贵**:29 s 示例片 `--stage all --cost` 实测总 **47.3 s**,
140
140
  其中 `frames` 12.5 s、`qa` 23.9 s(VMAF 14.2 + ASR 8.7 + 其余);v0.7 的全链约 28 s。
141
141
  决策影响:**批量出片的瓶颈从渲染转到质检**——想压回渲染量级就 `--vmaf off` 或 `--vmaf sample`。
142
142
 
@@ -188,13 +188,13 @@ v0.7 及以前的质检只看"容器 / 响度 / 帧 / 音画同步",都建立
188
188
 
189
189
  ### 3.4 为什么默认线是 0.85 而不是 0.95
190
190
 
191
- 实测(29 s 模板片,含 "DeepSeek 4.1 Flash / DeepSeek Harness" 这类中英混排专名):
191
+ 实测(29 s 示例片,含 "DeepSeek 4.1 Flash / DeepSeek Harness" 这类中英混排专名):
192
192
 
193
193
  | 场景 | 召回 |
194
194
  |---|---|
195
- | 干净模板片 1080p(4 段全对上、缺句 0) | **0.803** |
196
- | 干净模板片 720p@24(同剧本) | **0.853** |
197
- | 干净模板片(另一轮) | **0.811 – 0.828** |
195
+ | 干净示例片 1080p(4 段全对上、缺句 0) | **0.803** |
196
+ | 干净示例片 720p@24(同剧本) | **0.853** |
197
+ | 干净示例片(另一轮) | **0.811 – 0.828** |
198
198
  | 丢句反证:把 l03 从音轨剪掉、timeline 仍写 3 句 | **0.677**(`missing_lines=["l03"]`、gate=fail、exit 1) |
199
199
 
200
200
  0.803–0.853 不是流水线坏了,是 ASR 对中英混排专名的正常误差——实测把 "DeepSeek 4.1 Flash"
@@ -203,7 +203,7 @@ v0.7 及以前的质检只看"容器 / 响度 / 帧 / 音画同步",都建立
203
203
 
204
204
  - **< 0.72 才说明"整句丢了 / 音轨跟剧本不是一套"**——这是真正要阻断的;
205
205
  - 0.72–0.85 给 warning:仍然出片,但提示可能是缓存串了;
206
- - **≥ 0.95 会把干净的模板片判成 fail**,所以不能拿短测试片的 0.985(19.87 s 纯中文素材,
206
+ - **≥ 0.95 会把干净的示例片判成 fail**,所以不能拿短测试片的 0.985(19.87 s 纯中文素材,
207
207
  没有专名)当默认线。
208
208
 
209
209
  ### 3.5 实测耗时
@@ -164,7 +164,7 @@ PNG 截图比 JPEG q94 慢但无压缩伪影,本模块按契约固定 PNG。
164
164
 
165
165
  ## 10. 和别的模块怎么接
166
166
 
167
- - **配乐 / 音效**:webengine.py cues 输出 work/cues.json(§4.2),scorekit/sfxkit 按 t 对齐;
167
+ - **配乐 / 音效**:webengine.py cues 输出 work/cues.json(§4.2),musicgen / sfxkit 按 t 对齐;
168
168
  页面 cue 的 type 用 sfxkit 配方名(ui_blip / whoosh / impact / data_chirp ...)。
169
169
  - **音画同步质检**:页面 S.event 登记的视觉事件进 work/cues.json 的 events,qa_frames.sync_check 用。
170
170
  - **合成成片**:webengine 只出无声画面;配音/配乐/混音由 filmkit 音频总线和 render.py 的 mux 阶段负责。
@@ -97,13 +97,13 @@ G10 会因此记 `skipped` 或 `fail`(看是全部为空还是个别句为空
97
97
  ### 4.1 词间有 0.1–0.3 s 的空隙
98
98
 
99
99
  edge-tts 的 WordBoundary 只覆盖"发音区间",相邻词之间的停顿**不在任何事件里**。
100
- 所以 `covered = Σ dur` 永远小于句长。模板片实测 4 句 64 词,覆盖率约 0.7 量级。
100
+ 所以 `covered = Σ dur` 永远小于句长。示例片实测 4 句 64 词,覆盖率约 0.7 量级。
101
101
  **判据不能写 `covered == dur`**——G10 的覆盖率下限是 **45%**(低于它才是"明显少了")。
102
102
 
103
103
  ### 4.2 句尾静音不在事件里(约 0.5 s)
104
104
 
105
105
  排轴用的是**整句实测时长**,不是词时长求和。G10 只检查"末词终点离句尾不超过 **0.40 s**"
106
- (模板片实测 0.09 s)。这条超过 0.40 s,通常不是"片子坏了",而是词事件本身整体错位了。
106
+ (示例片实测 0.09 s)。这条超过 0.40 s,通常不是"片子坏了",而是词事件本身整体错位了。
107
107
 
108
108
  ### 4.3 裁剪会让词整体提前(`words_shift`)
109
109