Article

视频里的字幕,我让 AI 自己读出来了

trial

做播客内容,有一类素材让我特别头疼。

不是没有字幕——是有,但读不出来。


硬字幕是什么问题

视频字幕有两种。

一种是「软字幕」:字幕单独存在一个轨道里,可以开关,可以导出,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 的完整过程。