如果以下内容对你有所帮助,可以点个赞关注一波哈!
openSMILE的主要版本包括Windows(32位)和Linux(64位)以及Android的二进制文件ARM(自2.1以来,android-10)。
对于Windows的朋友来说,大概意思就是你得再安一个linux系统(除非你的系统是Windows XP)。
这边建议在虚拟机中安linux系统。那具体怎么安装,这个别的地方应该挺多资源可以参考的,在这就不过多描述了。
如果已经安好linux系统了,就先去http://opensmile.audeering.com/下载opensmile。
Linux系统发行包包含静态链接的主要可执行文件SMILExtract,示例配置文件在config/文件夹中,可视化脚本 以及其他比如建模任务在scrrpts/文件夹,其他资源在src/文件夹。
下载完之后,可以参考另外一位博主的博客,翻译得特别好。https://blog.csdn.net/lccever/article/details/78649538
如果在这个过程中遇到什么问题,可以在底下评论或者私信我,我会尽快回复!
在这里先介绍下opensmile自带的配置文件:
emobase2010.conf 情感特征:1582维特征向量
emobase_live4_batch_single.conf 现场情感识别:988维特征向量
IS09_emotion.conf 情感挑战:384维特征向量
emo_large.conf :大集合6552维向量
IS12_speaker_trait.conf 数字兼容特性
liveProsodyAcf.conf 语音韵律特征的音调和响度
(不同版本的opensmile特征数量稍微有些不同)
所以只需要根据自身的需求,更换下面命令中的conf文件名即可,注意音频文件必须是无损的wav格式。
SMILExtract -C config/IS09_emotion.conf -I wav_samples/opensmile.wav -O speech01.energy.csv
那在正式提取特征之前,我们还需要将我们需要提取的音频切割成多个短音频(如果后续有特征融合的需求的话),短音频的时长具体多少可根据自身需求进行更改,我这里是切割成了多个100ms(0.1s)的短音频。(很多情况下其实是按句子来切割的!)
具体代码如下:
import os
import wave
from pydub import AudioSegment
import contextlib
def get_wav_time(wav_path):
'''
获取音频文件时长
:param wav_path: 音频路径
:return: 音频时长 (单位秒)
'''
with contextlib.closing(wave.open(wav_path, 'r')) as f:
frames = f.getnframes()
rate = f.getframerate()
duration = frames / float(rate)
return duration
def get_ms_part_wav(main_wav_path, start_time, end_time, part_wav_path):
'''
音频切片,获取部分音频 单位是毫秒级别
:param main_wav_path: 原音频文件路径
:param start_time: 截取的开始时间
:param end_time: 截取的结束时间
:param part_wav_path: 截取后的音频路径
:return:
'''
start_time = int(start_time)
end_time = int(end_time)
sound = AudioSegment.from_mp3(main_wav_path)
word = sound[start_time:end_time]
word.export(part_wav_path, format="wav")
if __name__ == '__main__':
path = r'E:/123/' # 原音频目录
path_segment = r'E:/234/' # 切割后的音频目录
print('开始切割音频!')
time_segment = 100 # 切割后短音频的时长
for root, dir, files in os.walk(path):
for i in range(len(files)):
audio = root + files[i]
time_all = int(get_wav_time(audio) * 1000) # 转换成毫秒
start_time = 0 # 从第0ms开始切割
index = 1 # 切割后的序号名,从序号1开始命令
while start_time <= time_all - time_segment:
# print(str(i)+ ': ' + str(index))
end_time = start_time + time_segment
aduio_segment = path_segment + files[i][:-4] + '_' + str(index) + '.wav'
get_ms_part_wav(audio, start_time, end_time, aduio_segment)
start_time += time_segment
index += 1
# 接下来这两行是为了将最终能够不足time_segment时长的音频剪下来
aduio_segment = path_segment + files[i][:-4] + '_' + str(index) + '.wav'
get_ms_part_wav(audio, start_time, time_all, aduio_segment)
print('音频切割完成!')
注释是不是超级详细哈哈!
切割完音频之后,接下来就是对切割好的多个短音频进行处理了。
试想一下,如果一个一个进行提取,如果原音频是时长30s,那么切割后的音频就有300个了,更何况我们往往需要处理大量得到音频。
所以这个时候就需要我们进行批处理啦!(其实也就是用了一下os这个大家都很熟悉的模块而已哈哈)
话不多说,上代码:
"""
在执行该代码之前,你可以选择将这份代码放在opensmile的初目录下。
或者是执行os.system('cd path') # 这里的path就是你opensmile的初目录
"""
import os
path = './audio27_segment/' # 待处理的音频路径
count = 0
for root,dir,files in os.walk(path):
for i in range(len(files)):
print(files[i])
print(count)
# path_remake(files[i])
try:
os.system('SMILExtract -C config/IS09_emotion.conf -I ' + 'audio27_segment/' + files[i] + ' -O ' + 'audio27_segment_fea/' + files[i][:-4] + '.csv')
except:
count += 1
print(count) # 出错次数,正常情况都是不会出错的。
当执行完以上操作之后,你也就基本完成了你的工作。
剩下最后一步,也就是将你真正需要用到的音频特征从csv文件中拿出来。
先介绍下这个生成的csv文件,这里以配置文件IS09_emotion.conf为例。
生成的csv文件中,第4~387行为特征名称,第392行一共386个元素,第一个元素为‘unknow’;最后一个元素为?;中间384个元素对应上面384个特征。
那如何将我们需要的这384个特征提取出来呢。
代码如下:
import os
import re
frome natsort import natsorted
# 先对文本进行操作
path2 = r'E:/audio27_segment_fea/' # 待处理的短音频的路径
arr = [[] for i in range(45)] # 45你可以随便改,只要大于你原长音频的数量就可以了!
index_num2 = []
for root, dir, files in os.walk(path2):
num = len(files)
files = natsorted(files)
for i in range(len(files)):
with open(root + files[i], 'r') as f:
content = f.readlines()
fea = content[-1]
fea1 = re.findall(r'\d+\.?\d*e?[-+]?\d+', fea)
fea1 = list(map(float, fea1))
index = int(re.findall(r'^(.*?)_', files[i])[0])
if index not in index_num2:
index_num2.append(index)
arr[index].append(fea1)
f_A1 = []
for i in arr:
if i != []:
f_A1.append(i)
# 除去有问题的音频特征(特征数为0)(一般都是原本长音频截取的最后一个短音频)
for index1, value1 in enumerate(f_A1):
for index2, value2 in enumerate(value1):
if len(value2) != 384:
print('holly shit!')
print(index1,end=': ')
print(index2,end=' ')
print(len(value2))
value1.pop(index2)
break
# 这里的f_A1 就是我们要的啦
这里给的代码很详细哈哈。
由于博主能力有限,博文中提及的信息,也难免会有疏漏之处。希望发现疏漏的朋友能热心指出其中的错误,以便下次修改时能以一个更完美更严谨的样子,呈现在大家面前。同时如果有更好的方法也请不吝赐教。
如果有什么相关的问题,也可以关注评论留下自己的问题,我会尽量及时发送!
然后如果这些内容对你有所帮助的话,麻烦点赞或者评论告诉我可以吗(这样我更新博客的速度才会越来越快!!!)