Article
视频里的字幕,我让 AI 自己读出来了
做播客内容,有一类素材让我特别头疼。
不是没有字幕——是有,但读不出来。
硬字幕是什么问题
视频字幕有两种。
一种是「软字幕」:字幕单独存在一个轨道里,可以开关,可以导出,ffmpeg 一行命令就能提取成 SRT 文件。
另一种是「硬字幕」:字幕直接烧录进画面,和视频融为一体。你看到的字幕,是画面的一部分,不是独立的数据。
我做内容研究的时候,大量素材来自短视频平台。这些视频的字幕,绝大多数是硬字幕——平台在处理视频的时候,已经把字幕压进去了。
想提取字幕?只能靠眼睛看,手动打。
一条 60 秒的视频,快的话打 5 分钟,慢的话 15 分钟。一期播客要研究十几条视频,加起来就是两三个小时的纯体力劳动。
我做了一段时间,实在受不了了。
思路:用 OCR 逐帧识别
解决硬字幕的思路很清楚:
视频本质上是一帧一帧的图片。字幕烧录在画面上,那就把每一帧图片拿出来,用 OCR 识别图片里的文字,再把相邻的相同文字合并成一条字幕,最后输出时间轴。
这套流程走通了,就能自动把硬字幕变成 SRT 文件。
问题是:用什么 OCR?
我本地已经有一个模型——GLM-OCR,智谱开源的文档识别模型,0.9B 参数,很小,跑在本机端口 8080,用 Apple Silicon 的 MLX 框架推理。
这个模型平时主要用来识别截图里的文字、扫描文档之类的事。
字幕识别,理论上也能干。
怎么做的
工具做了三个阶段:
第一阶段:探测字幕位置
不同视频的字幕位置不一样,有的在底部,有的在中间。
我用视频前5秒的画面做探测——把画面切成10个水平条带(每条15%高度,滑动扫描),每个条带单独送去 OCR,看哪个条带识别出的文字最多,那就是字幕所在的区域。
加上一点边距,把字幕区域锁定。
第二阶段:逐帧识别
确定了字幕区域,就只裁这一块区域,不用处理整张图。
每0.5秒抽一帧,裁切字幕区域,送 GLM-OCR 识别。
第三阶段:合并输出
相邻帧如果识别出的文字一样,说明这条字幕还在屏幕上——把它们合并成一条,记录开始和结束时间。
最后写成标准的 SRT 格式。
测试下来什么效果
拿了一条 103 秒的英文访谈短视频测试。
黄色粗体字幕,位置在画面中间偏上。
探测阶段,5帧扫完,定位到字幕在垂直方向 40%-55% 的区域。
识别阶段,206 帧,全部跑完,输出 100 条字幕。
随便摘几条:
1
00:00:00,000 --> 00:00:01,000
during sex
2
00:00:01,000 --> 00:00:02,500
the average man lasts
3
00:00:02,500 --> 00:00:03,500
2.5 minutes
准确率很高,基本没有错字,时间轴也对得上。
整个过程大概花了4分钟——以前手打这条视频要15分钟。
一个小细节
GLM-OCR 有时候遇到空白帧会返回奇怪的东西——no text、或者一个空的 markdown 代码块。
需要过滤掉这些无效输出,不然 SRT 文件里会混进一堆乱码。
我加了一个清洗函数,把这类模型幻觉直接过滤,保证输出干净。
这种小问题不处理的话,工具就不稳定。稳定比「功能多」重要。
现在的工具合集
| 工具 | 做什么 |
|---|---|
transcribe_audio | 本地语音转录(SenseVoice) |
generate_xiaohongshu_card | 小红书配图生成 |
dub_narration | 声音克隆配音 + 字幕视频 |
vision_analyze | 本地视觉理解(Qwen3-VL) |
manage_services | 后台服务调度,释放内存 |
extract_subtitles | 硬字幕提取(GLM-OCR) |
六个工具,全部本地运行。
做内容的人,很多时间花在这种「搬运」工作上——把视频里的字幕打出来,把音频转成文字,把图片描述给 AI 听。
这些都是体力活,不是脑力活。
AI 应该把这些事接走,让人去做真正需要判断的部分。
这是我一直在做的事。
本系列持续更新,记录把各种工具接进 AI 助手 pi 的完整过程。