首页 > 分享 > 声音分类及其实战(一)

声音分类及其实战(一)

目录 前言音频简介WAV是什么?采样率、位深 声音处理以及可视化

前言

玩过CV的都知道猫狗识别,通过输入一张猫狗图片之后经过神经网络就能知道这张图片属于猫还是狗,图像识别的输入是很直观的,这源于我们对图像理性的认识(RGB图像有三个通道,每个通道中每个像素点都是一个数字…)。但声音显然是一个朦胧的概念,声音是如何实现分类的呢?CV有人脸识别,声音也存在声纹识别,这些在人工智能里面都是如何判断的,让我们从一篇简单的声音分类开始吧!

音频简介

现在正式开启语音的学习历程,语音识别下面有很多任务,例如中文识别、文字转语音、语音唤醒以及说话人识别(声纹识别)等。我观察了很多demo,他们的语音识别所用的语音数据集文件基本上都是.wav文件,就算原始语音不是wav也会转为wav,所以我们首先了解一下wav文件是什么,以及其中的组成。

WAV是什么?

WAV格式是微软公司开发的一种声音文件格式,也叫波形声音文件,是最早的数字音频格式,被Windows平台及其应用程序广泛支持。WAV格式支持许多压缩算法,支持多种音频位数、采样频率和声道,采用44.1kHz的采样频率,16位量化位数,因此WAV的音质与CD相差无几,通常被称为无损音频。

采样率、位深

对于音频数据,比较重要的两个概率就是采样率和位深了,在此简单介绍一下这两个概念,以对声音的产生有一个直观的感受。先对他们有一个宏观的认识:声波,有频率和振幅,频率高低决定音调,振幅大小决定响度,采样率是对频率采样,位深是对振幅采样。

音频信号是一种连续的模拟信号,计算机不可能处理这种连续的模拟数据。它首先需要转换为一系列离散值,而“采样”就是这样做的。“采样率”和“位深”是离散化音频信号时最重要的两个要素。在下图中,我们可以看到它们与模数转换的关系。在图中,x 轴是时间,y 轴是幅度。“采样率”决定采样的频率,“位深度”决定采样的详细程度。
在这里插入图片描述
因此采样率是指:声音信号在“模→数”转换过程中单位时间内采样的次数,也就是每秒钟所采样样本的总数目是采样率。而每个样本中信息的比特数就是位深。

可能还是比较抽象,此处借助一个具体的例子来解释:

经常见到这样的描述: 44100HZ 16bit stereo 或者 22050HZ 8bit mono 等等.
44100HZ 16bit stereo: 每秒钟有 44100 次采样(采样率), 采样数据(即位深)用 16 位(2字节)记录, 双声道;
22050HZ 8bit mono: 每秒钟有 22050 次采样, 采样数据用 8 位(1字节)记录, 单声道;

每个采样数据记录的是振幅, 采样精度取决于储存空间的大小:

1 字节(也就是8bit) 只能记录 256 个数, 也就是只能将振幅划分成 256 个等级;2 字节(也就是16bit) 可以细到 65536 个数, 这已是 CD 标准了;4 字节(也就是32bit) 能把振幅细分到 4294967296 个等级, 实在是没必要了;

如果是双声道(stereo), 采样就是双份的, 文件也差不多要大一倍。

人对频率的识别范围是 20HZ - 20000HZ, 如果每秒钟能对声音做 20000 个采样, 回放时就足可以满足人耳的需求。 所以 22050 的采样频率是常用的, 44100已是CD音质, 超过48000的采样对人耳已经没有意义。这一点在后面声音处理时会用到。

声音处理以及可视化

上面已经简单介绍了声音的基本知识,那么就来到我们的声音处理环节,可视化声音的波纹。首先引入我使用的第一个数据集:UrbanSound8K,可以点此处直接下载。如果使用的是服务器,可以使用wget下载解压,命令如下:

wget https://zenodo.org/record/1203745/files/UrbanSound8K.tar.gz 1

下载完成之后解压到指定文件夹:

tar -zxvf UrbanSound8K.tar.gz -C /media/data/xxx/Speech_Recognition/ 1

首先在Python中元数据的格式,其是一个csv文件,包含了所有声音文件的信息:

import pandas as pd data = pd.read_csv("../Dataset/UrbanSound8K/metadata/UrbanSound8K.csv") print(data) print(data.head()) print(data.shape) 123456

结果为:

slice_file_name fsID start ... fold classID class 0 100032-3-0-0.wav 100032 0.000000 ... 5 3 dog_bark 1 100263-2-0-117.wav 100263 58.500000 ... 5 2 children_playing 2 100263-2-0-121.wav 100263 60.500000 ... 5 2 children_playing 3 100263-2-0-126.wav 100263 63.000000 ... 5 2 children_playing 4 100263-2-0-137.wav 100263 68.500000 ... 5 2 children_playing ... ... ... ... ... ... ... ... 8727 99812-1-2-0.wav 99812 159.522205 ... 7 1 car_horn 8728 99812-1-3-0.wav 99812 181.142431 ... 7 1 car_horn 8729 99812-1-4-0.wav 99812 242.691902 ... 7 1 car_horn 8730 99812-1-5-0.wav 99812 253.209850 ... 7 1 car_horn 8731 99812-1-6-0.wav 99812 332.289233 ... 7 1 car_horn [8732 rows x 8 columns] slice_file_name fsID start ... fold classID class 0 100032-3-0-0.wav 100032 0.0 ... 5 3 dog_bark 1 100263-2-0-117.wav 100263 58.5 ... 5 2 children_playing 2 100263-2-0-121.wav 100263 60.5 ... 5 2 children_playing 3 100263-2-0-126.wav 100263 63.0 ... 5 2 children_playing 4 100263-2-0-137.wav 100263 68.5 ... 5 2 children_playing [5 rows x 8 columns] (8732, 8)

1234567891011121314151617181920212223 slice_file_name:音频文件的名称fsID:摘录的录音的 FreesoundIDstart:切片的开始时间end:切片的结束时间salience:声音的显着性等级。1 = 前景,2 = 背景fold:此文件已分配到的折叠编号(1-10)classID:
0 = air_conditioner
1 = car_horn
2 = children_playing
3 = dog_bark
4 = drilling
5 = engine_idling
6 = gun_shot
7 = jackhammer
8 = siren
9 = street_musicclass:类别名称

接下来来看看数据的分布情况:

appended = [] for i in range(1, 11): appended.append(data[data.fold == i]['class'].value_counts()) class_distribution = pd.DataFrame(appended) class_distribution = class_distribution.reset_index() class_distribution['index'] = ["fold" + str(x) for x in range(1, 11)] print(class_distribution) 1234567

结果为:

index jackhammer children_playing ... siren car_horn gun_shot 0 fold1 120 100 ... 86 36 35 1 fold2 120 100 ... 91 42 35 2 fold3 120 100 ... 119 43 36 3 fold4 120 100 ... 166 59 38 4 fold5 120 100 ... 71 98 40 5 fold6 68 100 ... 74 28 46 6 fold7 76 100 ... 77 28 51 7 fold8 78 100 ... 80 30 30 8 fold9 82 100 ... 82 32 31 9 fold10 96 100 ... 83 33 32 1234567891011

通过最后两列可以发现,数据的分布是不平衡的。更直观的观察是每类的频率:

print(data['class'].value_counts(normalize=True)) 1

其结果为:

[10 rows x 11 columns] dog_bark 0.114521 children_playing 0.114521 street_music 0.114521 engine_idling 0.114521 drilling 0.114521 jackhammer 0.114521 air_conditioner 0.114521 siren 0.106390 car_horn 0.049130 gun_shot 0.042831 Name: class, dtype: float64 123456789101112

以上就是数据的具体信息,那么接下来就是对wav文件的详细分解。

首先可视化一个声音文件:

import os import struct import pandas as pd from scipy.io import wavfile as wav import matplotlib.pyplot as plt data = pd.read_csv("../Dataset/UrbanSound8K/metadata/UrbanSound8K.csv") def path_class(filename): excerpt = data[data['slice_file_name'] == filename] path_name = os.path.join('../Dataset/UrbanSound8K/audio', 'fold' + str(excerpt.fold.values[0]), filename) return path_name, excerpt['class'].values[0] def wav_plotter(full_path, class_label): rate, wav_sample = wav.read(full_path) print(wav_sample) wave_file = open(full_path, "rb") riff_fmt = wave_file.read(36) bit_depth_string = riff_fmt[-2:] bit_depth = struct.unpack("H", bit_depth_string)[0] print('sampling rate: ', rate, 'Hz') print('bit depth: ', bit_depth) print('number of channels: ', wav_sample.shape[1]) print('duration: ', wav_sample.shape[0] / rate, ' second') print('number of samples: ', len(wav_sample)) print('class: ', class_label) plt.figure(figsize=(12, 4)) plt.plot(wav_sample) plt.show() fullpath, label = path_class('100263-2-0-117.wav') wav_plotter(fullpath, label)

123456789101112131415161718192021222324252627282930313233

可视化结果:

在这里插入图片描述
那么从这幅图中我们可以看到采样数据大约17600个,而这段音频我已经知道大约四秒左右,也就是说这段音频的采样率应该是44100HZ,而振幅很显然是大于2000的,那么其位深就可以猜测为16bit(32bit很少),并且我们可以看到图中有两种颜色的图,也就对应了双声道,是一个立体声。

那么结果是否准确,我们可以通过上面的代码获得具体结果:

sampling rate: 44100 Hz bit depth: 16 number of channels: 2 duration: 4.0 second number of samples: 176400 class: children_playing 123456

但此处有一个问题,就是Urbansound8K有一个注释说“8732个WAV格式的城市声音音频文件。采样率、位深度和通道数与上传到 Freesound 的原始文件相同(因此可能因文件而异)。”这意味着数据中可能有许多不同的采样率。此外,不同的位深度意味着它们可以取不同的值范围。其中一些可能是立体声,而另一些则是单声道。这些都很难处理。

那么接下来我们就可以统计这个数据集的每种采样率,位深共包含多少声音文件,在统计之前,我们首先需要了解一下wav文件的组成, 一幅图概括:
在这里插入图片描述
那么从这幅图可以很清晰的看出声道数量,采样频率,位深(采样位数)所对应的字段,接下来就可以获取具体的信息:

def wav_fmt_parser(file_name): full_path, _ = path_class(file_name) wave_file = open(full_path, "rb") riff_fmt = wave_file.read(36) #只读取前36位信息 n_channels_string = riff_fmt[22:24] # 通道数 n_channels = struct.unpack("H", n_channels_string)[0] s_rate_string = riff_fmt[24:28] # 采样率 s_rate = struct.unpack("I", s_rate_string)[0] bit_depth_string = riff_fmt[-2:] # 倒数两位即位深 bit_depth = struct.unpack("H", bit_depth_string)[0] return n_channels, s_rate, bit_depth 1234567891011

接下来就是遍历所有声音文件进行信息的捕捉:

wav_fmt_data = [wav_fmt_parser(i) for i in data.slice_file_name] 1

这样获得的信息全在一个元组里面,数据格式为:

(2, 44100, 16), (2, 44100, 16), (2, 44100, 16), (2, 44100, 16), (2, 44100, 16) 1

将其利用pandas加入到data原始数据:

data[['n_channels', 'sampling_rate', 'bit_depth']] = pd.DataFrame(wav_fmt_data) 1

此时得到:

slice_file_name fsID ... sampling_rate bit_depth 0 100032-3-0-0.wav 100032 ... 44100 16 1 100263-2-0-117.wav 100263 ... 44100 16 2 100263-2-0-121.wav 100263 ... 44100 16 3 100263-2-0-126.wav 100263 ... 44100 16 4 100263-2-0-137.wav 100263 ... 44100 16 ... ... ... ... ... ... 8727 99812-1-2-0.wav 99812 ... 44100 16 8728 99812-1-3-0.wav 99812 ... 44100 16 8729 99812-1-4-0.wav 99812 ... 44100 16 8730 99812-1-5-0.wav 99812 ... 44100 16 8731 99812-1-6-0.wav 99812 ... 44100 16 123456789101112

注意看后几列,之后统计每个采样率的音频数量:

print(data.sampling_rate.value_counts()) print(data.n_channels.value_counts()) print(data.bit_depth.value_counts()) 123

结果为:

[8732 rows x 11 columns] 44100 5370 48000 2502 96000 610 24000 82 16000 45 22050 44 11025 39 192000 17 8000 12 11024 7 32000 4 Name: sampling_rate, dtype: int64 2 7993 1 739 Name: n_channels, dtype: int64 16 5758 24 2753 32 169 8 43 4 9 Name: bit_depth, dtype: int64

12345678910111213141516171819202122

可以发现数据的格式还是非常混乱的,如果不做归一化处理可能无法直接使用。那么接下来就是对数据进行转换以获得可以在同一情况下直接比对的数据。所幸万能的Python有着一种直接的方法对数据进行转换,名为:Librosa。

默认情况下,Librosa 的加载函数会将采样率转换为 22.05khz,并将通道数减少到 1(单声道),并对数据进行归一化,使值范围从 -1 到 1。

fullpath, class_name = path_class('100652-3-0-1.wav') librosa_load, librosa_sampling_rate = librosa.load(fullpath) print('converted sample rate:', librosa_sampling_rate) print('converted wav file min~max range:', np.min(librosa_load), '~', np.max(librosa_load)) 1234

此时结果为:

converted sample rate: 22050 converted wav file min~max range: -0.7296108 ~ 0.74331266 12

而原始结果:

scipy_sampling_rate, scipy_load = wav.read(fullpath) print('original sample rate:', scipy_sampling_rate) print('original wav file min~max range:', np.min(scipy_load), '~', np.max(scipy_load)) 123

original sample rate: 44100 original wav file min~max range: -30926 ~ 30119 12

我们可以将转换后的数据和原始数据的左右声道都绘制出来:

plt.subplot(3, 1, 1) plt.plot(librosa_load) plt.subplot(3, 1, 2) plt.plot(scipy_load[:, 0]) plt.subplot(3, 1, 3) plt.plot(scipy_load[:, 1]) plt.show() 1234567

可以看到结果图为:
在这里插入图片描述
从这幅图中我们可以粗略看出,该工具很好的将原始数据在不损失其质量的前提下转化成功。OK,本节结束,对此声音的特征提取和网络的分类可以见下节。

参考博客:

https://www.cnblogs.com/ranson7zop/p/7657874.htmlhttps://blog.csdn.net/cindywry/article/details/108244610https://towardsdatascience.com/urban-sound-classification-part-2-sample-rate-conversion-librosa-ba7bc88f209a

相关知识

动物声音情绪识别系统及其方法
鸟类分类、鸟类声音相关深度学习数据集大合集
广州宠物美容师助理岗位实战培训班
实战追踪基础知识分享
基于卷积神经网络通过声音识别动物情绪的方法及系统
数码单反相机新手攻略宠物摄影实战(上)
数码单反相机新手攻略宠物摄影实战(上)(2)
数码单反相机新手攻略宠物摄影实战(下)
影视后期音频基础(一)-电影声音后期制作流程
宠物声音识别与理解研究.pptx

网址: 声音分类及其实战(一) https://m.mcbbbk.com/newsview171135.html

所属分类:萌宠日常
上一篇: AI动物语音模拟软件免费:支持多
下一篇: 人类在模仿猫狗叫声的时候,它们能