前言
我之前一直使用windows笔记本的subtitleedit 来识别字幕文件,可以调用显卡使用whisper识别,速度和识别准确度都还可以接受,最近使用mac办公,简单记录下mac下使用whisper识别字幕
link:https://github.com/SubtitleEdit/subtitleedit
mac下字幕识别方案
Buzz
Buzz是一款基于OpenAI Whisper的开源语音转文字工具,它支持Windows、macOS、Linux等多平台,用户可以将麦克风的语音实时转换为文字,也可以将视频、音频文件转换为文字或字幕。
link:https://github.com/chidiwilliams/buzz
主要支持下面几种引擎,可以讲是比较完美的解决方案
- Whisper
- Whisper.cpp
- Hugging Face
- Faster Whisper
- OpenAI Whisper API
whisper或者mlx_whisper
使用whisper比较成熟,根据文档似乎使用--device mps
可以实现调用GPU,但是根据我实测有很多报错,这个问题在此PR中进行了讨论 https://github.com/openai/whisper/pull/382
另一种方案是使用mlx_whisper,mlx是MLX 是由 Apple 机器学习研究团队推出的 Apple 芯片上机器学习的阵列框架
link: https://github.com/ml-explore/mlx
使用mlx版本的whisper在使用相同大小模型的情况下,实测字幕识别速度甚至优于我的4070笔记本。
mlx-whisper也提供了模型转化脚本可以将whisper的预训练模型转为whisper风格的模型,也可以直接使用hugging face上转化好的可用模型,下面模型全部可用,选择合适自己的模型使用即可
link:https://huggingface.co/collections/mlx-community/whisper-663256f9964fbb1177db93dc
import whisper
import mlx_whisper
import os
from typing import List
import tqdm
def find_mp4_files(base_dir, depth=3):
"""
遍历给定目录,搜索最多 depth 层级的所有 .mp4 文件。
"""
mp4_files = []
for root, dirs, files in os.walk(base_dir):
# 限制遍历深度
current_depth = root[len(base_dir):].count(os.sep)
if current_depth >= depth:
dirs.clear() # 停止进一步深入
# 查找所有 .mp4 文件
for file in files:
if file.lower().endswith(".mp4"):
mp4_files.append(os.path.join(root, file))
return mp4_files
def generate_subtitles(mp4_files: List[str], model_name, task="transcribe", language="en", apple_silicon=False):
"""
使用 Whisper 模型为每个 MP4 文件生成字幕,并保存到指定目录。
"""
for mp4_file in tqdm.tqdm(mp4_files):
# 提取文件名(不含扩展名)
# filename = os.path.splitext(os.path.basename(mp4_file))[0]
# output_file = os.path.join(output_dir, f"{filename}.srt")
output_file = mp4_file.replace(".mp4", ".srt")
print(f"Transcribing: {mp4_file}")
if apple_silicon:
result = mlx_whisper.transcribe(mp4_file, path_or_hf_repo=model_name, task=task, language=language)
else:
model = whisper.load_model(model_name)
result = model.transcribe(mp4_file, task=task, language=language)
segments = result.get('segments', [])
# 保存为 RST 文件,标准字幕格式
with open(output_file, "w", encoding="utf-8") as rst_file:
for idx, segment in enumerate(segments, start=1):
start_time = format_timestamp(segment['start'])
end_time = format_timestamp(segment['end'])
text = segment['text']
rst_file.write(f"{idx}\n")
rst_file.write(f"{start_time} --> {end_time}\n")
rst_file.write(f"{text.strip()}\n\n")
print(f"Subtitle saved: {output_file}")
def format_timestamp(seconds):
"""将时间转换为 hh:mm:ss,SSS 格式"""
hours = int(seconds // 3600)
minutes = int((seconds % 3600) // 60)
secs = int(seconds % 60)
millis = int((seconds - int(seconds)) * 1000)
return f"{hours:02}:{minutes:02}:{secs:02},{millis:03}"
# 示例调用
if __name__ == "__main__":
# 用户输入目录
config = {
# "model_name": "mlx-community/whisper-medium-mlx-8bit", # apple m系列芯片使用带mlx前缀的模型
"model_name": "medium.en",
"task": "transcribe",
"language": "en",
"apple_silicon": False # 苹果M系列芯片设置为 True
}
input_dir = "" # 输入的 MP4 文件路径
# 查找所有 MP4 文件
print("Searching for MP4 files...")
mp4_files = find_mp4_files(input_dir)
print(f"Found {len(mp4_files)} MP4 file(s).")
# 生成字幕文件
# 默认输出在mp4 同级目录
generate_subtitles(mp4_files,
model_name=config["model_name"],
task=config["task"],
language=config["language"],
apple_silicon=config["apple_silicon"])
print("All subtitles have been generated.")