【class19】
上节课,我们学习了:
语音识别模型的结构和原理,同时调用创建好的AipSpeech客户端实现了语音转文字功能。
本节课,我们将初识字幕,学习这些知识点:
1. srt字幕 2. 获取时间数据 3. 时间换算
我们先来了解一下什么是外挂字幕?这类字幕有三个特点:
1. 独立于视频的字幕文件 2. 不破坏视频画面,可随时根据需要更换字幕语言
3. 可随时编辑字幕内容,方便修改错误
我们今天要学习和自动生成的srt字幕,就是一种外挂字幕。当你在观看视频的时候,下面的那些字幕可能就是通过srt文件播放的哦~
srt字幕
定义
srt字幕的全称是SubRip Text,是一种文本格式的外挂字幕,也是目前最常用的外挂字幕格式之一。
srt字幕制作规范简单,格式为:序号+时间+字幕,修改十分方便!
我们以文本格式(如word文档,以Unicode(UTF-8)编码方式)打开一个标准的srt字幕文件,可以看到每段字幕由三部分组成:
1. 序号 2. 时间 3. 字幕
学习完srt字幕的概念和内容组成之后:
我们实现“生成标准srt字幕文件”的第一步——从音频中获取时间数据。
接下来,我们需要从视频中获取时间数据,以便制作srt字幕中的时间部分~
让我们一起尝试一下吧~
代码结构
将从音频文件中获取时间数据分为两部分:
part1. 导入模块
part2. 获取时间数据
分析代码:
获取时间数据
代码的作用
第12-16行,导入模块,获取语音段的时间信息;第16行,使用detect_nonsilent()获取语音段时间信息。
导入detect_nonsilent类
需要使用的是pydub.silence模块里面的detect_nonsilent类。使用from...import从pydub.silence导入detect_nonsilent类。
detect_nonsilent()
导入模块后,通过detect_nonsilent()获取语音段时间信息。
detect_nonsilent()与前面切分音频中学到的split_on_silence()传入参数和用法一样。
不同之处在于:detect_nonsilent()通过传入静音段的参数min_silence_len, silence_thresh去除静音段,来定位语音段的位置,从而获取语音段时间信息,返回值为列表。
必选参数:sound
sound为必选参数,即待提取时间信息的音频对象。
可选参数:min_silence_len
min_silence_len为可选参数,它表示的是静音段的最小长度,默认值:1000(毫秒)。
设置min_silence_len = 500,表示静音部分长度任何比这大的值将被视为静音段。
可选参数:silence_thresh
silence_thresh为可选参数,它表示的是静音段的最小声音强度,默认值:-16dbfs。
设置silence_thresh = -50,表示任何比这安静(如-55)的值将被视为静音。
返回值
将获取的语音段时间信息返回值赋值给变量timestamp_list,返回值为列表。
总结:
获取时间数据
首先,使用from...import从pydub.silence导入detect_nonsilent。
然后,使用detect_nonsilent()获取语音段时间信息。
获取语音段时间信息后保存至列表timestamp_list中。输出查看列表timestamp_list,结果为语音段开始时间和结束时间的二维列表。
二维列表中的每个列表元素为单段语音段的开始时间和结束时间。时间单位为毫秒。
二维列表中共12个列表元素,对应着获取的12个音频片段的开始时间的结束时间。
举例说明:图中绿色框内的数据表明,音频片段11.wav在整段音频的22640ms开始,至23048ms结束。
以文本格式(如word文档)打开一个标准的srt字幕文件,可以看到:
在标准的srt字幕中,时间格式为【时:分:秒,毫秒】。其中,时:分:秒的位数均为两位数,毫秒位数为三位数。
获取了语音段时间数据之后,我们需要进行格式转换:
我们实现“生成标准srt字幕文件”的第二步——毫秒换算成【时:分:秒,毫秒】格式~
接下来,我们通过数学计算模和余数,进行时间换算: 将毫秒时间换算成【时:分:秒,毫秒】格式。
学习代码前你需要知道的:1秒=1000毫秒,1分钟=60秒,1小时=60分钟。
代码结构
将时间数据格式转换分为两部分:
part1. 毫秒、秒、分和时换算
part2. 组成【时:分:秒,毫秒】格式
分析代码:
毫秒转换
毫秒t除以1000,将毫秒t转换为秒,毫秒,整数为秒,余数为毫秒。
整数和余数分别赋值给spart和mspart。
divmod()
divmod() 函数把取整和取余运算结合起来,返回一个包含商和余数的元组。
这里返回元组为:(t//1000 , t%1000)。
秒转换
秒spart除以60,将秒spart转化为分,秒,整数为分,余数为秒。
整数和余数分别赋值给mpart和spart。
时转换
分mpart除以60,将分mpart转化为时,分,整数为时,余数为分。
整数和余数分别赋值给hpart和mpart。
【时:分:秒,毫秒】格式
通过str()函数将hpart、mpart、spart、mspart转化为字符串格式,并以字符串组成【时:分:秒,毫秒】格式。
本段代码stype的结果为【0:0:0,53】,其中时、分、秒和毫秒的位数都不满足要求。
要求为:时:分:秒的位数均为两位数,毫秒位数为三位数。
下节课,我们将时间格式处理为标准格式——【00:00:00,053】
本节课,我们学习了srt字幕概念,获取了音频中的时间数据,并做了时间换算,初步得到字幕时间。
下节课,我们将完善【时:分:秒,毫秒】格式,以满足字幕格式要求,得到标准的srt字幕文件。
【class20】
上节课,我们学习了:
srt字幕概念,获取了音频中的时间数据,并做了时间换算,初步得到字幕时间。
上节课获取了音频中的时间数据,并做了时间换算,初步得到字幕时间格式为【0:0:0.53】。
但是,时、分、秒和毫秒的位数都不满足srt字幕文件格式要求。
要求为:时:分:秒的位数均为两位数,毫秒位数为三位数。
本节课,我们将学习这些知识点:
1. 【时:分:秒,毫秒】格式标准化 2. 去除标点符号
在上节课生成的时间格式基础之上:
我们实现“标准字幕文件生成”的第一步——时间格式标准化。
接下来,我们通过代码实现【时:分:秒,毫秒】格式标准化:将时、分、秒和毫秒的位数调整为srt字幕文件标准【时:分:秒,毫秒】格式。
【时:分:秒,毫秒】格式标准化
代码的作用
第8行-第18行,通过返回指定长度字符串(位数不够前面补0),将【时:分:秒,毫秒】格式标准化。
第9行,mspart返回长度为3的字符串
第11行,spart返回长度为2的字符串
第13行,mpart返回长度为2的字符串
第15行,hpart返回长度为2的字符串
第18行,组成【时:分:秒,毫秒】格式
示例代码:
...
""将毫秒t转换为时,分,秒,毫秒""
spart,mspart = divmod(t,1000)
mpart,spart = divmod(spart,60)
hpart,mpart = divmod(mpart,60)
# mspart返回长度为3的字符串,位数不够前面补0
mspart=str(mspart).zfill(3)
# spart返回长度为2的字符串,位数不够前面补0
spart=str(spart).zfill(2)
# mpart返回长度为2的字符串,位数不够前面补0
mpart=str(mpart).zfill(2)
# hpart返回长度为2的字符串,位数不够前面补0
hpart=str(hpart).zfill(2)
# 组成【时:分:秒,毫秒】格式
stype = hpart+":"+mpart+":"+spart+","+mspart
分析代码:
设置毫秒格式
使用str()函数将mspart转化为字符串,再通过zfill()函数返回长度为3的字符串,位数不够前面补0。
例如:mspart为14,位数为2,通过本行代码可变为"014"。
字符串
使用str()函数将mspart转化为字符串,mspart字符串为待处理对象。
zfill()
通过zfill()函数并传入数字参数,可返回指定长度的字符串。
Zfill()函数的用法
zfill()
是字符串对象的一个方法,用于在字符串的左侧填充指定数量的零字符('0'),直到字符串达到指定的长度。如果字符串已经达到或超过指定的长度,则不会填充任何字符。
zfill()
方法的语法如下:
str.zfill(width)
其中,str 是要操作的字符串,width 是期望的字符串长度。如果 str 的长度小于 width,则在左侧填充足够数量的零字符,直到字符串达到指定的长度。
例如:
s = "42"
padded_s = s.zfill(5)
print(padded_s) # 输出 "00042"
在这个例子中,字符串 "42" 的长度为 2,通过 zfill(5) 方法,将在字符串左侧填充 3 个零字符,使得最终字符串的长度为 5。
这个方法通常用于格式化数字字符串,以确保它们具有相同的长度,方便对齐和比较。
长度
必选参数,zfill()函数传入数字参数3,设置字符串长度为3。
设置秒格式
使用str()函数将spart转化为字符串,再通过zifill()函数返回长度为2的字符串,位数不够前面补0。
设置分格式
使用str()函数将mpart转化为字符串,再通过zifill()函数返回长度为2的字符串,位数不够前面补0。
设置时格式
使用str()函数将hpart转化为字符串,再通过zifill()函数返回长度为2的字符串,位数不够前面补0。
我们已经学会了将毫秒时间转换为【时:分:秒,毫秒】标准时间格式。
基于DRY原则,我们只需要把获取标准时间格式方法写成一个函数,方便多次调用。
编写得到标准时间的函数代码如下:
# 定义函数getTime(),传入参数t
def getTime(t):
# 将毫秒t转换为秒,毫秒
# 毫秒t除以1000,整数为秒,余数为毫秒
spart,mspart = divmod(t,1000)
# 将秒spart转化为分,秒
# 秒spart除以60,整数为分,余数为秒
mpart,spart = divmod(spart,60)
# 将分mpart转化为时,分
# 分mpart除以60,整数为时,余数为分
hpart,mpart = divmod(mpart,60)
# mspart返回长度为3的字符串,位数不够前面补0
mspart=str(mspart).zfill(3)
# spart返回长度为2的字符串,位数不够前面补0
spart=str(spart).zfill(2)
# mpart返回长度为2的字符串,位数不够前面补0
mpart=str(mpart).zfill(2)
# hpart返回长度为2的字符串,位数不够前面补0
hpart=str(hpart).zfill(2)
# 组成【时:分:秒,毫秒】格式
stype = hpart+":"+mpart+":"+spart+","+mspart
# 返回标准时间格式stype
return stype
进行检验:
学到这里,我们已经定义了标准时间格式函数,通过遍历列表可以循环调用。
我们前面得到的时间数据列表为timestamp_list,接下来,我们通过遍历和索引的方式取出二维列表中的每个元素,并将其转换为【时:分:秒,毫秒】标准时间格式。
通过定义时间格式转换函数:
我们已经将二维列表timestamp_list中的时间数据,转换为【时:分:秒,毫秒】的标准时间格式。
接下来,我们开始分析并制作srt字幕~
我们再次以文本格式(如word文档)打开一个标准的srt字幕文件,可以看到每段字幕由三部分组成:
第一行:序号,从0开始
第二行:标准时间格式,格式为:开始时间 --> 结束时间
第三行:字幕的文字部分,也就是语音识别的文字
字幕文件中需要注意的:
1.开始时间和结束时间的" --> ",箭头前后各有一个空格;
2.两段字幕之间空了两行分隔开来,方便后续修改字幕。
字幕文件的三部分:序号、标准时间格式和文字,我们均已获得。
接下来,我们通过代码将该三部分组合起来,实现“标准字幕文件生成”的第二步——输出标准的srt字幕。
学习了如何输出标准的srt字幕之后:
基于DRY原则,我们只需要把输出srt字幕方法写成一个函数,方便多次调用。
生成字幕的函数为:
# 定义函数getsrt(),传入参数sn,start_time,end_time,text
def getsrt(sn,start_time,end_time,text):
# 序号、开始结束时间和文字组合起来得到标准字幕
srt_text = str(sn)+"\n"+start_time+" --> "+end_time+"\n"+text+"\n"+"\n"
# 返回标准字幕srt_text
return srt_text
输出查看srttext的结果,可以看到:
输出的字幕文字部分包括标点符号。一般来说,电影的字幕要求十分严格,不允许加标点符号~
接下来,我们实现“标准字幕文件生成”的第三步——去除标点符号。
通过代码去除字符串中的标点符号:
使得输出的字幕文字更符合电影字幕的要求。
示例代码
代码的作用
第3行-第8行,将text文字中的标点符号替换成空格。
第4行,定义常用标点符号列表
第6行,for循环遍历symbol
第8行,使用replace()函数,将text文字中的标点符号替换成空格
...
# 定义常用标点符号列表,赋值给symbol
symbol = [",","。","!","?"]
# for循环遍历symbol
for j in symbol:
# 使用replace()函数,将text文字中的标点符号替换成空格
text = text.replace(j," ")
分析代码:
标点符号列表
定义常用标点符号列表,包括逗号、句号、感叹号和问号四种。
遍历
通过for循环遍历列表symbol,j为列表symbol中的每个元素。
replace()
使用replace() 函数,把字符串text中的标点符号替换成空格。
用法:new_str = old_str.replace(a,b),将字符串old_str中变量a的值替换为变量b的值,结果赋值给new_str,原字符串old_str不变。
去除字幕文字中的标点符号之后:
我们实现“标准字幕文件生成”的第四步——写入srt字幕文件
写入srt字幕文件函数
通过定义函数writesrt(),传入参数srt,写入标准字幕文件。
使用 with...as 配合open()函数以a方式,打开路径为srtpath的srt文件(没有就新建一个),并赋值给 fp;
使用write()函数写入文件。最后,调用函数writesrt(),写入srt字幕文件,并输出提示信息:srt字幕文件写入成功!
# 定义函数writesrt(),传入参数srtpath,srt,写入标准字幕文件
def writesrt(srtpath,srt):
# 使用 with...as 配合open函数以a方式,打开路径为srtpath的srt文件
with open(srtpath,"a") as fp:
# 使用write()函数写入文件
fp.write(srt)
# 序号sn
sn = 0
# 开始时间start_time,结束时间end_time
start_time = "00:00:00,537"
end_time = "00:00:02,627"
# 文字部分text
text = "看来我不应该来"
# 将以上三部分组合起来,通过换行符\n实现换行
srt_text = str(sn)+"\n"+start_time+" --> "+end_time+"\n"+text+"\n"+"\n"
# 写入文件路径
srtpath = "/Users/yequ/大话西游.srt"
# 调用函数writesrt(),写入srt字幕文件
writesrt(srtpath,srt_text)
# 输出提示信息:srt字幕文件写入成功!
print("srt字幕文件写入成功!")
至此,我们要帮助阿九实现"一键生成srt字幕文件" 的功能就全部完成啦。
我们以文本格式(如word文档)打开生成的“大话西游.srt”字幕文件,可以看到最终的效果了!
使用支持外挂字幕的播放器打开“大话西游.mp4”视频文件,就可以看到视频中的字幕了!
需要注意两点:
1.视频文件和字幕文件在同一个文件夹下
2.视频文件和字幕文件命名一样