最近在做神经网络的研究,偶然间看到OpenAI开源出了一个多国语音转文字的模型,脑海里突然想到余大嘴在华为发布会发布实时语音翻译时满屏弹幕的“???”和“!!!”,于是决定做一个多国语音转简体中文字幕的软件来玩一玩。
想法是这样的:通过OpenAI最新发布的翻译模型whisper(可以翻译200多种语言,且其中部分语言的翻译效果已然接近甚至超过人类的神器)加上自己写的一点点程序,做一个傻瓜化的多国语言转中文字幕的软件。完成操作后,只需要通过简单的点击就可实现字幕的生成,从此告别生肉,不用苦等美剧、日剧字幕组的解救,或者毅然决然去学习该国语言。
PS:后续如果有空会出一个提高篇,直接将所有东西整合在一起,打包成大部分Windows电脑可直接双击运行的软件。
废话不多说,下面是计划的实施细节。
软件环境的搭建
1. 安装conda
地址如下:https://www.anaconda.com
下载完双击安装即可,安装完在程序列表中会出现一个叫做"Anaconda Prompt (Anaconda)"的东西,就是我们刚刚装的虚拟环境。
2. 创建虚拟环境
打开刚刚安装的Anaconda,输入如下指令:
conda create -n whisper python=3.9
3. 在虚拟环境中安装最新版本的pytorch
conda activate whisper
conda install pytorch torchvision torchaudio pytorch-cuda=11.7 -c pytorch -c nvidia
4. 安装OpenAI语音转文字模型
pip install whisper
5. 安装ffmpeg(用来提取视频中的语音和转换语音的格式)
打开控制台后,输入如下命令:
choco install ffmpeg
如果还没安装choco,得先装一下。
先以管理员身份打开powershell,然后在里面输入如下命令:
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
各部分代码编写和整合
1. 使用whisper和翻译引擎将视频中的语音转成简体中文
whisper目前开源的模型只支持输出语音相对应的该国文字(也就是如果语音是日语,输出的就是日文,德语则输出德文)。如果打开翻译选项,则只能将文字翻译成英文,暂时不支持翻译成其他文字。
为此,我们只能分两步走:
先用whisper将语音转成对应的英语:whisper 1.mp4 --model large --language Japanese --task translate --device cpu
再通过谷歌或者微软翻译将其转成中文,操作如下:
谷歌翻译版本(需要梯子)
import re
import html
from urllib import parse
import requests
GOOGLE_TRANSLATE_URL = 'http://translate.google.com/m?q=%s&tl=%s&sl=%s'
def translate(text, to_language="auto", text_language="auto"):
text = parse.quote(text)
url = GOOGLE_TRANSLATE_URL % (text,to_language,text_language)
response = requests.get(url)
data = response.text
expr = r'(?s)class="(?:t0|result-container)">(.*?)<'
result = re.findall(expr, data)
if (len(result) == 0):
return ""
return html.unescape(result[0])
# 打开FC2.srt文件,并按行读取
# 打开一个文件,以供写入翻译好的字幕
with open('1-ch.srt', 'w', encoding='utf-8') as wf:
with open('1.srt', 'r', encoding='utf-8') as rf:
lines = rf.readlines()
# 遍历每一行
for line in lines:
# 如果不是空行且不是以数字开头的行,则进行翻译
if line != '\n' and not line[0].isdigit():
line = translate(line, "zh-CN", "en")# 日语->中文
line = line + '\n'
# 打印翻译结果
print(line)
# 将翻译结果写入文件
wf.write(line)
必应翻译版本
pip install translators
import translators as ts
# 打开FC2.srt文件,并按行读取
# 打开一个文件,以供写入翻译好的字幕
with open('1-ch-bing.srt', 'w', encoding='utf-8') as wf:
with open('1.srt', 'r', encoding='utf-8') as rf:
lines = rf.readlines()
# 遍历每一行
for line in lines:
# 如果不是空行且不是以数字开头的行,则进行翻译
if line != '\n' and not line[0].isdigit():
line = ts.translate_text(line,str='bing',from_language='en',to_language='zh')
line = line + '\n'
# 打印翻译结果
print(line)
# 将翻译结果写入文件
wf.write(line)
2. 使用ffmpeg将视频和生成的字幕合在一起
ffmpeg -i "1.mp4" -i "1-ch.srt" -c copy -c:s mov_text -metadata:s:s:0 language=zh 1-ch.mp4
3. 制作软件界面整合所有功能(整合为负责界面及英文转中文的两个文件)
GUI.py
import os
import tkinter as tk
from tkinter import ttk, filedialog
import subprocess
# 设置文件选择
def browse_file():
file_path = filedialog.askopenfilename()
if file_path:
path_label.config(text=file_path)
# 调用whisper进行语音转文字
def whisper():
# 判断是否选择了文件
if path_label.cget("text") == "文件路径":
# 弹出提示框
tk.messagebox.showinfo(title='提示', message='请先选择文件')
return
else:
# 调用控制台激活虚拟环境whisper
outpath=path_label.cget("text")
# 找到文件夹路径
outpath=outpath[:outpath.rfind("/")+1]
print(outpath)
# 获得选择的语言模型
model=model_dropdown_value.get()
# 合成命令行语句
cmd="start cmd /k \"conda activate whisper && whisper "+path_label.cget("text")+" --model "+ model +" --language Japanese --task translate --output_format srt --output_dir "+outpath+" && EXIT\""
print(cmd)
# 执行命令行语句,结束后自动关闭
subprocess.Popen(cmd,shell=True).wait()
# 将字幕从英文翻译成中文
# 添加程序入口
if __name__ =='__main__':
root = tk.Tk()
root.title("Whisper字幕生成器")
# 设置背景颜色
root.config(bg="#F7EFE5")
root.geometry("640x480")
################################语言模型选择################################
# 窗口的最上面空一行
ttk.Label(root).pack(pady=10)
# 创建行的模块,以将模型选择的标签和下拉列表框放在一起
model_row_frame = ttk.Frame(root)
model_row_frame.pack(pady=10)
# 创建模型标签
model_dropdown_label = ttk.Label(model_row_frame, text="选择语言模型:", font=("微软雅黑", 14))
model_dropdown_label.pack(side="left")
# 创建模型选择拉列表
model_options = ["tiny", "base", "small", "medium", "large"]
model_dropdown_value = tk.StringVar(value=model_options[0])
model_dropdown = ttk.Combobox(model_row_frame, textvariable=model_dropdown_value, justify="center", values=model_options, width=20, foreground="#FD5825", font=("微软雅黑", 14))
model_dropdown.pack(side="left", padx=10)
################################语言模型选择################################
# 创建行的模块,以将语言选择的标签和下拉列表框放在一起
language_row_frame = ttk.Frame(root)
language_row_frame.pack(pady=10)
# 创建模型标签
language_dropdown_label = ttk.Label(language_row_frame, text="选择语音语种:", font=("微软雅黑", 14))
language_dropdown_label.pack(side="left")
# 创建语言选择下拉列表
language_options = ["日语", "英语", "中文"]
language_dropdown_value = tk.StringVar(value=language_options[0])
language_dropdown = ttk.Combobox(language_row_frame, textvariable=language_dropdown_value, justify="center", values=language_options, width=20, foreground="#FD5825", font=("微软雅黑", 14))
language_dropdown.pack(side="left", padx=10)
################################语言模型选择################################
# 创建行的模块,以将语言选择的标签和下拉列表框放在一起
bing_or_google_row_frame = ttk.Frame(root)
bing_or_google_row_frame.pack(pady=10)
# 创建模型标签
bing_or_google_dropdown_label = ttk.Label(bing_or_google_row_frame, text="选择翻译引擎:", font=("微软雅黑", 14))
bing_or_google_dropdown_label.pack(side="left")
# 创建语言选择下拉列表
bing_or_google_options = ["必应", "谷歌翻译(需要梯子)"]
bing_or_google_dropdown_value = tk.StringVar(value=bing_or_google_options[0])
bing_or_google_dropdown = ttk.Combobox(bing_or_google_row_frame, textvariable=bing_or_google_dropdown_value, justify="center", values = bing_or_google_options, width=20, foreground="#FD5825", font=("微软雅黑", 14))
bing_or_google_dropdown.pack(side="left", padx=10)
################################视频文件选择################################
# 创建文件选择按钮
# 创建行的模块,以将语言选择的标签和下拉列表框放在一起
file_row_frame = ttk.Frame(root)
file_row_frame.pack(pady=10)
# 创建模型标签
filepath_label = ttk.Label(file_row_frame, text="选择视频文件:", font=("微软雅黑", 14))
filepath_label.pack(side="left")
filepath_button = tk.Button(file_row_frame, text="...", command=browse_file, width=10, bg="#3FABAF", fg="#F7EFE5", font=("微软雅黑", 14, "bold"))
filepath_button.pack(side="left", padx=10)
# 创建文件路径标签,标签居中对齐
path_label = ttk.Label(root, text="文件路径", font=("微软雅黑", 14), justify="center")
path_label.pack(pady=10)
################################Whisper语音转字幕################################
whisper_Trans_button = tk.Button(root, text="语音转字幕", command=whisper, width=20, bg="#3FABAF", fg="#F7EFE5", font=("微软雅黑", 14, "bold"))
whisper_Trans_button.pack(pady=10)
################################Whisper语音转字幕################################
merge_button = tk.Button(root, text="合并语音和字幕", width=20, bg="#3FABAF", fg="#F7EFE5", font=("微软雅黑", 14, "bold"))
merge_button.pack(pady=10)
root.mainloop()
whisperTranslator.py
from urllib import parse
import requests
import re
import html
import translators as ts
import sys
GOOGLE_TRANSLATE_URL = 'http://translate.google.com/m?q=%s&tl=%s&sl=%s'
def translate(text, to_language="auto", text_language="auto"):
text = parse.quote(text)
url = GOOGLE_TRANSLATE_URL % (text,to_language,text_language)
response = requests.get(url)
data = response.text
expr = r'(?s)class="(?:t0|result-container)">(.*?)<'
result = re.findall(expr, data)
if (len(result) == 0):
return ""
return html.unescape(result[0])
# 添加程序入口
if __name__ =='__main__':
if len(sys.argv) > 2:
file_path = sys.argv[1]
bing_or_google = sys.argv[2]
# 获取文件名称
transfile_path=file_path[:file_path.rfind(".")]+"-ch.srt"
# 打开并逐行读取文件
with open(transfile_path, 'w', encoding='utf-8') as wf:
with open(file_path, 'r', encoding='utf-8') as f:
# 按行读取文件
lines = f.readlines()
for line in lines:
# 如果非空且不是以数字开头
if line[0].isascii() and not line[0].isdigit() and line[0]!="\n":
#print(line)
if bing_or_google == "谷歌翻译(需要梯子)":
line=translate(line,"zh-CN", "en")#汉语转英语
else:
line=ts.translate_text(line,str='bing',from_language='en',to_language='zh')
line=line+"\n"
print(line)
wf.write(line)
else:
print("请提供文件路径和翻译引擎选择参数")
完成上面的操作,我们就得到了一个基础版本的视频字幕生成工具啦。
还需改进的问题
1. 语音转字幕的速度还有待提高
在我的电脑(CPU 5900X,GPU 4090)上使用几个不同的模型将一集接近24分钟的《工作细胞》(日语语音)转为英语字幕所花的时间如下表所示:
使用模型 tiny base small medium large GPU识别速度(s) 240.86 252.37 193.85 224.00 291.68 CPU
识别速度(s) 1599.76 太慢了不测了 太慢了不测了 太慢了不测了 太慢了不测了
2. 翻译中存在很多重复和幻听的现象
我尝试了用下面第二篇参考文章用的参数来做,然而感觉还是不太好。最好应该是用vad技术先把存在语音的片段筛选出来,然后再把这些片段送进去翻译。这个实现稍微麻烦一点,我们留到提高篇研究。
参考资料
whisper本体
https://github.com/openai/whisper
参考文章
https://zhuanlan.zhihu.com/p/585113669