近来得空研究了下视频翻译,即将某种语言的视频处理后,显示另一种语言的字幕并使用该语言进行配音。最终实现了这种效果:
可将一种语言的视频翻译为另一种语言和配音的视频。 语音识别基于
openai-whisper
开源模型 文字翻译使用Microsoft Edge TTS
背景音消除使用Spleeter
无需购买任何商业接口,也无需花费一分钱
原本只打算语音识别转为文字后,生成字幕就完结了,因为实在没有找到满足“自然音色、准确度好、容易安装”这些条件的好的语音合成方案,比如facebook的 seamless_communication mozilla TTS , 又从 huggingface.co/ 找了不少模型,结果都不理想,貌似除了针对性训练,其他效果都很差,不具备可用性。
百度语音、讯飞等国内api只能提供中英语言,而无法提供其他语言比如日语、韩语等。 Azuer
googleCloud
的效果还不错,可惜不面向国内服务,购买或免费试用都过于困难。
这几天突然想到,edge浏览器自带语音朗读功能,一般 win10
win11
上自带安装edge,直接调用 edge tts
不就得了呗。 说干就干,github上搜索了些 edge-tts相关项目,参考了下,将之前未完成的语音配音继续完成。
整体思路如下:
技术栈:python3.10 + ffmpeg + openai-whisper + Spleeter + pydub + googleApi
从视频中提取出音频,然后将音频切割分段,对音频通过
openai-whisper
语音识别得到文字,将文字调用google api 翻译为想要的语言文字,整理为srt字幕文件。再将翻译后的语言文字,通过
Edge TTS
语音合成接口,生成相应语音片段,最后将语音片段、字幕和删掉音频轨道的视频合并,生成翻译后的视频
实现步骤
1. 分离视频和音频
原始 mp4 视频使用 ffmpeg
提取出音频,ffmpeg
命令如下
ffmpeg -i 1.mp4 -acodec pcm_s16le -f s16le -ac 1 -ar 16000 -f wav audio.wav
分离后的音频可以去除背景音,以便识别更准确,采用 Spleeter
库,适合背景是音乐的音频
from spleeter.separator import Separator
separator = Separator('spleeter:2stems', multiprocess=False)
separator.separate_to_file(a_name, destination=dirname, filename_format="{filename}{instrument}.{codec}")
这是一个强大的提取背景音乐的库,详细使用方法见 github.com/deezer/sple…
2. pydub 切分音频
将音频文件按照静音片段分隔为各个片段,以方便识别, 分割使用pydub
库进行,安装命令pip install pydub
# split audio by silence
def shorten_voice(normalized_sound):
normalized_sound=match_target_amplitude(normalized_sound,-20.0)
max_interval=10000
buffer=500
nonsilent_data = []
audio_chunks = detect_nonsilent(normalized_sound, min_silence_len=int(config.video['voice_silence']),silence_thresh=-20-25)
#print(audio_chunks)
for i, chunk in enumerate(audio_chunks):
start_time, end_time = chunk
n=0
while end_time - start_time >= max_interval:
n+=1
# new_end = start_time + max_interval+buffer
new_end = start_time + max_interval+buffer
new_start = start_time
nonsilent_data.append((new_start, new_end, True))
start_time += max_interval
nonsilent_data.append((start_time, end_time, False))
return nonsilent_data
3. 识别语音为文字
调用 openai-whisper
的语音识别库进行,安装命令 pip install SpeechRecognition
r=speech_recognition.Recognizer()
text = r.recognize_whisper(audio_listened, language="en")
4. 翻译识别结果为目标语言文字
根据识别到的文本,再调用 google翻译api 翻译为所需语言的文字,采用requests直接抓取google翻译页面,然后提取结果的白嫖方案
# google 翻译
def googletrans(text, src, dest):
url = f"https://translate.google.com/m?sl={urllib.parse.quote(src)}&tl={urllib.parse.quote(dest)}&hl={urllib.parse.quote(dest)}&q={urllib.parse.quote(text)}"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
proxies = None
if "http_proxy" in os.environ:
proxies = {
'http': os.environ['http_proxy'],
'https': os.environ['https_proxy']
}
try:
response = requests.get(url, proxies=proxies, headers=headers, timeout=40)
if response.status_code != 200:
return f"error translation code={response.status_code}"
re_result = re.findall(
r'(?s)class="(?:t0|result-container)">(.*?)<', response.text)
except:
return "[error google api] Please check the connectivity of the proxy or consider changing the IP address."
return "error on translation" if len(re_result) < 1 else re_result[0]
5. 创建字幕文件
安装 srt 库 pip install srt
合并获取到的文字为srt格式字幕
subs=[]
sub = srt.Subtitle(index=index, start=start, end=end, content=text)
subs.append(sub)
final_srt = srt.compose(subs)
with open(sub_name, 'w', encoding="utf-8") as f:
f.write(final_srt)
6. 将字幕嵌入视频
使用 ffmpeg 可以很容易实现
ffmpeg -y -i {source_mp4} -i {sub_name} -c copy -c:s mov_text -metadata:s:s:0 language={subtitle_language} {video_subtitle.mp4}
7. 语音合成创建配音音频
安装 pip install edge-tts
,将翻译后的每段文本依次交给edge_tts
合成,合成后临时创建mp3音频,将该音频使用pydub转为合适的数据格式,存在segments
列表中,同时记录下每段文本位于原音频中的开始时间 start_time
放入 start_times
列表中,关键代码如下
segments=[]
communicate = edge_tts.Communicate(result, "配音角色名", rate="加减语速")
tmpname = f"./tmp/{start_time}-{index}.mp3"
asyncio.run(communicate.save(tmpname))
audio_data = AudioSegment.from_file(tmpname, format="mp3")
segments.append(audio_data)
start_times.append(start_time)
最后将合成后的所有语音片段连接
# 拼接配音片段
def merge_audio_segments(segments, start_times, total_duration, mp4name):
# 创建一个空白的音频段作为初始片段
merged_audio = AudioSegment.empty()
# 检查是否需要在第一个片段之前添加静音
if start_times[0] != 0:
silence_duration = start_times[0]
silence = AudioSegment.silent(duration=silence_duration)
merged_audio += silence
# 逐个连接音频片段
for i in range(len(segments)):
segment = segments[i]
start_time = start_times[i]
# 检查前一个片段的结束时间与当前片段的开始时间之间是否有间隔
if i > 0:
previous_end_time = start_times[i - 1] + len(segments[i - 1])
silence_duration = start_time - previous_end_time
# 可能存在字幕 语音对应问题
if silence_duration > 0:
silence = AudioSegment.silent(duration=silence_duration)
merged_audio += silence
# 连接当前片段
merged_audio += segment
# 检查总时长是否大于指定的时长,并丢弃多余的部分
if len(merged_audio) > total_duration:
merged_audio = merged_audio[:total_duration]
merged_audio.export(f"./tmp/{mp4name}.wav", format="wav")
return merged_audio
8. 配音音频加入视频
同样执行 ffmpeg 命令实现
ffmpeg -y -i {video_subtitle.mp4} -i hecheng.wav -c copy -map 0:v:0 -map 1:a:0 {target_mp4}
至此完成。
遇到不少坑
-
ffmepg 直接放在工程目录下,然后通过修改
os.environ['path']
的方式直接定位 -
声画不对齐的问题。
同样一句话,使用中文和使用英文大概率所需时间是不同的,这就导致原本5s的声音片段转为其他语言后变成了10s或者相反。 因此加入了语速调整,根据需要可降低或增加语速。
同时添加了自动调整语速,如果翻译后的语音时长大于翻译前的,则对翻译后的音频强制加速播放,实现时长对齐。
-
按照静音片段分割音频时,可能切割出时长非常长的音频,比如100s、200s等,尤其是有背景音乐或背景噪音的视频,没有足够的静音片段,因此针对此情况,强制对大于10s的片段再次分割,同时防止一句话突然断掉,加入
buffer
时长来后延,比如buffer=1000
,当在第1分25秒切割时,实际延后1s到第1分26s切割,以尽量增强连贯性。不过这种方案会出现重复文字,原因很显然,前一个片段的最后1s和下一个片段的前1s是同样内容。
使用 PyQt5 包装个GUI界面
-
之前版本使用py自带的标准GUI库
tkinter
简单包装了下,为布局方便,又使用了 tkinter 的包装库PySimpleGUI
,但界面过于丑陋,而且打包不便,不同平台常常出现缺失Tcl
问题,因此重构采用了PyQt5
-
使用 pyinstaller 打包exe
pyinstall -w sp.py
GUI界面
CLI 模式
新增了cli模式,先部署源码工程后,执行 python cli.py
,可在命令行下执行
支持的参数
--source_mp4: 【必填】待翻译视频路径,以.mp4结尾 --target_dir: 翻译后视频存放位置,默认存放源视频目录下的 _video_out 文件夹
--source_language:视频语言代码,默认en
( zh-cn | zh-tw | en | fr | de | ja | ko | ru | es | th | it | pt | vi | ar )
--target_language:目标语言代码,默认zh-cn
( zh-cn | zh-tw | en | fr | de | ja | ko | ru | es | th | it | pt | vi | ar )
--proxy:填写 http 代理地址,默认 None,如果所在地区无法访问google,需要填写,例如: http://127.0.0.1:10809
--voice_role:根据所选目标语言代码,填写对应的角色名,注意角色名的前2个字母需要和目标语言代码的前2个字母一致,如果不知道该怎么填写,执行python cli.py show_vioce
将显示每种语言对应可用的角色名称
--voice_rate:负数降低配音语速,正数加快配音语速,默认10
,即加快
--remove_background:是否移除背景音,如果传入该参数即代表去除背景音
--voice_silence: 输入100-2000之间的数字,表示静音段的最小毫秒,默认为300。
--voice_autorate: 如果翻译后的音频时长超过原时长,是否强制加速播放翻译后的音频,以便对齐时长?
--whisper_model: 默认为base,可选 base / small / medium / large,效果越来好,速度越来越慢。
cli示例
python cli.py --source_mp4 "D:/video/ex.mp4" --source_language en --target_language zh-cn --proxy "http://127.0.0.1:10809" --voice_role zh-CN-XiaoxiaoNeural
上述意思是,将源语言位英文的 D:/video/ex.mp4 视频,翻译为中文视频,设置代理 http://127.0.0.1:10809 使用配音橘色为 zh-CN-XiaoxiaoNeural
python cli.py --source_mp4 "D:/video/ex.mp4" --source_language zh-cn --target_language en --proxy "http://127.0.0.1"1080 9" --voice_role en-US-AriaNeural --voice_autorate --whisper_model small
上述意思是,将源语言为中文的 D:/video/ex.mp4 视频,翻译为英文视频,设置代理 http://127.0.0.1:10809 使用配音角色为 en-US-AriaNeural,如果翻译后的语音时长大于原语音,则自动加速,文字识别模型采用 small 模型