Communitygithub.com

rocketmandrey/对话视频字幕

为对话类短视频(Reels、Shorts、TikTok、1:1、16:9)烧录字幕,基于逐词时间码:每个口语词固定底部基线居中显示(无抬升、无横向偏移;与面部/手部/火焰重叠时记录为接受,可选碰撞抬升记录原因和像素),说话人通过颜色强调或微标记区分,无歌词弹跳效果,逐帧检查WCAG对比度。输出标准字幕清单(JSON)、烘焙预览、纯文本SRT、带样式的ASS和WebVTT,以及测量渲染像素与配置文件的验证器。当被要求“为对话视频添加字幕”、“субтитры для диалога / скетча / рилса”、“像Shorts那样做字幕”、“烧录字幕但不遮挡面部”、“带当前词的卡拉OK字幕”、“按安全区检查字幕”,或从词时间戳(ElevenLabs Scribe、Groq Whisper)导出SRT/ASS/VTT时使用。

对话视频字幕 是什么?

这是一个为短视频(如Reels、Shorts、TikTok)设计的专业字幕生成技能。它接收逐词时间码和禁止覆盖框(如面部、手部、火焰区域),生成符合WCAG对比度标准、不弹跳、固定底部基线的字幕。支持多种输出格式(SRT、ASS、WebVTT、烘焙视频),并提供验证器确保像素级精度。默认使用固定底部居中模式(不会自动抬升),仅在明确要求时才启用碰撞抬升。通过颜色强调区分说话人,并支持卡拉OK、揭示、卡片、动态和混合五种配置文件。该技能适用于任何AI编码助手(如Claude Code、Cursor),通过命令行或Python模块调用,并可选集成ElevenLabs或Groq的云端语音转文字服务。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add rocketmandrey/conversational-video-subtitles-skill

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

对话视频字幕 是做什么的?

概述

字幕是一个独立图层:它承载每个口语词,绝不依赖于画面中已经绘制的内容。画面中事物的标签(部件名称、利弊、箭头)是不同的图层,使用另一种视觉语言(对象旁边的标注);永远不要因为“画面已经显示了”就从字幕中删掉一个词。

流程:words.json + forbid.json(字幕不得覆盖的框) + config.json + 配置文件 → plan → manifest.json(规范数据) → burn(烘焙预览 = 视觉真实) / export srt|ass|vtt → validate。

格式角色(不要混淆):

格式承载内容不承载内容
manifest.json词语、时间、布局、placement、接受的覆盖、带原因的抬升、渲染检查:数据的真实来源像素
.srt文本 + 时间粗细、颜色、轮廓、当前词、位置。永远不要在SRT中承诺样式
.ass字体系列 + 粗体/\b700、转换为libass度量的大小、轮廓、背景框、每个时间片的活动词、位置淡入淡出;需要字体:subtitles=x.ass:fontsdir=fonts
.vtt::cue CSS(字体系列、粗细、通过text-shadow的轮廓、背景)+ 每个时间片的类 .active_<spk> / .em_<spk> / .dim / .u精确位置;页面必须使用@font-face加载字体
烘焙 preview.mp4一切:唯一的视觉真实来源—

何时使用

  • 对话短剧、访谈、有旁白的讲解:竖屏或方屏,最多两行。
  • 你有词时间码(或音频:通过环境密钥可选云端STT),并且知道面部/手部/关键道具的位置(框;对于3D场景,渲染纯色遮罩并取框)。
  • 不适用于歌词视频(故意不支持弹跳、缩放文本)。

算法

分段(配置文件中的chunk):在停顿 > pause_s(0.35秒)、句子结束、说话人变化时强制断开;在逗号处软断开,前提是当前块已有2+个词;受max_words、像素宽度(安全区的max_width_frac)和max_dur_s(约1.2秒的口语阅读效果较好)限制。块不会以功能词(не、в、а、and、the……)结尾:它们会移到下一个块。单个词如果空间允许,则加入同一句子的相邻块。词持续时间限制为中位数的2.5倍,以免词悬在停顿中。时间:一个字幕单元在其第一个词开始前≤50毫秒出现,绝不切断前一个词,在其最后一个词结束后停留hold_s,至少min_show_s。

放置——placement: {mode: fixed_bottom_center, collision_lift: false}(对话默认)。

  1. 整个视频使用一条基线:baseline = safe.bottom - baseline_margin_px(或显式设置)。每个单元在该基线上底部对齐(y + h = baseline)并在画面上居中(x + w/2 = W/2)。无任何移动:无抬升、无横向偏移、无按说话人的x偏移。行宽受限,确保居中行保持在安全区内。
  2. 在基线上穿过禁止框的行保持原样:该单元获得overlap_accepted: [classes],QA表会画一个黄色框“重叠已接受(固定基线)”。此为信息,非失败。穿过平台UI仍然是错误(缩小字体或拆分词组)。
  3. 可选collision_lift: true(模式baseline_lift):x位于说话人下方(align: speaker)或居中,左右回退;如果每个x都碰撞,则按整个lift_step_px步长抬升直到空闲。该单元获得lift = {px, reason: [基线处命中的类], t0, t1},清单中列出所有抬升在collision_lifts。无原因则无抬升。仅在需求明确要求不惜一切代价保持面部清晰时使用。
  4. 绝不删除词。

外观。 轮廓(outline_px)+ 柔和阴影 + 浅色背景,其透明度仅根据需要为每个单元提高,以保持最暗的亮色与背后第90百分位像素的对比度≥4.5:1(WCAG)。说话人通过活动词颜色区分,在混合/卡片模式下还有一条4像素的说话人色条;无气泡对话,无厚重名牌。平稳运动:仅透明度淡入淡出,无缩放弹出,无垂直滑动。

质量保证(scripts/validate.py,也可通过demo运行):

  • schema:清单对照templates/manifest.schema.json;
  • 行数 ≤ 2;
  • 放置:在fixed_bottom_center模式下,所有单元的y基线分布和x中心分布为0(容忍0.5像素),且无抬升;在baseline_lift模式下,每个抬升都在网格上,有原因,且该单元确实在基线处碰撞;
  • 每一帧与平台UI的覆盖为零;禁止框:baseline_lift模式下为0,fixed_bottom_center模式下报告为overlap_accepted(信息);
  • 每个词精确出现一次,按顺序,在说话期间可见;
  • 对比度 ≥ 4.5且无弹跳,两者均由burn逐帧测量;
  • 字体可解析:文件存在,Pillow系列和样式,OS/2粗细,fc-scan,且libass的fontselect选中声明的文件和粗细;
  • 像素:在烘焙帧上以及ASS的libass渲染中,活动颜色存在,字形底部在布局3像素内,主干宽度在声明粗细参考栅格的1像素内,轮廓在outline_px的1像素内。

配置文件(profiles/*.json)

配置文件特点最适合
karaoke每次显示2–4个词,当前词为说话人颜色,其余变暗最稳定;固定轨道,讲解
reveal词随说话淡入;布局预先固定;≤ 2行平缓旁白
cards每个卡片一个想法(≤ 8词,2行),密集面板,关键词着色慢速、引文;比说话提前阅读
kinetic一次一个标记,更大字号仅限点睛句;上下文少
hybrid(默认)≤ 3词,词淡入,当前词下划线,强调颜色,说话人色条对话

研发系统中的选项作为参数:节奏 = chunk.max_dur_s: 1.2 + timing.min_show_s: 0.83(混合模式下开启);强调 = emphasis: "energy"(每个说话人从--audio得到的词响度z分数),或带有keywords列表的"keyword"。

命令

python -m subkit demo examples/fixture                     # 5种配置 + hybrid_lift -> 预览、srt/ass/vtt、validation_report.{md,json}
python -m subkit plan --words W --forbid F --config C [--profile hybrid] [--audio A] -o manifest.json
python -m subkit burn manifest.json --forbid F --video in.mp4 [--audio A] -o preview.mp4 --qa qa.jpg
python -m subkit export manifest.json --format ass -o captions.ass     # srt | ass | vtt
python scripts/check_clean.py                                          # 提交前:无媒体/密钥/绝对路径
ELEVENLABS_API_KEY=... python -m subkit stt audio.wav -o words.json     # 可选;或 --provider groq 配合 GROQ_API_KEY

陷阱

  • libass ≠ Pillow字体大小。 libass按OS/2 winAscent+winDescent大小调整,并将基线置于行顶下方winAscent处;导出时进行转换,否则ASS文本会小约35%、更细且更低。
  • ASS中的可变字体会回退到默认实例(通常是Regular)。请提供你使用的粗细的静态实例(此处为Onest-Bold.ttf,700)并传递fontsdir。
  • ffmpeg -ss 在 -i 之前会重置时间戳:字幕滤镜随后渲染t=0。烧录ASS时应在-i之后seek。
  • \k/\kf卡拉OK单位是厘秒,且会永久着色每个口语词;对于“仅当前词”,应发射时间片(如导出所做)。
  • SRT无样式:如果交付物需要粗细或高亮,请提供ASS/VTT或烘焙视频。
  • 浮动行(按说话人的x、滞后、“看起来更美”的抬升)在对话中会被视为抖动。保持默认固定模式;在baseline_lift模式下,验证器会重新检查每个抬升是否确实碰撞。
  • 对比度以亮色与背后最亮10%像素(而非平均值)判断:火焰、雪地和高亮区域会使平均值失效。
  • 不要删除已在画面上绘制的词。将绘制的标签移到注释图层。
  • templates/safe_zones.json中的安全区是近似值;平台UI会变化,发布前请重新检查。

清单

  • words.json覆盖整个语音(云端STT或强制对齐器);说话人已标记
  • forbid.json:每个镜头的面部、手部、火焰/道具(+ align: speaker用的speakers框)
  • config:尺寸、fps、safe_zone、说话人颜色(不同色调,两者在背景上均须≥4.5:1)
  • plan → manifest;阅读overlap_accepted(固定模式)或collision_lifts(抬升模式,每个有真实原因)
  • burn → 查看QA表(青色 = 基线,黄色 = 重叠已接受,橙色 = 有原因的抬升)以及真实帧
  • validate 全部通过;交付烘焙视频(+ 如果播放器能渲染则加ASS/VTT;仅纯文本则用SRT)

相关技能