从视频中“抠”出硬字幕:白嫖智谱AI大模型开发硬字幕提取软件

983 阅读5分钟

为视频添加字幕,如今借助语音识别技术(ASR)已变得相当便捷。特别是 OpenAI 的 Whisper 系列模型,在语音转文字方面表现出色,让自动生成字幕成为可能。

然而,提取视频中已有的硬字幕(内嵌在视频画面中的字幕),仍然面临不少挑战。尽管原理看似简单:利用光学字符识别(OCR)技术识别图像中的文字即可,但实际操作却远非如此轻松。

硬字幕提取的难点:数量与精度并存

视频本质上是由连续的图像帧组成。常见的视频帧率是 30fps(每秒 30 帧),这意味着 1 小时的视频就包含 108,000 张图像,对于高清视频,帧数则会更高。如此庞大的数据量对 OCR 处理能力提出了严峻的考验。

  • 效率挑战: 即使硬件性能足够,本地运行也需要耗费大量时间,效率低下,难以满足用户需求。
  • 成本挑战: 借助云端 AI 厂商的 API 进行 OCR 处理,虽然效率有所提升,但高昂的 API 调用费用和受限的免费额度令人望而却步。例如,Google Gemini 的 1.5-flash 模型每日提供 1500 次调用额度,对于处理长视频来说,显然杯水车薪。而且,由于网络限制,还需借助 VPN 才能使用,支付方式也存在障碍。

另辟蹊径:免费且高效的解决方案

难道就没有一种成本低廉且行之有效的方案吗?答案是肯定的。智谱 AI 的 GLM-4V-FLASH 模型为我们提供了新的思路。它不仅免费(仅限制调用频率,不限制次数),而且具备强大的图像理解能力,可以作为 OCR 工具使用。虽然目前仅支持中英文识别,但对于大多数场景已经足够。

利用智谱 AI 的 GLM-4V-FLASH 模型,并结合 sentence-transformers 模型的文本相似度检测技术,尝试制作了一款硬字幕提取软件,完成效果如下

image.png

技术原理

该软件提取硬字幕的核心步骤如下:

  1. 视频切帧: 首先,使用 FFmpeg 工具将视频按 1 秒间隔切分为图像帧。选择 1 秒间隔而非逐帧提取,一方面可以大幅减少需识别的图像数量,另一方面考虑到字幕通常持续时间不会低于 1 秒,过多的帧数也会增加去重的难度。
  2. OCR 识别: 将切分后的图像帧发送给智谱 AI 的 glm-4v-flash 模型,进行 OCR 识别,提取图像中的文字。
  3. 字幕去重: 由于连续的图像帧可能包含相同的字幕内容,为了避免重复,使用 sentence-transformers 模型计算当前识别出的字幕与前一句字幕的相似度。如果相似度超过 60%,则认为两条字幕内容相同,进行去重。
  4. 生成字幕文件: 最后,将去重后的字幕文本按照对应的时间戳进行拼接,并保存为 SRT 格式的字幕文件。

攻克难题

在开发过程中,遇到了以下挑战,并一一克服:

  1. 连续字幕相同但识别结果有差异: 连续帧的文字内容通常相同,但由于画面变化,OCR 识别结果可能存在细微差异,比如多几个字或少几个字。尝试过多种简单方法均难奏效,最后通过引入一个几百MB的 sentence-transformers 模型做相似度判断,才有效识别并过滤重复字幕。
  2. 识别区域: 最初,OCR 识别会提取整个画面中的文字,而不仅仅是字幕部分。即使通过 prompt 强约束 glm-4v-flash 返回特定位置的文字,效果也不理想。最终,使用 FFmpegcrop 滤镜,强制只裁剪视频中字幕所在区域的图像,解决了这个问题。

核心代码

  1. 裁切图片, 下面命令代表裁切视频底部 1/3 区域的图片,通常硬字幕多位于该处

ffmpeg -i video_path -vf fps=1,crop=iw:ih/3:0:ih*2/3 "%06d.jpg"

image.png

如果想裁切顶部,则crop滤镜参数改为 crop=iw:ih/3:0:0

如果识别整个图片,则删掉crop,仅保留 -vf fps=1

图片名字即代表当前裁切所处秒数。

  1. 向 智谱AI发起识别请求
# pip install zhipuai

client = ZhipuAI(api_key='')  
response = client.chat.completions.create(
    model='glm-4v-flash',
    messages=[
       {
        "role": "user",
        "content": [
          {
            "type": "text",
            "text": """请将图片中的文字识别出来,如果图片中没有文字,就返回字符串 'no',不要任何解释或说明,仅仅返回这2个字母即可。 如果图片中存在文字,就直接返回识别到的文字,不要返回任何其他提示或说明,也不要解释遗漏或错误,仅将识别到的文字返回即可。"""
          },
          {
            "type": "image_url",
            "image_url": {
                "url" : 图片的base64编码数据
            }
          }
        ]
      }
    ],
    top_p= 0.7,
    temperature= 0.95,
    max_tokens=1024,
    stream=False
)
 
如果识别成功,response.choices[0].message.content 即为文字。 

  1. 句子相似度判断去重
# result_ocr 是一个按视频秒数排序的list,每个项均是  {text:识别出的文字,sec:秒数}  dict

model = SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2")
        raws=[]
        for i,it in enumerate(result_ocr):       
            if not it['text'].strip():
                continue
            if i>0:
                # 生成句子嵌入
                embeddings = model.encode([it['text'].strip(),result_ocr[i-1]['text'].strip()])

                # 计算余弦相似度
                similarity_score = cosine_similarity([embeddings[0]], [embeddings[1]])[0][0]

                # 相似度大于60%,可认为是同一条字幕
                similarity_percentage = max(0, min(100, similarity_score * 100))
                if similarity_percentage>60:
                    continue        
            raws.append(it)

  1. 其他则为pyside6界面代码和QThread线程任务代码省略

获取智谱AI的api key

免费的 glm-4v-flash ,不用白不用,用了不白用

image.png

免费注册并获取 API Key: bigmodel.cn/usercenter/…

image.png