近来得空研究了下视频翻译,即将某种语言的视频处理后,显示另一种语言的字幕并使用该语言进行配音。最终实现了这种效果:
可将一种语言的视频翻译为另一种语言和配音的视频。 语音识别基于openai-whisper
离线模型、文字翻译使用Microsoft Edge TTS
,背景音消除使用Spleeter
,无需购买任何商业接口,也无需付费
开源地址https://github.com/jianchang512/pyvideotrans
原本只打算语音识别转为文字后,生成字幕就完结了,因为实在没有找到满足“自然音色、准确度好、容易安装”这些条件的好的语音合成方案,比如facebook的 seamless_communication mozilla TTS , 又从 https://huggingface.co/ 找了不少模型,结果都不理想,貌似除了针对性训练,其他效果都很差,不具备可用性。
百度语音、讯飞等国内api只能提供中英语言,而无法提供其他语言比如日语、韩语等。 Azuer
googleCloud
的效果还不错,可惜不面向国内服务,购买或免费试用都过于困难。
这几天突然想到,edge浏览器自带语音朗读功能,一般 win10
win11
上自带安装edge,直接调用 edge tts
不就得了呗。 说干就干,github上搜索了些 edge-tts相关项目,参考了下,将之前未完成的语音配音继续完成。
整体思路如下:
技术栈:python3.10 + ffmpeg + openai-whisper离线模型 + Spleeter
从视频中提取出音频,然后将音频切割分段,对音频通过
openai-whisper
语音识别得到文字,并记录起始时间戳,将文字调用google api 翻译为想要的语言文字,整理为srt字幕文件。再将翻译后的语言文字,通过
Edge TTS
语音合成接口,生成相应语音片段,最后将语音片段、字幕和删掉音频轨道的视频合并,生成翻译后的视频语音识别使用
openai-whisper
离线模型、文字翻译使用google api,文字合成语音使用 Microsoft Edge tts,无需购买任何商业接口,也无需付费
实现步骤
原始 mp4 视频使用
ffmpeg
提取出音频,ffmpeg
命令如下ffmpeg -i 1.mp4 -acodec pcm_s16le -f s16le -ac 1 -ar 16000 -f wav audio.wav
将音频文件按照静音片段分隔为各个片段,以方便识别, 分割使用
pydub
库进行,安装命令pip install pydub
pydub.silence.detect_silence( normalized_sound, min_silence_len=min_slien ) # 返回二维的 list 结构起始时间戳,比如 # [ [0,5000],[6000,10000] ]
调用
openai-whisper
的语音识别库进行,安装命令pip install SpeechRecognition
r=speech_recognition.Recognizer() text = r.recognize_whisper(audio_listened, language="en")
text
即为识别到的文本。根据识别到的文本,再调用 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]
安装 srt 库
pip install srt
合并获取到的文字为srt格式字幕sub = srt.Subtitle(index=index, start=start, end=end, content=text)
合并后的srt文件
1
00:00:00,000 --> 00:01:00,000
如今,语音体验在商业领域已成为一件大事。为了获得良好的体验,您需要实时、准确的转录基础。但大多数自动语音识别服务 (ASRS) 都是建立在已有 50 多年历史的技术之上。2
00:01:00,000 --> 00:02:00,000
成本或高转录成本。因为我们更好、更快、更便宜,你猜怎么着?我们也更具可扩展性。因此,如果您因为缺乏正确的 API 而避免构建出色的语音功能,那么这是个好消息。是时候去上班了。3
00:02:00,000 --> 00:02:10,009
打造下一个出色的语音产品,我们已准备好帮助实现它。 deepgram,没有妥协,只有机会..到此字幕文件完成了。调用 ffmpeg 合成即搞定了字幕翻译
ffmpeg -y -i {source_mp4} -i {sub_name} -c copy -c:s mov_text -metadata:s:s:0 language={video_config['subtitle_language']} {target_mp4}
还想继续生成配音,那么继续安装
pip install edge-tts
,将翻译后的每段文本依次交给edge_tts
进行合成,合成后临时创建mp3音频,将该音频使用pydub转为合适的数据格式,存在segments
列表中,同时记录下每段文本位于原音频中的开始时间start_time
放入start_times
列表中,关键代码如下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
将原视频删掉音频轨道
ffmpeg.exe -i videoWithAudio.mp4 -map 0 -map 0:a:1 -copy novoice.mp4
然后添加上面合成后的新音频
ffmpeg -y -i novoice.mp4 -i hecheng.wav -c copy -map 0:v:0 -map 1:a:0 voiceandvideo.mp4
合成后的新视频再添加字幕srt
ffmpeg -y -i voiceandvideo -i zimu.srt -c copy -c:s mov_text -metadata:s:s:0 language=zh-cn allend.mp4
至此完成。字幕效果看起来还凑合,语音效果勉强也能接受。作为低层本方案玩玩可还行。
- 去除背景音采用
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}")
这是一个强大的提取背景音乐的库,详细使用方法见 https://github.com/deezer/spl...
遇到不少坑
- ffmepg直接放在工程目录下,然后通过修改
os.environ['path']
的方式直接定位 声画不对齐的问题。
同样一句话,使用中文和使用英文大概率所需时间是不同的,这就导致原本5s的声音片段转为其他语言后变成了10s或者相反。
因此加入了语速调整,根据需要可降低或增加语速。
同时添加了自动调整语速,如果翻译后的语音时长大于翻译前的,则对翻译后的音频强制加速播放,实现时长对齐。
使用 tkinter 包装个GUI界面
- 使用py自带的标准GUI库
tkinter
简单包装了下,为布局方便,又使用了 tkinter 的包装库PySimpleGUI
- 使用 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_replace:根据所选目标语言代码,填写对应的角色名,注意角色名的前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_replace 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_replace en-US-AriaNeural --voice_autorate --whisper_model small
上述意思是,将源语言为中文的 D:/video/ex.mp4 视频,翻译为英文视频,设置代理 http://127.0.0.1:10809 使用配音角色为 en-US-AriaNeural,如果翻译后的语音时长大于原语音,则自动加速,文字识别模型采用 small 模型
效果
github源码
https://github.com/jianchang512/pyvideotrans
使用或参考的开源项目
https://github.com/jiaaro/pydub
https://github.com/rany2/edge...
https://github.com/facebookre...
https://github.com/coqui-ai/TTS