手撸个视频翻译和配音工具玩玩 结果不太妙

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

原本只打算语音识别转为文字后,生成字幕就完结了,因为实在没有找到满足"自然音色、准确度好、容易安装"这些条件的好的语音合成方案,比如facebook的 facebook seamless_communication mozilla/TTS , 又从 huggingface.co/ 找了不少模型,结果都不理想,貌似除了针对性训练,其他效果都很差,不具备可用性。

百度语音、讯飞等国内api只能提供中英语言,而无法提供其他语言比如日语、韩语等。

Azuer googleCloud 的效果还不错,可惜不面向国内服务,购买或免费试用都过于困难。

这几天突然想到,edge浏览器自带语音朗读功能,一般 win10 win11 上自带安装edge,直接调用 edge tts 不就得了呗。

说干就干,github上搜索了些 edge-tts相关项目,参考了下,将之前未完成的语音配音继续完成。

整体思路如下:

工具 python3.10 + ffmpeg

从视频中提取出音频,然后将音频切割分段,对音频进行语音识别得到文字,并记录起始时间戳,将文字调用翻译接口,翻译为想要的语言文字,整理为srt字幕文件。

再将翻译后的语言文字,通过语音合成接口,生成相应语音片段,最后将语音片段、字幕和删掉音频轨道的视频合并,生成翻译后的视频

语音识别和文字翻译使用google接口,文字合成语音使用 Microsoft Edge tts,无需购买任何商业接口,也无需付费

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

    css 复制代码
    ffmpeg -i 1.mp4 -acodec pcm_s16le -f s16le -ac 1 -ar 16000 -f wav audio.wav
  2. 将音频文件按照静音片段分隔为最大不超过60s的小片段,以方便识别,使用pydub库进行,先安装 pip install pydub

ini 复制代码
pydub.silence.detect_nonsilent(normalized_sound, 
min_silence_len=min_slien, 
silence_thresh=-20.0 - thd,
seek_step=1)
  1. 调用google的语音识别库进行,直接 pip install SpeechRecognition ,可避免再去申请google cloud key,里面自带一个测试key,至少目前来说,并没发现频率和用量方面的限制,关键命令
ini 复制代码
r=speech_recognition.Recognizer()
text = r.recognize_google(audio_listened, language="en")

text 即为识别到的文本。

  1. 根据识别到的文本,再调用google翻译api翻译为所需语言的文字
ini 复制代码
translator = Translator(service_urls=['translate.googleapis.com'])
transd = translator.translate(text, src="en",dest="zh-cn")

这里需要安装google翻译python库,同样无需申请api key。 pip install googletrans

  1. 使用 srt 库 pip install srt 合并获取到的文字为srt格式字幕
sql 复制代码
sub = srt.Subtitle(index=index, start=start, end=end, content=text)
  1. 到此字幕文件完成了。调用ffmpeg合成即搞定了字幕翻译
css 复制代码
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}
  1. 还想继续生成配音,那么继续安装 edge-tts pip install edge-tts,将翻译后的每段文本依次交给edge_tts 进行合成,合成后临时创建mp3音频,将该音频使用pydub转为合适的数据格式,存在segments列表中,同时记录下每段文本位于原音频中的开始时间 start_time 放入 start_times 列表中,关键代码如下
ini 复制代码
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)

最后将合成后的所有语音片段连接

ini 复制代码
# 拼接配音片段
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
  1. 将原视频删掉音频轨道
go 复制代码
ffmpeg.exe -i videoWithAudio.mp4 -map 0 -map 0:a:1 -copy novoice.mp4

然后添加上面合成后的新音频

go 复制代码
ffmpeg -y -i novoice.mp4 -i hecheng.wav -c copy -map 0:v:0 -map 1:a:0 voiceandvideo.mp4 
  1. 合成后的新视频再添加字幕srt
css 复制代码
ffmpeg -y -i voiceandvideo -i zimu.srt -c copy -c:s mov_text -metadata:s:s:0 language=zh-cn  allend.mp4

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


遇到不少坑

  1. google代理的问题。由于都懂的缘故,无法访问google,不得不配置代理,但 googletrans 库在代理支持下貌似存在问题,只好安装googletrans 3.1.0a0版本,然后将 lib/googletrans/client.py 中的proxies类型改为
python 复制代码
proxies: typing.Dict[str, httpcore.SyncHTTPProxy] = None,

在程序启动脚本中修改环境变量

css 复制代码
os.environ['http_proxy'] = proxy
os.environ['https_proxy'] = proxy

设置系统代理

  1. ffmepg同样也是直接放在工程目录下,然后通过修改 os.environ['path'] 的方式直接定位

  2. 声画不对齐的问题。

    同样一句话,使用中文和使用英文大概率所需时间是不同的,这就导致原本5s的声音片段转为其他语言后变成了10s或者相反。因此加入了语速调整,根据需要可降低或增加语速。然而效果还是不尽如人意,有时中文很短的一句话,英文却说半天。看了其他方案,基本都是在配音加入视频前,允许用户手动调整时间线再次对齐,作为用爱发电的项目,就省略了这麻烦的一步。

  3. 不对齐的问题也会出现在字幕和声音上,主要是音频切割导致的,音频一般按照固定时间切分,或者按照静音片段切分,但前者可能导致一句话未说完就被分开;后者在长时间没有足够静音片段时,可能导致某段太长,不得不强制分割,或者存在背景音时,会无法准确识别到文字。(这里不得不吐槽下,很多老外说话机关枪似的,在1分钟内500ms的停顿时间都很难找)。

  4. 当前解决方案是:遇到某些片段无法识别,就直接跳过复制原始音频片段,所以翻译后的视频中可能仍存在原语音,尤其是在存在背景音乐和背景噪声时。 尝试通过 noisereduce 库降噪,但效果更糟糕,暂时放弃。

ini 复制代码
# 尝试用过的降噪方法
from noisereduce import reduce_noise
audio_array = audio.get_array_of_samples() 
reduced_noise = reduce_noise(audio_array, audio.frame_rate) 

reduced_audio = AudioSegment( reduced_noise.tobytes(), frame_rate=audio.frame_rate, sample_width=audio.sample_width, channels=audio.channels )

使用 tkinter 包装个GUI界面

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

GUI界面

效果

原视频 翻译后视频

翻译后的视频如预料中一样,中间某些片段无法识别或无法合成,就直接复制了原音频片段,效果不理想。 改进方法还是需要通过降噪、去除背景音乐等方式提高识别准确度,但目前尚未找到"无需训练、无需安装庞大模块"的合适方案。

github源码

github.com/jianchang51...

使用或参考的开源项目

github.com/jiaaro/pydu...

github.com/rany2/edge-...

github.com/facebookres...

github.com/coqui-ai/TT...

相关推荐
湫ccc2 分钟前
《Python基础》之pip换国内镜像源
开发语言·python·pip
瓜牛_gn2 分钟前
依赖注入注解
java·后端·spring
hakesashou3 分钟前
Python中常用的函数介绍
java·网络·python
菜鸟的人工智能之路13 分钟前
极坐标气泡图:医学数据分析的可视化新视角
python·数据分析·健康医疗
菜鸟学Python14 分钟前
Python 数据分析核心库大全!
开发语言·python·数据挖掘·数据分析
小白不太白95016 分钟前
设计模式之 责任链模式
python·设计模式·责任链模式
Estar.Lee20 分钟前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
喜欢猪猪21 分钟前
Django:从入门到精通
后端·python·django
一个小坑货21 分钟前
Cargo Rust 的包管理器
开发语言·后端·rust
bluebonnet2726 分钟前
【Rust练习】22.HashMap
开发语言·后端·rust