this512 · 2023年10月30日 · 山东

搞了个视频翻译和配音工具,使用whisper,edgeTTS

近来得空研究了下视频翻译,即将某种语言的视频处理后,显示另一种语言的字幕并使用该语言进行配音。最终实现了这种效果:

可将一种语言的视频翻译为另一种语言和配音的视频。 语音识别基于 openai-whisper 离线模型、文字翻译使用 google 翻译api,文字合成语音使用 Microsoft Edge TTS,背景音消除使用 Spleeter,无需购买任何商业接口,也无需付费

1.png

开源地址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,无需购买任何商业接口,也无需付费

实现步骤

  1. 原始 mp4 视频使用 ffmpeg 提取出音频,ffmpeg 命令如下

    ffmpeg -i 1.mp4 -acodec pcm_s16le -f s16le -ac 1 -ar 16000 -f wav audio.wav
  2. 将音频文件按照静音片段分隔为各个片段,以方便识别, 分割使用pydub库进行,安装命令pip install pydub

    pydub.silence.detect_silence(
     normalized_sound, 
     min_silence_len=min_slien
    )
    # 返回二维的 list 结构起始时间戳,比如
    # [ [0,5000],[6000,10000] ]
     
  3. 调用 openai-whisper 的语音识别库进行,安装命令 pip install SpeechRecognition

        r=speech_recognition.Recognizer()
        text = r.recognize_whisper(audio_listened, language="en")

    text 即为识别到的文本。

  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格式字幕

    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,没有妥协,只有机会..

  6. 到此字幕文件完成了。调用 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}
  7. 还想继续生成配音,那么继续安装 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
  8. 将原视频删掉音频轨道

    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 
  1. 合成后的新视频再添加字幕srt

    ffmpeg -y -i voiceandvideo -i zimu.srt -c copy -c:s mov_text -metadata:s:s:0 language=zh-cn  allend.mp4

至此完成。字幕效果看起来还凑合,语音效果勉强也能接受。作为低层本方案玩玩可还行。

  1. 去除背景音采用 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...


遇到不少坑

  1. ffmepg直接放在工程目录下,然后通过修改 os.environ['path'] 的方式直接定位
  2. 声画不对齐的问题。

    同样一句话,使用中文和使用英文大概率所需时间是不同的,这就导致原本5s的声音片段转为其他语言后变成了10s或者相反。
    因此加入了语速调整,根据需要可降低或增加语速。
    同时添加了自动调整语速,如果翻译后的语音时长大于翻译前的,则对翻译后的音频强制加速播放,实现时长对齐。

使用 tkinter 包装个GUI界面

  1. 使用py自带的标准GUI库 tkinter 简单包装了下,为布局方便,又使用了 tkinter 的包装库 PySimpleGUI
  2. 使用 pyinstaller 打包exe pyinstall -w sp.py

GUI界面

1.png

CLI 模式

cli.png

新增了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 模型

效果

点击查看demo对比效果

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

https://github.com/deezer/spl...

https://github.com/openai/whi...

推荐阅读
关注数
1
内容数
1
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息