@dsh-gis/dsh-video-studio-skill 0.7.2 → 0.9.1
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.
- package/CHANGELOG.md +1109 -0
- package/README.md +76 -10
- package/THIRD_PARTY_NOTICES.md +1 -0
- package/docs/styles/sheet-8-styles.png +0 -0
- package/docs/styles/sheet-before-after.png +0 -0
- package/index.js +4 -1
- package/package.json +7 -3
- package/schemas/film.schema.json +329 -2
- package/skills/video-studio/SKILL.md +270 -13
- package/skills/video-studio/assets/golden_frames.json +65 -0
- package/skills/video-studio/assets/scenes.py +2162 -1190
- package/skills/video-studio/assets/web/engine.js +63 -2
- package/skills/video-studio/assets/web/film.js +224 -101
- package/skills/video-studio/assets/web/index.html +4 -2
- package/skills/video-studio/assets/web/qa_text.js +36 -8
- package/skills/video-studio/references/audio-variation.md +4 -3
- package/skills/video-studio/references/audio.md +2 -2
- package/skills/video-studio/references/change-impact.md +1 -1
- package/skills/video-studio/references/quality-gates.md +25 -3
- package/skills/video-studio/references/scene-grammar.md +306 -0
- package/skills/video-studio/references/style-packs.md +108 -3
- package/skills/video-studio/references/v08-quality.md +256 -0
- package/skills/video-studio/references/web-engine.md +19 -2
- package/skills/video-studio/references/word-timeline.md +147 -0
- package/skills/video-studio/requirements.txt +14 -0
- package/skills/video-studio/scripts/filmcheck.py +218 -0
- package/skills/video-studio/scripts/filmkit.py +521 -38
- package/skills/video-studio/scripts/gates.py +88 -4
- package/skills/video-studio/scripts/grammar.py +559 -0
- package/skills/video-studio/scripts/preflight.py +774 -0
- package/skills/video-studio/scripts/qa_frames.py +49 -8
- package/skills/video-studio/scripts/qaasr.py +1563 -0
- package/skills/video-studio/scripts/qavmaf.py +1442 -0
- package/skills/video-studio/scripts/render.py +764 -32
- package/skills/video-studio/scripts/scorekit.py +16 -30
- package/skills/video-studio/scripts/selfcheck.py +483 -0
- package/skills/video-studio/scripts/sfxkit.py +130 -14
- package/skills/video-studio/scripts/stagewatch.py +19 -6
- package/skills/video-studio/scripts/stylekit.py +242 -12
- package/skills/video-studio/scripts/voicekit.py +15 -7
- package/skills/video-studio/scripts/webengine.py +1119 -72
- package/tools/ci_check.py +173 -31
- package/tools/estimate.py +130 -19
- package/tools/golden_frames.py +664 -0
- package/tools/silhouette_qa.py +910 -0
|
@@ -71,7 +71,8 @@ def build_audio(self, total=None, sr=SR, base_db=None, default_gain_db=0.0,
|
|
|
71
71
|
- **必须保证**:同 `fingerprint` 逐采样一致;不同 fingerprint 的相关性显著下降(§5 阈值)。
|
|
72
72
|
- 新增报告字段:`variation`(dict:`{"chord_swaps": n, "motif_shifts": n, "drum_ghosts": n}`),
|
|
73
73
|
写进 work/score.json,方便审计。
|
|
74
|
-
- legacy 路径(style=None
|
|
74
|
+
- legacy 路径(style=None)**逐字节不变**(`scorekit.py --selftest` 的第 20 条会验;
|
|
75
|
+
别和 `selfcheck.py` 的编号搞混——那边第 21 条只查 scorekit 同 seed 确定性)。
|
|
75
76
|
|
|
76
77
|
### 3.4 assets/scenes.py 的 cues()(B 路)
|
|
77
78
|
|
|
@@ -128,11 +129,11 @@ def build_audio(self, total=None, sr=SR, base_db=None, default_gain_db=0.0,
|
|
|
128
129
|
> 下面 1–4 的路径都按**仓库根**写(`python skills/video-studio/scripts/…`);如果你已经 `cd` 进技能目录,
|
|
129
130
|
> 去掉 `skills/video-studio/` 前缀即可。`<REPO>` = 仓库根 / 装好后的包根。
|
|
130
131
|
|
|
131
|
-
1. `python skills/video-studio/scripts/selfcheck.py` 全 PASS(现在 43
|
|
132
|
+
1. `python skills/video-studio/scripts/selfcheck.py` 全 PASS(现在 55 条;v0.7.2 时的 43 条、v0.8 的 51 条是旧的)。
|
|
132
133
|
2. `python skills/video-studio/scripts/scorekit.py --selftest` 全 PASS。
|
|
133
134
|
3. `python skills/video-studio/scripts/sfxkit.py --selftest` 全 PASS。
|
|
134
135
|
4. `python <REPO>/tools/ci_check.py` 全 PASS(在包根执行)。
|
|
135
|
-
5. legacy
|
|
136
|
+
5. legacy 逐样本不变:`scorekit.py --selftest` 第 20 条 + `selfcheck.py` 第 26(风格 legacy)/33(音效 legacy)条,**这些断言不要改**。
|
|
136
137
|
|
|
137
138
|
## 6. 协作规则
|
|
138
139
|
|
|
@@ -439,7 +439,7 @@ v0.5 的 style pack 决定的是**风格**(8 套),不是**这一部的具
|
|
|
439
439
|
|
|
440
440
|
| 层 | 行为 | 开关 |
|
|
441
441
|
|---|---|---|
|
|
442
|
-
| 配乐和声/动机/鼓型 | 内容指纹做稳定局部替换(每两小节、±1 级数、ghost 击) | `style: auto
|
|
442
|
+
| 配乐和声/动机/鼓型 | 内容指纹做稳定局部替换(每两小节、±1 级数、ghost 击) | `style: auto`(默认);`seed_from_content: false` **只关 seed 派生**(音符细节不再随内容变),结构变奏照旧——`render.py` 只要不是 legacy 就把 `fingerprint` 传给 scorekit(`st_score` 里 `fp_hex` 那段)。想连结构一起回到旧行为只有不写 `style`(legacy) |
|
|
443
443
|
| 音效 seed | `content_seed(base, style, fp, type, t)`,随内容变 | `sfx.content_seed`(默认 true;false = 旧 seed_base+i) |
|
|
444
444
|
| 提示音调性 | `in_key(freq, key)` 跟随配乐调性 | `CueBus.build_audio(key=...)`,render.py 自动传 |
|
|
445
445
|
| 段落边界 | [边界-2.2s, 边界] 内的 whoosh/riser 自动 -3 dB | `sfx.duck_transitions`(默认 true) |
|
|
@@ -477,6 +477,6 @@ D 组构造"有人声段 + 间隙",量人声段旁白/背景响度差。任何
|
|
|
477
477
|
### 13.4 改音频代码后的自检清单
|
|
478
478
|
|
|
479
479
|
1. `python <REPO>/tools/audio_similarity.py --strict` —— A/B/C 三组达标(在包根执行)。
|
|
480
|
-
2. `python selfcheck.py` ——
|
|
480
|
+
2. `python selfcheck.py` —— 55 条全 PASS(第 33 条盯 legacy 逐样本兼容,第 35–38 条盯旁白优先,第 39 条盯音频边界)。
|
|
481
481
|
3. `python scorekit.py --selftest` / `python sfxkit.py --selftest` —— 各自全 PASS。
|
|
482
482
|
4. 改 `assets/scenes.py` 的 `cues()` 后,跑一次 `--stage cues` 看类型分布是否覆盖 hook/body/quote/outro。
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
|---|---|---|
|
|
38
38
|
| 改了 `text` 只重跑 `tts` | 新语音配旧时间轴 → 字幕漂移、音效错位 | 跑 `--stage explain`,按它给的最小重跑命令 |
|
|
39
39
|
| 改了 `style` 只重跑 `frames` | 画风换了但配乐/声线还是旧的(或反之) | `style` 是所有下游的上游,通常整链重跑 |
|
|
40
|
-
| 手工删 `work/_plan.json` 绕过指纹 | 拼接出的片子画面和旁白对不上,且**不报错** | 删 `work/segments` 整目录重渲 |
|
|
40
|
+
| 手工删 `work/segments/_plan.json` 绕过指纹 | 拼接出的片子画面和旁白对不上,且**不报错** | 删 `work/segments` 整目录重渲 |
|
|
41
41
|
|
|
42
42
|
## 3. 一条命令代替这张表
|
|
43
43
|
|
|
@@ -1,7 +1,27 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 质量门禁(方法论 G0–G8)
|
|
2
2
|
|
|
3
3
|
Agent 看不见画面、听不见声音,所以**验收必须脚本化 + 静帧目检**双轨。
|
|
4
4
|
|
|
5
|
+
> ## ⚠ 两套编号别混用
|
|
6
|
+
>
|
|
7
|
+
> 本页的 `G0–G8` 是 v0.3 起的**方法论分组**(按"查什么"分类,写给写剧本/写场景的人看)。
|
|
8
|
+
> `render.py` 落进 `work/gates.json` 与 `out/QC-REPORT.md` 的,是**运行时账本 id**(按"哪一步判"排)。
|
|
9
|
+
> 两套编号**有重名但不同义**,看到 `G0` 先确认是哪一套:
|
|
10
|
+
>
|
|
11
|
+
> | 本页(方法论) | 账本 id(运行时) | 关系 |
|
|
12
|
+
> |---|---|---|
|
|
13
|
+
> | G0 内容门禁:上屏数字可溯源 | `G0` 渲染前资源预检(v0.8) | **同名不同义**:账本 G0 查字形/字体/TTS/web 页,不核对数字出处 |
|
|
14
|
+
> | G1 帧级门禁(确定性/单帧预算/元素存在性) | — | 人工/代码评审,不进账本 |
|
|
15
|
+
> | G2 静帧门禁(母版预览目检) | `signoff`(`stills` 闸) | 目检结论走人工签核 |
|
|
16
|
+
> | G3 音频门禁(音效/混音/母带) | `G5` 响度/真峰 | 账本只查成片级的两个数 |
|
|
17
|
+
> | G4 成片门禁(容器/时长/锐度/字幕/黑帧) | `G4` 容器 · `G5` 响度 · `G6` 锐度比 · `G7` 字幕条数 · `G8` 黑帧 | 账本把 G4 拆成 5 行 |
|
|
18
|
+
> | G7 时间层门禁(闪烁/空窗/同步) | `G9` 帧级深检 | **同名不同义**:账本 G9 才是时间层 |
|
|
19
|
+
> | G8 页面层门禁(web 文字重叠/出画) | web 引擎 qa_text 结果 | 只有 `--engine web` 才有 |
|
|
20
|
+
> | — | `G10` 词级时间戳 · `G11` VMAF/SSIM · `G12` ASR 回读 | **v0.8 新增**,详见 [v08-quality.md](v08-quality.md) 与 [word-timeline.md](word-timeline.md) |
|
|
21
|
+
>
|
|
22
|
+
> 一句话记法:**本页 G0–G8 是"查什么",账本 G0 + G4–G12 是"哪一行判"**。
|
|
23
|
+
> 账本里**没有** `G1`/`G2`/`G3`:`G0` 之后直接从 `G4` 开始(容器),人工目检走 `signoff` 的 `stills` 闸,不进 gate matrix。
|
|
24
|
+
|
|
5
25
|
## G0 内容门禁:上屏数字可溯源
|
|
6
26
|
|
|
7
27
|
| 判据 | 做法 |
|
|
@@ -61,7 +81,9 @@ pr = K.probe(film); f = pr["format"]
|
|
|
61
81
|
# 容器
|
|
62
82
|
assert f["duration"] > 0
|
|
63
83
|
v = [s for s in pr["streams"] if s["codec_type"] == "video"][0]
|
|
64
|
-
|
|
84
|
+
# v0.8:比的是**交付规格**不是内部构图。qa_report(expect_w=, expect_h=, expect_fps=) 会从
|
|
85
|
+
# timeline.json 的 video 块取期望值;缺省回落 1920x1080@30(老项目行为不变)。
|
|
86
|
+
assert (v["width"], v["height"]) == (W, H) # W/H 这里代表交付宽高,不是 1920x1080 常量
|
|
65
87
|
assert any(s["codec_type"] == "audio" for s in pr["streams"]), "无音轨"
|
|
66
88
|
|
|
67
89
|
# 时长
|
|
@@ -221,5 +243,5 @@ python webengine.py textqa ./web/index.html --step 0.2
|
|
|
221
243
|
信号:-16.3 LUFS / 真峰 -1.2 dBTP / 锐度比 0.94 / 黑帧 0 / 字幕 86 条 = 剧本 86 句
|
|
222
244
|
时间:单帧闪烁 0 / 画面空窗 0 / 音画同步最大偏差 38 ms
|
|
223
245
|
页面:文字重叠 0 / 出画 0 / 一闪而过 0 / 从没露面 0
|
|
224
|
-
自检:python selfcheck.py -> PASS
|
|
246
|
+
自检:python selfcheck.py -> PASS 55 / FAIL 0 / SKIP 0
|
|
225
247
|
```
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# 场景语法(Scene Grammar):内容决定构图
|
|
2
|
+
|
|
3
|
+
> v0.9.0 新增。配套文件:`skills/video-studio/scripts/grammar.py`(纯标准库)、
|
|
4
|
+
> `skills/video-studio/assets/scenes.py` 第三节(10 类绘制器)、`tools/silhouette_qa.py`(剪影门禁 G13)。
|
|
5
|
+
> 一句话:**这一段内容该长成什么构图,由内容自己决定;风格只决定"长成什么样"。**
|
|
6
|
+
|
|
7
|
+
## 0. 为什么需要它
|
|
8
|
+
|
|
9
|
+
v0.5 的"风格包"解决了"每部片的画风/配乐/旁白都差不多",但它只覆盖**皮肤**:
|
|
10
|
+
配色、字体族、纹理、颗粒、字距、缓动、HUD 三档、3 种版式几何。
|
|
11
|
+
|
|
12
|
+
画面骨架没有跟着变。证据在代码里:
|
|
13
|
+
|
|
14
|
+
- `assets/scenes.py` 的 v0.5 绘制器只有 3 个(`_DRAWERS` 三项:hook / body / outro);
|
|
15
|
+
- 叙事 kind 里的 quote / emphasis / calm 全部回落到 body 绘制器(`_DRAWERS.get(kind) or _draw_body`);
|
|
16
|
+
- 三个绘制器共用同一套骨架:背景板 → 眉标 → 大字标题 → 副标题 → 强调线 → HUD,
|
|
17
|
+
差异只在颜色、字号、位移;
|
|
18
|
+
- 于是 8 套风格换完,**剪影(灰度三值化后的前景占比 / 重心 / 边缘占用 / 对称度)几乎不动**。
|
|
19
|
+
|
|
20
|
+
用户的原话是"每个视频都差不多,只是主题和颜色不同"。这不是审美判断,是可以量化的:
|
|
21
|
+
改造前用 `tools/silhouette_qa.py` 的判据跑 8 套风格两两对比(28 对),
|
|
22
|
+
同明暗族 12 对里有 4 对五项指标一项都不达标——也就是说换风格真的只是换色。
|
|
23
|
+
|
|
24
|
+
v0.9 的做法不是"再加几套风格",而是把"内容 → 构图"这条选择逻辑独立成一层。
|
|
25
|
+
|
|
26
|
+
## 1. 三层正交
|
|
27
|
+
|
|
28
|
+
film.json(内容)
|
|
29
|
+
│ ① 场景语法:这段内容该长什么样(由内容决定,不由风格决定)
|
|
30
|
+
▼
|
|
31
|
+
scene_type ∈ {hero, statement, list, stat, compare, process, quote, chapter, chart, outro}
|
|
32
|
+
│ ② 视觉语言包:这套风格怎么画(motion / motif / transition / typography / bias)
|
|
33
|
+
▼
|
|
34
|
+
stylekit.VISUAL_LANGUAGES[style_id]
|
|
35
|
+
│ ③ 片内编排:同一部片里连续段落怎么不"复制粘贴"(变体轮换 / 装饰密度 / 相位,确定性)
|
|
36
|
+
▼
|
|
37
|
+
draw_frame(...) → PIL.Image
|
|
38
|
+
|
|
39
|
+
三层的边界要分清,否则会写出"改了不生效"的配置:
|
|
40
|
+
|
|
41
|
+
| 层 | 由谁决定 | 落在哪 | 改它会怎样 |
|
|
42
|
+
|---|---|---|---|
|
|
43
|
+
| 叙事 kind | 作者写 `lines[].kind` | hook / body / quote / outro / emphasis / calm | 换旁白语气与配乐能量(voicekit / scorekit 读它) |
|
|
44
|
+
| 场景 type | grammar 按内容打分,或作者显式写 `lines[].type` | `work/storyboard.json`、timeline 的 `scene_type` | 换构图(10 个绘制器之一) |
|
|
45
|
+
| 视觉语言 | 风格 id(`style`) | `work/style.json` 的 `language` | 换这套风格的骨架参数(母题 / 转场 / 字距 / 重心) |
|
|
46
|
+
|
|
47
|
+
**为什么没有新增 kind 值**:kind 同时是 voicekit 的语气 id 与 filmcheck 的 `KINDS`
|
|
48
|
+
(两者是同一组 6 个值)。把 hero/stat 塞进 kind 会撞语气枚举,也让"引语"和"引语构图"
|
|
49
|
+
这种本来可以分开的两件事重新粘死。所以 type 是**新字段**,与 kind 正交:
|
|
50
|
+
一句话可以是 `kind=quote`(女声念引语)同时 `type=chapter`(画成章节卡)。
|
|
51
|
+
|
|
52
|
+
## 2. 10 类构图
|
|
53
|
+
|
|
54
|
+
坐标基于 1920×1080;正文底界 860(下方是字幕带),HUD 顶部 ≤96。
|
|
55
|
+
"最短时长"是 `grammar.MIN_DUR`(`skills/video-studio/scripts/grammar.py:49`)的硬门槛,
|
|
56
|
+
低于它按降级表换类型(见 §3.3)。类型名单与顺序在 `grammar.SCENE_TYPES`(grammar.py:30),
|
|
57
|
+
绘制器注册表在 `assets/scenes.py:1979`(`_DRAWERS`)。
|
|
58
|
+
|
|
59
|
+
| 类型 | 构图 / 重心 | 吃哪些字段 | 持续动效 | 自动选型特征 | 最短 |
|
|
60
|
+
|---|---|---|---|---|---|
|
|
61
|
+
| `hero` 封面 | 左栏起标题,右侧留白;variant 0/1/2 分别左/右/中 | `big` / `sub`(lead) / `kicker` / `chapter` / 顶层 `title` | 标题下强调线呼吸 + 背景同心弧缓推 | 首段或 `kind=hook` | 2.4 s |
|
|
62
|
+
| `statement` 金句 | 居中单列 ≤1400,逐字显现(字距随呼吸微动) | `big` 或 `text` 前 24 字;`sub` 作落款 | 下划线从左生长 + 字距呼吸 | 无条目且短句(≤16 字) | 2.6 s |
|
|
63
|
+
| `list` 要点 | 左标题 + 右条目列;variant 1 转全宽序号列 | `items[]`、`big`、`kicker` | 焦点行每 2.2 s 换一条(左侧竖条 + 行底微亮) | `items ≥ 3` | 3.0 s |
|
|
64
|
+
| `stat` 数字卡 | 巨数字居中(初始 240 px,超宽自动缩)+ 单位 + label | `stats[]`;缺省从 `text`/`sub` 正则抽"数字+单位" | count-up + 底部 12 格刻度逐格点亮 | 文本含数字+单位且 `items < 3` | 3.2 s |
|
|
65
|
+
| `compare` 左右对比 | 中缝竖线,左右双栏;栏头缺省时中缝上方画 VS 徽标 | `sides{}`(`left/right` 各 `head/items`);缺省把 `items` 前后对半分 | 中缝高光下扫 + 左右两栏轮流反相高亮(3.2 s) | 命中对比词且 `items ≥ 2` | 4.0 s |
|
|
66
|
+
| `process` 流程轴 | 横向轴 + 最多 5 个节点,标签在轴上方、注在下方 | `steps[]`(`label/note`);缺省按 `items` 顺序当步骤 | 进度光点沿轴匀速走完本段 | 命中顺序词且 `items` 3–6 条 | 4.0 s |
|
|
67
|
+
| `quote` 引语 | 左上巨型引号(字形不是矢量线)+ 引文 46 px 起 + 右下出处 | `quote` / `by`;缺省从 `text` 剥引号与出处 | 引号呼吸上浮 + 末行光标块闪烁 | 命中引语标记(「」""——说:…) | 4.0 s |
|
|
68
|
+
| `chapter` 章节卡 | 左上巨型编号(矩形裁剪揭示)+ 章节名 + 中缝横线 | `chapter`(编号从 `"03 · xxx"` 里取)/ `big` / `kicker` | 细光带在编号上缘横向扫(2.8 s 周期) | 章节变化 + 无条目 + 标题短 + 段短 + 无数字/引语 | 2.2 s |
|
|
69
|
+
| `chart` 数据条 | 左侧标签列 + 横向条 + 数值;最多 6 条 | `series[]`(`label/value/unit`);缺省从 `items` 抽"标签+数字" | 最大条端点呼吸 + 网格高亮扫描 | `series` 非空,或 `items ≥ 3` 且 ≥3 条带数字 | 4.5 s |
|
|
70
|
+
| `outro` 收尾 | 正中心大字 + 副标 + 强调线;光点环绕,数量随母题变 | `big` / 顶层 `end_text` / `sub` / `title` | 强调线呼吸 + 光点环向内收 | 末段或 `kind=outro` | 2.4 s |
|
|
71
|
+
|
|
72
|
+
四条兜底(绘制器里的运行时兜底,与 §3.3 的段长门是两回事):
|
|
73
|
+
|
|
74
|
+
- `stat` 拿不到任何数字 → 退到 `statement`;
|
|
75
|
+
- `process` 步骤 < 2 条 → 退到 `list`;
|
|
76
|
+
- `chart` 序列 < 2 条 → 退到 `stat`;
|
|
77
|
+
- `compare` 对半分不出两栏 → 退到 `statement`。
|
|
78
|
+
|
|
79
|
+
**容量上限**(超了会被静默截断,画面少几行):`stats` 画前 3 条、`series` 前 6 条、
|
|
80
|
+
`steps` 前 5 个、`sides` 每侧前 3 条。filmcheck 对超容量报 `scene_capacity` **WARN**
|
|
81
|
+
(不阻断,但说明"那几条不会出现在画面上")。
|
|
82
|
+
|
|
83
|
+
## 3. 自动选型:打分 → 段长门 → 降级 → 去重
|
|
84
|
+
|
|
85
|
+
选型是纯函数(`grammar.score_types` / `choose_type` / `downgrade_for`,
|
|
86
|
+
`skills/video-studio/scripts/grammar.py:202/303/315`),不读 random / time / hash(),
|
|
87
|
+
派生量只用 sha1 前 8 位。**同一份稿子永远同一张分镜表。**
|
|
88
|
+
|
|
89
|
+
### 3.1 打分
|
|
90
|
+
|
|
91
|
+
原则:**字段存在性 > 关键词 > 结构位置**。字段是作者明说的意图,关键词是猜的。
|
|
92
|
+
|
|
93
|
+
| 信号 | 加权 | 条件 |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| 显式 `type` | +10.0 | 值在 `SCENE_TYPES` 里;压倒一切 |
|
|
96
|
+
| `stats[]` 非空 | +6.0 | → stat |
|
|
97
|
+
| `series[]` 非空 | +6.0 | → chart |
|
|
98
|
+
| `sides{}` 非空 | +6.0 | → compare |
|
|
99
|
+
| `steps[]` 非空 | +6.0 | → process |
|
|
100
|
+
| `quote` 非空 | +6.0 | → quote |
|
|
101
|
+
| `items ≥ 3` | +5.0 | → list(通用兜底,所以权重低于更具体的形态) |
|
|
102
|
+
| `items ≥ 3` 且 ≥3 条带数字 | +5.5 | → chart |
|
|
103
|
+
| 命中对比词且 `items ≥ 2` | +5.5 | → compare |
|
|
104
|
+
| 命中顺序词且 `items` 3–6 | +5.5 | → process |
|
|
105
|
+
| 命中引语标记 | +5.5 | → quote |
|
|
106
|
+
| 文本含"数字+单位"且 `items < 3` | +3.5 | → stat |
|
|
107
|
+
| 无条目且短句(≤16 字) | +3.5 | → statement |
|
|
108
|
+
| 首段或 `kind=hook` | +4.6 | → hero |
|
|
109
|
+
| 末段或 `kind=outro` | +5.0 | → outro |
|
|
110
|
+
| 章节变化 + 纯标题卡 | +4.2 | → chapter;还要满足无条目、标题 ≤14 字、段 ≤4.0 s、无数字、无引语 |
|
|
111
|
+
|
|
112
|
+
几个刻意的取舍(都是实测反例逼出来的,别"优化"掉):
|
|
113
|
+
|
|
114
|
+
- **关键词只看叙述文字,不看 `items`**。反例:模板片第 3 段正文讲"画质三件事",
|
|
115
|
+
条目里写的是 `rawvideo -> libx264 CRF17`,`->` 命中顺序词表,整段被误判成"流程轴"——
|
|
116
|
+
画面上一条横轴连着四个不相干的要点。条目是载荷不是叙事。
|
|
117
|
+
- **`hero`/`outro` 的结构分刻意低于字段分**:开场如果带 5 条要点,观众要看的是要点,
|
|
118
|
+
不是封面。结构位置只是"没有更具体形态时"的兜底。
|
|
119
|
+
- **`list` 只给 5.0**:它是"有条目"的通用兜底;一旦有更具体的形态(数字、对比、流程),
|
|
120
|
+
必须压过它——否则"能画"永远赢过"该画",所有带条目的段都会退化成列表。
|
|
121
|
+
- **年份不算数字**:单位表里有"年",但 `2024 年` 这种纯年份被单独排除,
|
|
122
|
+
它是时间背景不是"值得做成巨字卡"的数据。真要做数字卡,写 `stats[]`。
|
|
123
|
+
|
|
124
|
+
平手时按 `grammar.SCENE_TYPES` 的固定顺序取位(**改顺序会让同一稿子的分镜漂移**,
|
|
125
|
+
所以这个元组有独立语义,不能随便排序)。
|
|
126
|
+
|
|
127
|
+
### 3.2 段长门
|
|
128
|
+
|
|
129
|
+
每类有最短时长(`MIN_DUR`,见 §2 表格)。段长不足时**不是降分,是不许选**:
|
|
130
|
+
候选保留但分数 −6.0 并写明理由,便于审计;若所有候选都被段长门排除,退到 `statement`
|
|
131
|
+
(最耐短的类型)。
|
|
132
|
+
|
|
133
|
+
### 3.3 降级表
|
|
134
|
+
|
|
135
|
+
超过段长门后仍可能被降级(例如显式写了 `type=chart` 但这段只有 2 秒):
|
|
136
|
+
|
|
137
|
+
| 原类型 | 降级到 |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `chart` / `compare` / `process` | `list` |
|
|
140
|
+
| 其余(含 `list`、`hero`、`chapter`、`stat`、`quote`、`outro`) | `statement` |
|
|
141
|
+
|
|
142
|
+
降级表写死在 `grammar.DOWNGRADE`(grammar.py:63)里,防环(配置写错时不会挂死)。
|
|
143
|
+
降级不是静默的:`storyboard.json` 的 `reason` 里会写
|
|
144
|
+
"段长 x.xxs < y.ys,降级 chart -> list"。
|
|
145
|
+
|
|
146
|
+
### 3.4 类型去重
|
|
147
|
+
|
|
148
|
+
连续同类型最多 `MAX_REPEAT = 2` 段(grammar.py:77)。第 3 段如果还是同一类型,该候选 −5.0
|
|
149
|
+
(`chapter` / `outro` 除外——章节卡连排是刻意的分章锚点)。
|
|
150
|
+
效果是"三连 list"会变成 list → list → 下一个候选(通常是 statement 或别的更具体形态)。
|
|
151
|
+
|
|
152
|
+
## 4. `work/storyboard.json`:审计产物
|
|
153
|
+
|
|
154
|
+
`--stage layout`(以及 `--stage all`)会把选型结果落盘到 `work/storyboard.json`,
|
|
155
|
+
并在控制台打印一行分镜摘要 + 每段的"为什么":
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
python render.py --script film.json --scenes scenes.py --work work --stage layout
|
|
159
|
+
# 分镜: 4 段 / 封面×1 要点列表×2 收尾×1
|
|
160
|
+
# l01 hook -> hero v0 d0 big 后缩进
|
|
161
|
+
# l02 body -> list v0 d2 items>=3
|
|
162
|
+
# l03 body -> list v2 d1 items>=3
|
|
163
|
+
# l04 outro -> outro v0 d0
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
字段(`grammar.plan_scenes`(grammar.py:467)的返回结构):
|
|
167
|
+
|
|
168
|
+
| 字段 | 含义 |
|
|
169
|
+
|---|---|
|
|
170
|
+
| `version` | 目前恒为 1 |
|
|
171
|
+
| `generator` | `"grammar 0.9.0"`(改版本号时同步,便于区分不同版本的分镜表) |
|
|
172
|
+
| `style` / `legacy` | 本片风格 id / 是否走 v0.4 兼容路径。**legacy 不落 storyboard**(见 §7) |
|
|
173
|
+
| `scenes[]` | 每段一行:`id`、`kind`、`type`、`variant`(0–2)、`decor`(0–2)、`layout`、`dur`、`reason[]` |
|
|
174
|
+
| `type_counts` | `{"hero": 1, "list": 2, ...}`,给了哪些类型用过几次 |
|
|
175
|
+
|
|
176
|
+
同一份结果也会**就地**写进 timeline 的每个 scene(渲染器读这些字段):
|
|
177
|
+
`scene_type` / `scene_variant` / `scene_decor` / `scene_layout` / `scene_phase` / `scene_reason`。
|
|
178
|
+
timeline 顶层再加一个 `storyboard: {"type_counts": ...}` 摘要。
|
|
179
|
+
|
|
180
|
+
想只看选型、不跑 TTS:`python grammar.py film.json`(按字数粗估时长,只用于看选型,
|
|
181
|
+
不是时间轴权威)。
|
|
182
|
+
|
|
183
|
+
## 5. 显式指定 `lines[].type`
|
|
184
|
+
|
|
185
|
+
不写 `type` 就自动选;写死在 `film.json` 的行级字段上(全部可选):
|
|
186
|
+
|
|
187
|
+
```json
|
|
188
|
+
{"id": "l03", "kind": "body", "text": "残差网络在 2015 年把错误率降到 3.6%。",
|
|
189
|
+
"type": "stat",
|
|
190
|
+
"stats": [{"value": "3.6", "unit": "%", "label": "ImageNet top-5 错误率", "note": "2015"}],
|
|
191
|
+
"chapter": "02 · 数据"}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
| 字段 | 类型 | 给谁用 | 说明 |
|
|
195
|
+
|---|---|---|---|
|
|
196
|
+
| `type` | 枚举(10 类) | 绘制器派发 | 写了就以它为准(+10 分),但仍受段长门降级 |
|
|
197
|
+
| `quote` | 字符串 | `quote` | 打在屏幕上的引文;`text` 是 TTS 念的那句,两者可以不同 |
|
|
198
|
+
| `by` | 字符串 | `quote` | 出处/作者;缺省不画署名 |
|
|
199
|
+
| `stats[]` | `[{value, unit?, label?, note?}]` | `stat` | `value` 必填(数字或字符串,如 `">3x"`) |
|
|
200
|
+
| `series[]` | `[{label, value, unit?}]` | `chart` | 最多取 6 条 |
|
|
201
|
+
| `steps[]` | `[{label, note?}]` | `process` | 最多取 5 个节点 |
|
|
202
|
+
| `sides{}` | `{"left": {head?, items[]}, "right": {...}}` | `compare` | 每侧最多取 3 条 |
|
|
203
|
+
|
|
204
|
+
**写错 `type` 会被 filmcheck 报 ERROR**(枚举 `SCENE_TYPES`,唯一硬门禁:
|
|
205
|
+
`skills/video-studio/scripts/filmcheck.py:288`,schema 侧在 `schemas/film.schema.json:246`);
|
|
206
|
+
`grammar` 自己遇到非法值时**静默忽略**(当作没写,继续按内容自动选)。
|
|
207
|
+
其余新字段写错键名会被 filmcheck 报 WARN("是不是想写 X"),不会静默失效。
|
|
208
|
+
|
|
209
|
+
## 6. 片内编排(防"每屏同一个骨架")
|
|
210
|
+
|
|
211
|
+
| 维度 | 规则 | 现状 |
|
|
212
|
+
|---|---|---|
|
|
213
|
+
| 版式变体 | `variant_of(type, chapter, k)` 派生 0/1/2,并强制与上一段同类型不同 | 写入 storyboard 的每段;**当前只有 hero / statement / list / stat 四个绘制器读取它**,其余六类构图唯一 |
|
|
214
|
+
| 装饰密度 | `decor_of`(grammar.py:349)三档 0.55 / 0.75 / 1.0,每 3 段回落到最低档 | 所有绘制器都读(经 `_apply_motif` → `_decor_scale`) |
|
|
215
|
+
| 相位偏移 | `phase_of(idx, chapter)` = 2π × sha1 派生值 | hero / statement / list / compare / process / quote / chapter / chart / outro 的持续动效都用它,避免同频"复印" |
|
|
216
|
+
|
|
217
|
+
变体号、密度档、相位都**不按时间/帧号/随机数**派生——同一稿子同一结果,
|
|
218
|
+
这是"可复现"的一部分。改 `chapter` 字段会改变 `variant_of` 的输入,进而改变变体号。
|
|
219
|
+
|
|
220
|
+
## 7. legacy 边界:不写 `style` = v0.4 三个老绘制器
|
|
221
|
+
|
|
222
|
+
`film.json` 不写 `style` 字段时整条 v0.9 链路都不参与:
|
|
223
|
+
|
|
224
|
+
- `render.st_layout` 不调用 `grammar.plan_scenes`,不写 `work/storyboard.json`,
|
|
225
|
+
timeline 里也不会出现 `scene_type` 等字段;
|
|
226
|
+
- `scenes.draw_frame`(`assets/scenes.py:2003`)读 `plan["legacy"]` 为真,
|
|
227
|
+
直接走 `_LEGACY_DRAWERS`(scenes.py:232,hook / body / outro 三个 v0.4 绘制器),逐字节不变;
|
|
228
|
+
- 金帧基线(`skills/video-studio/assets/golden_frames.json`)就是钉这条的:
|
|
229
|
+
legacy 项目跑出来的 3 帧像素与基线一致,改坏了立刻红。
|
|
230
|
+
|
|
231
|
+
想让老项目享受新构图,写 `"style": "auto"` 或任意风格 id 即可;
|
|
232
|
+
但注意这会同时换掉配色/配乐/旁白(v0.5 的风格包是整体生效的),做好重看一遍的准备。
|
|
233
|
+
|
|
234
|
+
## 8. 加第 11 类构图(8 步)
|
|
235
|
+
|
|
236
|
+
以新增 `timeline`(时间轴图)为例。顺序不能乱,每步都有对应的门禁/断言在盯:
|
|
237
|
+
|
|
238
|
+
1. **`grammar.SCENE_TYPES`(grammar.py:30)末尾追加** `"timeline"`。**只追加不改顺序**——
|
|
239
|
+
顺序是平手取位顺序,插队会让所有老稿子的分镜漂移。同时补 `SCENE_LABELS`。
|
|
240
|
+
2. **`grammar.MIN_DUR` 加最短时长**(按这套构图入场+读完所需的秒数,别照抄别的类型),
|
|
241
|
+
并在 `DOWNGRADE` 里给它一个降级目标(键必须都在 `SCENE_TYPES` 里,且不能自环)。
|
|
242
|
+
3. **`grammar.score_types` 加打分规则**:优先"字段存在性"(比如 `timeline: []` 给 +6.0),
|
|
243
|
+
其次才是关键词/结构位置。加上去之前先问:它和 chart / process 的区别是什么?
|
|
244
|
+
如果画面上差别不大,就不要加——10 类已经覆盖了"数字/列表/对比/流程/引语/章节"这些基本形态。
|
|
245
|
+
4. **`assets/scenes.py` 加绘制器** `_draw_timeline(scene, t, dur, tg, script, frame, plan, th)`,
|
|
246
|
+
签名与现有 10 个一致。约定:
|
|
247
|
+
- 先用 `_bg_plate` / `_apply_motif` 铺底,最后 `_apply_veil` + `_hud`;
|
|
248
|
+
- 坐标过 `_view`(视差),文字用 `_draw_text`(字号不重采样);
|
|
249
|
+
- 持续动效至少一个(相位用 `_phase(scene)`),否则 3–6 s 的静态画面会撞"画面静止"判据;
|
|
250
|
+
- 重的东西预烘焙并合成进底版(参考 `_motif_layer` + `_plate`:一次烘焙,逐帧只 copy/EXTENT
|
|
251
|
+
约 1.3–2.0 ms)。**不要逐帧全屏 RGBA paste**(实测 5.6 ms,v0.9.0 踩过);
|
|
252
|
+
也不要每帧全屏 GaussianBlur / 全屏双线性 resize(预算 25 ms)。
|
|
253
|
+
5. **`assets/scenes.py` 的 `_DRAWERS` 加键**。缺了会在渲染时回落 list 并 warn 一次。
|
|
254
|
+
6. **`filmcheck.SCENE_TYPES` 加值**(这份只做成员校验,顺序无所谓),
|
|
255
|
+
以及 **`schemas/film.schema.json` 的 `lines[].type` 枚举**加值(IDE 补全与文档)。
|
|
256
|
+
7. **`selfcheck.py` 第 54 条(`scripts/selfcheck.py:1526`)会自动兜底**:它断言 grammar / filmcheck / scenes.`_DRAWERS` /
|
|
257
|
+
schema enum 四处**集合相等**(顺序允许不同)。这四份真值来源必须同时改,漏一处立刻红。
|
|
258
|
+
8. **`tools/silhouette_qa.py` 的 `SCENE_CONTENT` 补一段合成内容**(给足该类型的原料字段),
|
|
259
|
+
再跑一次剪影门禁确认新类型在 8 套风格下也"看得出来"。这一步不做,等于新加的构图
|
|
260
|
+
没有进入 G13 的覆盖范围。
|
|
261
|
+
|
|
262
|
+
跑完的验证顺序:`python selfcheck.py`(52–55 条)→ `python tools/ci_check.py`
|
|
263
|
+
(语法/包内容/文档路径)→ `python tools/silhouette_qa.py`(28 对仍全过)。
|
|
264
|
+
|
|
265
|
+
## 9. 常见误用
|
|
266
|
+
|
|
267
|
+
| 现象 | 原因 | 修法 |
|
|
268
|
+
|---|---|---|
|
|
269
|
+
| 分镜表里一段都没变,全是 list | 没有写 `style`(走 legacy),grammar 根本没跑 | 写 `"style": "auto"` 或某个风格 id |
|
|
270
|
+
| 某段想要数字卡,结果画成列表 | 没有 `stats[]`,文本里的数字是"2024 年"(年份被排除)或没有单位 | 显式写 `stats[]`;或让那句话带"数字 + 单位" |
|
|
271
|
+
| 写了 `type: "stats"` 却没生效,还报错 | `type` 是枚举,`stats` 才是字段名;写错值 filmcheck 报 ERROR | 改 `type` 为 10 类之一,数据放 `stats[]` |
|
|
272
|
+
| 对比段左右两栏一栏空 | 只有 1 条 `items`(对半分不出来),或 `sides{}` 只写了一侧 | 补到 ≥2 条,或显式写 `sides` 两侧 |
|
|
273
|
+
| 流程轴上的箭头/破折号来自条目 | 关键词只看叙述文字,不看 `items`——但如果你把"→"写进了 `text/big`,仍会命中 | 流程用 `steps[]` 或叙述里写"第一步…然后…";条目里的排版符号不算顺序 |
|
|
274
|
+
| 章节卡把整段要点吃掉了 | 章节卡要求"无条目 + 标题 ≤14 字 + 段 ≤4.0 s",但显式 `type=chapter` 会压过这些条件 | 章节卡单独成段(不带 items),要点另起一段 |
|
|
275
|
+
| 段长 2 秒写了 `type=quote`,出来是金句 | 段长门:quote 最短 4.0 s,按降级表落到 statement | 拆句/加停顿把段拉长,或接受降级;`storyboard.json` 的 reason 会写明 |
|
|
276
|
+
| 同一部片里前深后浅,闪烁门禁红了 | 同一部片仍锁一个明暗族(跨族会撞闪烁门禁) | 用同族主题;或拆成两部片 |
|
|
277
|
+
| 写了 5 条 `series`,画面只显示 3 条 | 容量上限:chart 只画前 6 条(stats 3 / steps 5 / sides 每侧 3) | filmcheck 会给 `scene_capacity` WARN;要全画就拆成多段 |
|
|
278
|
+
| 改了 `chapter` 后分镜变了 | `variant_of` / `chapter` 是变体与章节卡的输入 | 这是期望行为;要固定版式就写 `visual.layout` |
|
|
279
|
+
|
|
280
|
+
## 10. 已知限制(本版没做,写下来别装看不见)
|
|
281
|
+
|
|
282
|
+
- **web 引擎不跟随场景语法**:`--engine web` 的页面由用户自己写(`assets/web/film.js`),
|
|
283
|
+
本次不改造。web 路线的画风仍只有主题色,没有 10 类构图;
|
|
284
|
+
剪影门禁也只覆盖 PIL 渲染器。
|
|
285
|
+
- **同一部片仍锁一个明暗族**:段界跨明暗族会整屏换底,精确触发闪烁门禁,观感也像换片。
|
|
286
|
+
主题覆盖(`scene["theme"]`)只允许同族。
|
|
287
|
+
- **`lines[].type` 写错只有 filmcheck 拦**:grammar 对非法值静默回落(当没写)。
|
|
288
|
+
不跑 filmcheck 的流水线,写错就只是"没生效"。
|
|
289
|
+
- **场景类型有 4 份真值来源**:`filmcheck.SCENE_TYPES`(`skills/video-studio/scripts/filmcheck.py:51`)/
|
|
290
|
+
`grammar.SCENE_TYPES`(grammar.py:30)/
|
|
291
|
+
`scenes._DRAWERS` / schema 的 enum。靠 selfcheck 第 54 条断言"集合相等"兜底;
|
|
292
|
+
**顺序可以不同**——grammar 的那份顺序用于平手取位,另外三份只看成员。
|
|
293
|
+
- **版式变体只有 4 类绘制器读**:`variant_of` 对每段都算并写进 storyboard,
|
|
294
|
+
但当前只有 hero / statement / list / stat 用它,其余六类构图唯一。
|
|
295
|
+
这条不修的话,"每类型 3 个变体"只在四类上成立。
|
|
296
|
+
- **部分视觉语言字段"已声明、已体检、但绘制器没读"**:`motion.enter`、`motion.push`、
|
|
297
|
+
`typography.align`、`typography.ratio`(以及 `layout_cycle`)在
|
|
298
|
+
`stylekit.VISUAL_LANGUAGES` 里声明、`stylekit.visual_lint` 会查范围,
|
|
299
|
+
但 `assets/scenes.py` 当前不读它们——写了不会报错,也不会改变画面。
|
|
300
|
+
详见 [style-packs.md](style-packs.md) 的"视觉语言包"一节。
|
|
301
|
+
- **场景数据超容量会被截断**:`stats` >3 / `series` >6 / `steps` >5 / `sides` 每侧 >3
|
|
302
|
+
的条目不会出现在画面上。filmcheck 报 `scene_capacity` WARN(容量表在
|
|
303
|
+
`skills/video-studio/scripts/filmcheck.py:652`),但**不阻断渲染**——
|
|
304
|
+
WARN 不是通过,也不是失败,是"画面上没有"。
|
|
305
|
+
- **`work/storyboard.json` 是审计产物,不是输入**:改它不会改变渲染结果;
|
|
306
|
+
想要固定构图请改 `film.json` 的 `lines[].type`。
|
|
@@ -112,7 +112,9 @@ v0.4 及以前,所有"身份参数"都是模块级常量:
|
|
|
112
112
|
| voice.jitter | 0.35 | 语速/音高微扰,0 = 关 |
|
|
113
113
|
| voice.max_distinct | 3 | 全片最多几把声音 |
|
|
114
114
|
| pacing.* | 风格默认 | 语前留白/语后留白/最短段长/条目落位步长 |
|
|
115
|
-
| lines[].voice/tone
|
|
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 |
|
|
116
118
|
|
|
117
119
|
## 4. 三级指纹:改错别字不会换整首曲子
|
|
118
120
|
|
|
@@ -123,7 +125,9 @@ v0.4 及以前,所有"身份参数"都是模块级常量:
|
|
|
123
125
|
| style | 标题(配合关键词评分破平) | 风格 id |
|
|
124
126
|
|
|
125
127
|
所以:改一个错别字 -> full 变(音色纹理微变),tempo/style 不变(还是同一首曲子、同一个调);
|
|
126
|
-
换一部片 ->
|
|
128
|
+
换一部片 -> 三个都变。`music.seed_from_content: false` 只锁住 **seed 派生**(音色细节);
|
|
129
|
+
`full` 指纹仍会传给 scorekit 参与结构变奏(和弦替换/动机偏移/鼓型 ghost,见
|
|
130
|
+
[audio-variation.md](audio-variation.md) §3.3)。要把声音完全锁回 v0.4 行为,只能不写 `style`(legacy)。
|
|
127
131
|
|
|
128
132
|
## 5. 片内变化:整体是一套,但每段有呼吸
|
|
129
133
|
|
|
@@ -173,6 +177,107 @@ stylekit.theme_lint(theme) 在 --stage style 就拦,不用等渲完 300 帧:
|
|
|
173
177
|
|---|---|---|
|
|
174
178
|
| 画面换了、配乐还是老的 | 只改了 visual.theme 没改 music.style | 直接改 style,或两个都写 |
|
|
175
179
|
| 同一部片前后像两部片 | 段界跨了明暗族 | 用同族主题;或拆成两部 |
|
|
176
|
-
| 改台词后配乐变了 | 改了标题或 kind 结构 -> tempo
|
|
180
|
+
| 改台词后配乐变了 | 改了标题或 kind 结构 -> tempo 指纹变(BPM/调性跟着换) | 写死 music.key / music.bpm(`seed_from_content:false` 只管音色 seed,压不住 BPM/调性变化) |
|
|
177
181
|
| 旁白听感像广播剧 | max_distinct 太大 / 相邻句频繁换人 | 降到 2,靠 tone 而不是换人做变化 |
|
|
178
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
|
+
|