news 2026/9/10 3:51:40

Hyperframes 嵌入字幕分组实战:如何把 Whisper 词级转写切分为 plan.json 视觉短语

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Hyperframes 嵌入字幕分组实战:如何把 Whisper 词级转写切分为 plan.json 视觉短语

Hyperframes 嵌入字幕分组实战:如何把 Whisper 词级转写切分为 plan.json 视觉短语

【免费下载链接】hyperframesWrite HTML. Render video. Built for agents.项目地址: https://gitcode.com/GitHub_Trending/hy/hyperframes

本篇技术指南围绕 Hyperframes 内置embedded-captions技能中的核心编排环节——**字幕分组(Caption Grouping)**展开,讲解如何把 Whisper 生成的词级转写(transcript.json)转成plan.json中的groups[]数组,让每个字幕组成为"一个视觉短语":进场、逐词揭示、退场。你将掌握五类切分边界、组时间窗口的精确计算公式、样式与语气(style/tone)的选取规则,以及如何借助仓库内的fill-timings.cjscheck-timing.cjs脚本保证时间戳与转写严格对齐。

分组的目标:一组 = 一个视觉短语

在 Hyperframes 的embedded-captions工作流中,最终呈现在画面上的不是逐词飘过的字幕条,而是一组一组有排版身份的视觉短语。每个 group 拥有统一的入场动画、统一的字重字号、统一的语义角色(intro / phrase / emph / dream / crown),并在组内按词做卡拉 OK 式的逐词揭示。

判断分组是否合理的经验法则很简单:1 个 group ≈ 1 个逗号到逗号的小句(comma-to-comma clause),或 1 次呼吸的气口(breath of speech)。换句话说,分组依据的是语义和语调的自然停顿,而不是机械的"每 N 个词一组"。这是本技能与普通烧录字幕最大的分野:普通字幕像法庭记录,嵌入字幕是"写在画面里的排版"。

输入:从transcript.json的词级转写开始

分组的输入是由scripts/transcribe.cjs生成的transcript.json。该脚本读取项目目录下的source.mp4,优先通过uvx驱动 WhisperX(wav2vec2 强制对齐,词级时间精度远高于 whisper.cpp 的段级插值;可通过TRANSCRIBE_ENGINE=whisperx|whisper切换,模型默认small,可用WHISPER_MODEL覆盖),输出结构如下:

{ "words": [ { "text": "Some", "start": 0.24, "end": 0.44, "type": "word" }, { "text": " ", "start": 0.44, "end": 0.48, "type": "spacing" }, { "text": "memories", "start": 0.48, "end": 0.82, "type": "word" } ] }

做分组的第一步是丢弃type: "spacing"的条目——你只需要带真实时间的word条目。从源码实现看,transcribe.cjs会做三类健壮性收尾:

  • 对齐缺失的词:WhisperX 偶尔给出无时间戳的词(OOV、数字),脚本会从前一词的end与后一词的start插值补齐;
  • 尾部幻觉词裁剪:Whisper 会在静音尾巴上"编造"重复词(如反复说 "I'm sorry."),脚本用 ffmpegsilencedetect找出真实可听内容的终点,删除落在其后 0.4 秒之外的词;
  • 近静音护栏:整段平均音量低于 -45dB 时直接警告"这是幻觉转写,不是真实语音",提醒遵守决策门禁、拒绝为编造的词配字幕。

正是因为这层清洗,transcript.json的词级时间才是后续分组与对齐的可靠基准。这也解释了为什么技能里强调:转写是垃圾就拒绝配字幕,一个由幻觉词拼成的逐字 rail 比没有字幕更糟。

切分边界:满足任意一条就开新组

把词流切成组时,以下五个断点条件任意命中其一,就应在该处新起一个组:

  1. 停顿 ≥ 500msword.end[i]word.start[i+1]之间的间隙)——说话人换了一口气;
  2. 句末终止符——词以.?!结尾,或出现破折号式的长停;
  3. 强逗号——,后跟 ≥ 250ms 的停顿;
  4. 话语重置(discourse reset)——"but"、"so"、"and then"、"you know" 这类开启新小句的词,往往值得独占一组或开启新组;
  5. 组达到 6 个词或 2.5 秒——哪个先到算哪个。过长的组读起来像字幕条,而不是嵌入排版。

同时还有三条硬约束必须满足:

  • 每组最少 2 个词(唯一的 1 词例外:感叹词如 "Wait.",或 crown 金句行);
  • 组在屏时长最少 0.5 秒,不足则并入相邻组;
  • 组与组不得在时间上重叠——同一时刻至多一个组可见。

第 5 条边界(6 词 / 2.5 秒上限)与"1 组 ≈ 1 个小句"的目标相互配合:短句不会因为停顿不够而长到失去节奏,长句则被自然断点拆成若干视觉呼吸单位。

组时间窗口:inout的精确算法

对一组词w[0]..w[n-1],组窗口的时间按下式计算:

  • in = w[0].start - 0.08——在第一个词出声前稍早进场(约提前 80ms),避免"词到了字还没到"的错位感;
  • out = min(next_group.in - 0.05, w[n-1].end + 0.6)——最后一个词结束后再滞留约 0.6 秒让观众读完,但绝不能撞上下一个组的in(留 50ms 安全间隙);
  • 最后一组如需可延伸到视频结尾。

从实现层面看,这个公式与scripts/check-timing.cjs的校验规则互为表里:校验器要求group.in ≤ 组内最早词的 startgroup.out ≥ 组内最晚词的 end——一旦in晚于某个词的 start,该词会被静默推迟到容器挂载后才显示(仓库注释记录过由此产生的 800ms 滞后 bug);一旦out早于某个词的 end,词会被硬生生截断。

需要强调的是:组窗口只做"包住词"的保证,不做"收紧"fill-timings.cjs的源码注释明确说明:作者故意设置的更晚out(高潮滞留、句子累积)与更早in(预进场)会被原样保留,仅当它们会裁剪词时间时才做夹取(clamp)。这保证了编排水准不被机械规则抹平。

样式与语气:每个组都要有排版身份

分组确定后,按references/typography-presets.md为每个组挑选styletone,并从左到右逐组推进。五种命名样式对应模板中的cap-*CSS 类:

styleCSS 规格适用场景
intro66px 斜体 500首行、填充语("You know…"、"So…")、沉思式开场,视觉重量低
phrase78px 正体 600主要陈述小句,大多数行的默认值
emph92px 正体 800情绪峰值或关键成就("I've achieved incredible things")
dream82px 斜体 700愿景类、回忆类("was dreaming of…"),斜体暗示记忆与想象
crown140px 正体 900 大写仅限高潮行,用在居中crown-plane,全片至多一个——通常就是最后一行

tone与 style 相互独立,二选一:

  • soft——柔和淡入 + 8px 垂直漂移,power2.out缓动,飘浮、怀旧,用于记忆、开场、dream;
  • present——干脆的 6px 位移 + 1.04 倍缩放弹入,power3.out,以中心为变换原点,果断、当下感,用于强调与 crown。

选样式的推进顺序:

  1. 第一组:默认intro+soft
  2. 观察强调信号(转写中全大写罕见;更常见的是语义信号——最高级形容词、专有名词);
  3. 独白从铺垫转为陈述时,把语气升为present
  4. crown最多保留一个组,通常是最后一行。

字号还随字幕平面的列宽缩放:typography-presets.md给出的默认值是针对约 560px 列宽(champion 原始构图)调校的,平面更宽时字号要等比放大(如 600–760px 中宽时 phrase 提到 108px、crown 提到 220px);若平面rotateY超过约 8°,可见宽度收缩,字号应上调约 10% 补偿。

编辑手术:你不是在誊写法庭记录

字幕组并不要求逐字全配。为了视觉节奏,允许合理的编辑取舍:

  • 删填充词——多余的 "you know"、"um"、"I mean" 若拖垮视觉节奏可以删;
  • 压缩——把 6 词长句通过删功能词压成 4 词,只要语义与时间仍然真实;
  • 整体跳过——明显的静音或非语音段(笑声、纯音乐间奏)可以不配。

总原则一句话:你写的是支撑语音的排版,不是逐字法庭记录——保住含义,裁掉噪音。这与composition-craft.md的"逐词角色标注"一脉相承:纯语音感叹词(um/uh/er)、完全重复的结巴("the the the" 只留一个)、自我纠正回退("I think— I mean actually…" 只留 "I mean actually")才被允许从转写中移除;内容词与结构词(冠词、连词、介词、代词)一律保留。

完整示例:champion 分组的"黄金样本"

原始转写(约 15 秒):

"You know, for me I've had this kind of upbringing, had the great foundation and, you know, I've achieved incredible things. I was dreaming of becoming number one in the world and becoming a Wimbledon champion"

经过编辑取舍后的分组(plan.jsongroups[]片段):

[ { "id": "cg-0", "style": "intro", "tone": "soft", "words": ["You", "know", "for", "me"], "in": 0.1, "out": 1.45 }, { "id": "cg-1", "style": "phrase", "tone": "soft", "words": ["I've", "had", "this", "kind", "of", "upbringing"], "in": 1.4, "out": 3.35 }, { "id": "cg-2", "style": "phrase", "tone": "soft", "words": ["the", "great", "foundation"], "in": 3.5, "out": 5.35 }, { "id": "cg-3", "style": "emph", "tone": "present", "words": ["I've", "achieved", "incredible", "things"], "in": 6.05, "out": 8.3 }, { "id": "cg-4", "style": "dream", "tone": "present", "words": ["dreaming", "of", "becoming", "number", "one"], "in": 8.5, "out": 10.4 } ]

加上 crown(独立于 groups[] 之外的crown_group字段):

{ "id": "cg-crown", "style": "crown", "words": ["Wimbledon", "Champion"], "in": 10.8, "out": 12.08 }

注意这里的三处编辑手术痕迹:cg-2 删掉了 "had"("had the great foundation" → "the great foundation"),cg-4 删掉了 "I was",crown 删掉了 "a"——全部是为了视觉节奏。同时注意时间窗口的衔接:cg-0 的out是 1.45,cg-1 的in是 1.4(先出后进的相邻交接,或靠空间分离共存);cg-3 选择了emph+present因为 "incredible" 是最高级;cg-4 用dream+present因为 "dreaming of" 是愿景表达;crown 独占最终高潮。

词级时间戳:原样透传,绝不重排

分组只负责决定哪些词组成一组、以及组窗口 in/out,组内每个词的start/end必须原样透传转写中的时间戳——不要对词重新计时。组内的逐词卡拉 OK 揭示动画用的是原始w.start

这一点在scripts/fill-timings.cjs中有完整的工程化支撑:该脚本按转写顺序(sequence)而非文本匹配plan.json填充词级时间。它维护一个转写游标,在 40 词前瞻窗口内按位置匹配每组词——这正是为了规避文本匹配在重复词(如第二个 "and"、"actions")上配错时间戳的经典 bug;匹配不到的词保留原时间并报警告,让拼写错误优雅降级而不是静默错位。

最后,scripts/check-timing.cjs --strict会在渲染前把守时间真相:

  • 词时间与转写偏差 ≤ 80msDRIFT_TOL = 0.08)——一个偏差 500ms 的字幕会毁掉场景幻觉;
  • 组窗口包住词——group.in晚于最早词 start、group.out早于最晚词 end 都会报错;
  • 时间 + 屏幕区域双重重叠检测——同时在时间与垂直带上重叠的组会报冲突,需空间分离、交接(前组out ≤后组in)或显式allow_overlap: true
  • 创意替换(如 "15%" 替代 "fifteen percent")通过CREATIVE_SUBS注册表放行,替换词按对应转写词位对齐。

这套"分组由人(或 Agent)决定、时间由脚本按位置填充、校验器在渲染前把关"的流水线,正是caption-grouping.md与整个embedded-captions技能"一切确定性步骤都计算/编译、绝不手写"原则的缩影——你只需要做好一件事:判断哪些词构成一个视觉短语,剩下的事交给脚本和校验器。

更多上下文

分组只是embedded-captions技能链条上的一环。若想继续深入,可查阅仓库内的配套文档与实现:

  • skills/embedded-captions/SKILL.md——五步流水线总览(prepare → 编排 JSON → preview → render),rail + embed 双轨字幕模型,以及"嵌入是稀缺的、值得争取的高潮"这一核心设计哲学;
  • skills/embedded-captions/references/typography-presets.md——五种样式 × 两种语气的完整对照表、字号随列宽的缩放矩阵、以及"每个渲染最多一个 crown"等禁忌清单;
  • skills/embedded-captions/references/composition-craft.md——embed 轨道的深水区:短语分组、平面与净区锚定、高潮弹出、遮挡三步判断法;
  • skills/embedded-captions/scripts/transcribe.cjs、skills/embedded-captions/scripts/fill-timings.cjs、skills/embedded-captions/scripts/check-timing.cjs——词级转写、按序填时间、80ms 严格校验的完整实现;
  • skills/embedded-captions/references/layout-heuristics.md——分组之后该把组放在画面哪里:净区选择、视线方向、crown 居中的三个条件。

其中每个文件都建议按需精读:分组规则回答"切到哪",排版预设置回答"长什么样",布局启发式回答"放哪里",时间脚本回答"何时出现"——四者合起来,才是一条真正嵌入场景、而不是浮在画面上的字幕。

【免费下载链接】hyperframesWrite HTML. Render video. Built for agents.项目地址: https://gitcode.com/GitHub_Trending/hy/hyperframes

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 3:47:07

Agent死循环本质与三层防御工程实践

1. 项目概述:Agent死循环不是Bug,是系统在“认真思考”却找不到出口“2026AI面试题-Agent 死循环如何解决”这个标题一出来,我就知道今年校招和社招的技术面试又要有新风向了。不是考你能不能调通一个LangChain链,而是考你能不能一…

作者头像 李华
网站建设 2026/9/10 3:45:59

Godot 4 + MCP协议:打造AI原生的游戏开发工作流

做 AI 原生游戏开发这事,我一开始是持怀疑态度的。游戏开发跟写 CRUD 网页不一样,它涉及场景树、信号、资源管线、物理系统,一堆跨模块的复杂状态,让 AI Agent 去理解这些,听起来就像是让一个只会背菜谱的人去当主厨。…

作者头像 李华
网站建设 2026/9/10 3:44:49

AI辅助FPGA开发实战:从Vivado到卡尔曼滤波与信号发生器

当“豆包”接管vivado进行 FPGA 开发我用了快十年的 Xilinx Vivado,从 ISE 14.7 一路升级到 Vivado 2023.2。以前遇到编译报错,第一反应是翻开 Xilinx 文档库翻半天,再去论坛上搜有没有人遇到过同样的问题;现在第一反应变了——复…

作者头像 李华
网站建设 2026/9/10 3:40:27

PowerShell脚本实战:从零搭建C盘自动清理方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华