掘金 人工智能 07月23日 14:40
一次用Python制作电影字幕的尝试
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文介绍如何使用Python自动为视频生成字幕文件。文章详细阐述了从视频中提取音频、将音频切片、调用语音转文字引擎(如Google、Sphinx)进行识别,以及利用Google翻译将识别结果翻译成目标语言等关键步骤。最后,文章展示了如何将处理后的文字信息按照SRT格式生成字幕文件,并提供了完整的Python代码实现,帮助用户快速掌握视频字幕自动化的技术。

🎦 **视频字幕生成流程:** 文章核心在于利用Python自动化处理视频字幕生成。首先,通过`moviepy`库从视频中提取音频并保存为WAV格式;接着,利用`pydub`库根据音频的静默段落将音频文件切割成小片段,以便于后续的语音识别;然后,调用`speech_recognition`库,支持Google在线识别和本地Sphinx识别,将音频片段转换为文字;最后,通过`googletrans`库进行翻译,并按照SRT格式组织生成字幕文件。

🔊 **音频处理与分割:** 为了提高语音识别的效率和准确性,文章强调了将长音频文件进行分割的重要性。作者选择了使用`pydub`库中的`detect_nonsilent`函数,通过检测音频中的静默部分来自动分割音频。这种方法比固定时间分割更为智能,能够更准确地抓住有声内容,为后续的文字转换奠定基础。

🗣️ **语音识别与翻译:** 文章重点介绍了两种语音识别引擎。Google在线识别简单易用,但可能受网络限制,作者提供了修改访问URL的解决方案;本地Sphinx识别则需要安装额外的库和配置,但无需联网。识别出的语音经过处理后,使用Google翻译API进行多语言翻译,最终输出源语言和目标语言的字幕内容。

📝 **SRT格式与字幕嵌入:** 文章解释了SRT字幕文件的基本格式,并提供了`format_time`函数将时间戳转换为SRT所需的时:分:秒,毫秒格式。此外,还简单介绍了如何使用FFmpeg命令将SRT字幕文件嵌入到视频中,包括软字幕和硬字幕的实现方式,为用户提供了字幕处理的完整流程。

💻 **代码实现与细节:** 文章提供了完整的Python代码,集成了视频处理、音频分割、语音识别、翻译和SRT文件生成等功能。代码中包含了对不同库的安装说明、关键函数的实现以及主程序逻辑,并详细记录了在实现过程中遇到的问题及解决方案,为读者提供了实践指导。

前言

    最近在看一部电影,发现没有字幕。网上找了找发现也没有,于是,萌生了一个想法:就是用Python去自动生成字幕文件

    这边大体是参考这篇文章,不过也有些许不同,主要是引擎方面的选择

一、字幕文件

    要做字幕,得先了解字幕文件的格式才行,主流的格式是:ass(SubRip Text)、ssa(S Station Alpha)、srt(Advanced SubStation Alpha)、vtt(webvtt,类似于srt),具体参考这里。这边我选择的是SRT格式,SRT的格式如下:

    除了字幕文件之外,还有就是选择语音转文字的引擎,国内的有:百度、网易见外等,国外的有:谷歌、IBM等,这里我选择的是speech_recognition,因为Python可以直接用,而且支持别的多国语言

二、思路整理

    开始做之前,先捋一下思路

1.分离视频里的语音

    由于做字幕只需要语音即可,所以需要先把视频中的语音提取出来,这部分直接用moviepy就行了,pip直接安装就可以了

pip install moviepy

代码也比较简单,如下:

from moviepy.video.io.VideoFileClip import VideoFileClipvideo_path = r'G:\usr\a.mp4'file_name = get_file_name(video_path);audio_file = file_name + '.wav'video = VideoFileClip(video_path)video.audio.write_audiofile(audio_file, ffmpeg_params=['-ar', '16000', '-ac', '1'])

FFmpeg的参数说明:

2.将语音切片

    电影分离的语音整个文件太大,而且做字幕也不能整个电影语音生成,所以需要把音频文件进行分割。分割用的是Python自带的pydub模块进行分割的(只支持原生的wav格式),安装命令,如下:

pip install pydub

分割语音主要有两种方式:一是用时间分割,二是用静默分割。这里用时间分割显然不行,不可能去看一遍电影在进行处理,所以只能用静默分割,主要代码如下:

from pydub import AudioSegmentfrom pydub.silence import detect_nonsilent# 这里silence_thresh是认定小于-70dBFS以下的为silence,发现小于 sound.dBFS * 1.3 部分超过500毫秒,就进行拆分。这样子分割成一段一段的。# 通过搜索静音的方法将音频分段# 参考:https://wqian.net/blog/2018/1128-python-pydub-split-mp3-index.htmlsound = AudioSegment.from_file(audio_file)timestamp_list = detect_nonsilent(sound, 500, sound.dBFS * 1.3, 1)

3.调用引擎转成文字

    接下来就是将切割好的语音转成文字,用的是speech_recognition模块进行转换,这个模块有google、sphinx、bing、google_cloud进行选择,除了sphinx是本地引擎外,其他都需要联网,google可以直接进行翻译,其他两个需要注册才能用,所以这边主要用googlesphinx

     google方法的用法比较简单,只需指定文件和语言就行了,不用由于连的是Google,所以访问不了,有两种选择,一个是用VPN,另一种是修改访问的URL。使用VPN就不说了,这里主要说下第二种,直接用PyCharm点开speech_recognition模块,找到**www.google.com/speech-api/…,将其改为**www.google.cn/speech-api/…即可。或者找到pyhon所在路径\Lib\site-packages\speech_recognition目录,底下有个__init__.py文件,打开进行同样的操作也行,这个主要参考这篇文章

改完之后,就可以使用了。代码如下:

import speech_recognition as srpython r = sr.Recognizer() wav = sr.AudioFile('abc.wav')with wav as source:audio = r.record(source)result = r.recognize_google(audio, language='ko-KR', show_all=True)

    使用 sphinx转换的话需要先安装pocketsphinx模块,安装命令如下:

 pip install pocketsphinx 

不过这样子直接安装,可能会出现以下错误:这里是由于没有安装Swig的缘故,需要下载在配置一下环境变量即可,具体可以参考这里

安装成功之后,speech_recognition默认只有英文的,所以还需要添加一下中文库。不过遗憾的是,这里好像没有日文和韩文。下载完之后解压,需要重命名文件,如下:

 cmusphinx-zh-cn-5.2 -> zh-CN zh_cn.cd_cont_5000 ->  acoustic-model zh_cn.lm.bin -> language-model.lm.bin zh_cn.dic -> pronounciation-dictionary.dict

改完之后复制到pyhon所在路径\Lib\site-packages\speech_recognition\pocketsphinx-data目录下,详细可以参考这里

完成之后,代码如下:

import speech_recognition as srpython r = sr.Recognizer() wav = sr.AudioFile('abc.wav')with wav as source:audio = r.record(source)result = r.recognize_sphinx(audio, language='zh-CN', show_all=True)    print("翻译结束:"+result.hyp().hypstr)

4.调用谷歌翻译

    使用谷歌翻译模块时候要注意,不能直接install,不然会出现如下错误(Google翻译更改了API导致):这里得先卸载再安装别的版本,具体参考这里,如下:

pip uninstall googletranspip install googletrans==4.0.0-rc1

安装好之后,用就很简单了,如下:

from googletrans import Translator# 修改google翻译地址translator = Translator(service_urls=[    'translate.google.cn'])translator.translate('hello', dest='zh-cn').text

5.生成SRT文件

    生成SRT文件也简单,只要按srt文件要求格式化一下时间就行了

import mathfrom time import strftime, gmtimedef format_time(timestamp):    if timestamp is None:        return ''    second = math.modf(timestamp / 1000)    return strftime("%H:%M:%S", gmtime(second[1])) + ',' + str(int(round(second[0], 3) * 1000))

6.视频嵌入字幕

    这一步其实可有可无,现在视频播放器基本都支持读取srt字幕。这里就简单说下吧,添加字幕分为软字幕和硬字幕,使用FFmpeg命令添加如软字幕(貌似只能mkv格式),如下:

ffmpeg -i test.mp4 -i test.srt -c copy output.mkv

添加硬字幕命令,如下:

ffmpeg -i test.mkv -vf subtitles=test.srt out.mp4

这边只是简单的介绍下,具体参考这篇文章

三、代码实现

    完整代码,如下:

import mathimport osfrom time import strftime, gmtimeimport speech_recognition as srfrom googletrans import Translatorfrom moviepy.video.io.VideoFileClip import VideoFileClipfrom pydub import AudioSegmentfrom pydub.silence import detect_nonsilent# 修改google翻译地址translator = Translator(service_urls=[    'translate.google.cn'])"""    转换srt格式时间,毫秒转时分秒"""def format_time(timestamp):    if timestamp is None:        return ''    second = math.modf(timestamp / 1000)    return strftime("%H:%M:%S", gmtime(second[1])) + ',' + str(int(round(second[0], 3) * 1000))"""    调用google翻译"""def translate(text, dest='zh-cn'):    if text is None:        return text    if text == '':        return text    return translator.translate(text, dest=dest).text"""    切割翻译结果,避免字幕显示过长"""def split_srt(text):    new_text = ''    # 长度大于20进行分割    while len(text) > 20:        new_text += text[:20] + '\n'        text = text[20:]    new_text += text[0:]    return new_text"""    获取文件名"""def get_file_name(file):    path, name = os.path.split(file)    name, suffix = os.path.splitext(name)    return name"""    生成字幕文件"""def generate_srt(srt_name, srt_content):    srt_file = srt_name + r'.srt'    with open(srt_file, "w", encoding='utf-8') as f:        f.write(srt_content)"""    调用google在线翻译"""def recognize_google(recognizer, audioData, language):    result = recognizer.recognize_google(audioData, language=language, show_all=True)    if len(result) != 0:        return handle_result(language, result['alternative'][0]['transcript'])"""    本地翻译"""def sphinx(recognizer, audioData, language):    result = recognizer.recognize_sphinx(audioData, language=language, show_all=True)    return handle_result(language, result.hyp().hypstr)"""    处理翻译结果"""def handle_result(language, source_result):    if source_result is None:        return None    source_result = source_result.replace(" ", "")    if 'zh' not in language:        dest_result = translate(source_result)        return source_result, dest_result.replace(" ", "")    return source_result, ''if __name__ == '__main__':    dest = translate('hello')    video_path = r'G:\FFOutput\test.mp4'    video_language = r'zh-CN'    file_name = get_file_name(video_path);    audio_file = file_name + '.wav'    video = VideoFileClip(video_path)    video.audio.write_audiofile(audio_file, ffmpeg_params=['-ar', '16000', '-ac', '1'])    # 这里silence_thresh是认定小于-70dBFS以下的为silence,发现小于 sound.dBFS * 1.3 部分超过 500毫秒,就进行拆分。这样子分割成一段一段的。    # 通过搜索静音的方法将音频分段    # 参考:https://wqian.net/blog/2018/1128-python-pydub-split-mp3-index.html    sound = AudioSegment.from_file(audio_file)    timestamp_list = detect_nonsilent(sound, 500, sound.dBFS * 1.3, 1)    r = sr.Recognizer()    idx = 0    srt_text = '';    for i in range(len(timestamp_list)):        # sound[timestamp_list[i][0]:timestamp_list[i][1]].export(str(i)+'.wav', format="wav")        # wav = sr.AudioFile(str(i)+'.wav')        # with wav as source:        #     audio = r.record(source)        #     result = r.recognize_google(audio, language='ko-KR', show_all=True)        wav = sound[timestamp_list[i][0]:timestamp_list[i][1]].raw_data;        source, dest = recognize_google(r, sr.AudioData(sample_width=2, sample_rate=16000, frame_data=wav),                                        language=video_language)        if source is not None:            srt_text += '{0}\n{1} --> {2}\n'.format(idx, format_time(timestamp_list[i][0]),                                                    format_time(timestamp_list[i][1]))            srt_text += split_srt(source)            srt_text += '\n'            srt_text += split_srt(dest)            srt_text += '\n'            idx = idx + 1            print(str(i) + ":" + source + '->' + dest);        d = timestamp_list[i][1] - timestamp_list[i][0]        print("Section is :", timestamp_list[i], "duration is:", d)    #print('dBFS: {0}, max_dBFS: {1}, duration: {2}, split: {3}'.format(round(sound.dBFS, 2), round(sound.max_dBFS, 2),    #                                                                   sound.duration_seconds, len(timestamp_list)))    generate_srt(file_name, srt_text)


总结

    这里实现的方式其实网上大部分都有,这里只是简单的做一下尝试和整合,以及记录一下遇到的问题(✪ω✪)

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Python 视频字幕 语音识别 ASR moviepy pydub speech_recognition googletrans
相关文章