python音频 降噪_从视频中提取音频数据,然后应用傅里叶对音频降噪(python)...

视频准备

QQ有热键

20201007142826961.png

然后随便打开一个视频网站进行录屏

我选择B站

20201007143035351.png

从视频中提取音频

需要安装包moviepy

pip install moviepy

提取代码

from moviepy.editor import *

video = VideoFileClip('C:\\Users\\Shineion\\Desktop\\新建文件夹\\录屏.mp4')

audio = video.audio

audio.write_audiofile('C:\\Users\\Shineion\\Desktop\\新建文件夹\\录屏.wav')

结果:

20201007144717283.png

音频绘图

音频知识储备

音频采样率:

采样率与频率之间的关系,采样率越低,高频信息越少。CD的发行最低标准

是44.1kHz的采样率。采样率就是采样频率,采样频率是指录音设备在一秒钟内对声音信号的采样次数,采样频率越高声音的还原就越真实越自然。采样频率越高声音的还原就越真实越自然。

采样信号

是指模拟信号先由采样器按照一定时间间隔采样获得时间上的离散的值。

方法1 使用librosa提取音频,然后绘图

安装包

pip install librosa

音频绘图

import librosa

import numpy as np

import matplotlib.pyplot as plt

from pylab import *

mpl.rcParams['font.sans-serif'] = ['SimHei']

mpl.rcParams['axes.unicode_minus'] = False

audio, freq = librosa.load('C:\\Users\\Shineion\\Desktop\\新建文件夹\\录屏.wav')#audio 采样信号,freq采样率

time = np.arange(0, len(audio)) / freq

plt.plot(time, audio)

plt.show()

20201007154157605.png

方法2 使用scipy.io.wavfile提取音频,然后绘图

import numpy as np

import matplotlib.pyplot as plt

from pylab import *

mpl.rcParams['font.sans-serif'] = ['SimHei']

mpl.rcParams['axes.unicode_minus'] = False

import scipy.io.wavfile as wf

#读取音频文件,将其按照采样率离散化,返回采样率和信号

#sample_reate:采样率(每秒采样个数), sigs:每个采样位移值。

#================1.原始音频信号,时域信息=================================

sameple_rate,sigs = wf.read('C:\\Users\\Shineion\\Desktop\\新建文件夹\\录屏.wav')

print('采样率:{}'.format(sameple_rate))

print('信号点数量:{}'.format(sigs.size))

print('采样信号',sigs)

times = np.arange(len(sigs))/sameple_rate#时间=信号点数量/采样频率

plt.figure('Filter',facecolor='lightgray')

plt.title('时间域',fontsize=16)

plt.ylabel('信号',fontsize=12)

plt.grid(linestyle=':')

plt.plot(times,sigs,color='dodgerblue',label='Noised Signal')

plt.legend()

plt.show()

采样信号有两列:第一列直流分量,第二例非直流

20201007154503950.png

20201007154536282.png

数据太多,只看前100个的数据

plt.plot(times[:100],sigs[:100],color=‘dodgerblue’,label=‘Noised Signal’)

20201007154655781.png

应用傅里叶对音频进行降噪

算法知识储备

假设有一时间域函数:y = f(x),根据傅里叶理论它可以被分解为一系列正弦函数的叠加,这些正弦函数具有不同的振幅A,频率ω或初相位φ。:

20201007153344662.png

在信息处理过程中,通常处理步骤是:

1.通过傅里叶变换是将时域(即时间域)上的信号转变为频域(即频率域)上的信号,进行相应的滤波去噪、增强、统计分析等处理。

2.处理后的信号可以通过傅里叶逆变换在转换成时域信号,从而达到相应的信号处理效果。

频率域的概念

频率域是指从函数的频率角度出发分析函数,和频率域相对的是时间域。简单说就是如果从时间域分析信号时,时间是横坐标,振幅是纵坐标。而在频率域分析的时候则是频率是横坐标,振幅是纵坐标。

举个例子,我们认为音乐是一个随着时间变化的震动。但是如果站在频域的角度上来讲,音乐是一个随着频率变化的震动,这样我们站在时间域的角度去观察你会发现音乐是静止的。同理,如果我们站在时间域的角度观察频率域的世界,就会发现世界是静止的,也是永恒的。这是因为在频率域是没有时间的概念的,那么也就没有了随着时间变化着的世界了。

另外,我们需要借助傅立叶变换,才能够在得到函数在频率域中的信息。

傅里叶变换

回顾一下傅里叶变换吧,没弄清傅里叶变换为什么能得到信号各个频率成分的同学也可以再借我的图理解下。

傅里叶变换把无限长的三角函数作为基函数:

20201008103400528.png

这个基函数会伸缩,平移(其实是两个正交基的分解)。缩得窄,对应高频;伸得宽,对应低频。然后这个基函数不断和信号做相乘。某一个尺度(宽窄)下乘出来的结果,就可以理解成信号所包含的当前尺度对应频率成分有多少。于是,基函数会在某些尺度下,与信号相乘得到一个很大的值,因为此时二者有一种重合关系。那么我们就知道信号包含该频率的成分的多少。

20201008103442272.png

看,这两种尺度能乘出一个大的值(相关度高),所以信号包含较多的这两个频率成分,在频谱上这两个频率会出现两个峰。

当然这是傅里叶变换,傅里叶变换我们需要对数据进行处理,然后逆傅里叶变换才能得到我们想要的。

第一步:读取数据

import numpy as np

import matplotlib.pyplot as plt

from pylab import *

mpl.rcParams['font.sans-serif'] = ['SimHei']

mpl.rcParams['axes.unicode_minus'] = False

import scipy.io.wavfile as wf

#读取音频文件,将其按照采样率离散化,返回采样率和信号

#sample_reate:采样率(每秒采样个数), sigs:每个采样位移值。

#================1.原始音频信号,时域信息=================================

sameple_rate,sigs = wf.read('C:\\Users\\Shineion\\Desktop\\新建文件夹\\录屏.wav')

times = np.arange(len(sigs))/sameple_rate#时间=信号点数量/采样频率

第二步:绘制降噪前的频率振幅谱

ft_y=np.fft.fft(sigs)

print('ft_y',ft_y)

n = len(sigs)

#取得最大的振幅的二分之一

avg=np.max(abs(ft_y[1:]))/2

print('avg',avg)

plt.title("降噪前的频率振幅谱")

plt.plot(abs(ft_y))

plt.show()

ft_y 两列:第一列直流分量,第二例非直流

20201007155527678.png

20201007155554190.png

第三步:干掉小于最大振幅二分之一的信号

可以自己设置要干掉多少信号,不一定要是二分之一,直流是否要看自己

#ft_y[0]=0+0j#是否干掉直流

ft_y[np.where(abs(ft_y)<=avg)]=0+0j

逆傅里叶变换重新得到音频

plt.plot(abs(ft_y))

plt.title("降噪后的频率振幅谱")

plt.xlabel("频率")

plt.ylabel("amplitude")

plt.title("降噪后的音频")

plt.plot(np.fft.ifft(ft_y))

plt.show()

data=np.fft.ifft(ft_y)

data = data.astype('i2')#格式转换

wf.write('降噪.wav',sameple_rate,data)#保存

后面声音没啦,因为原始音频本来就很好,再处理会更糟糕。

该方法在时间-振幅 上 进行降噪

我觉得不是很好。

或者可能原始数据非周期性,所以效果不好。

该方法好像在周期性音频数据上也可以。

具体是什么原因,有空你自己去研究。

20201007163044231.png

对噪声数据进行处理

因为前文的例子是非周期性,是在时间-振幅 上 进行降噪

这里给出一个周期性的数据来进行讨论,并在频域-振幅上进行降噪。

音频数据。

链接:https://pan.baidu.com/s/1PLIR3ug3neLcSpJZS1gc-Q

提取码:2vxr

第一步:读取数据

import numpy as np

import numpy.fft as nf

import matplotlib.pyplot as plt

from pylab import *

mpl.rcParams['font.sans-serif'] = ['SimHei']

mpl.rcParams['axes.unicode_minus'] = False

import scipy.io.wavfile as wf

#读取音频文件,将其按照采样率离散化,返回采样率和信号

#sample_reate:采样率(每秒采样个数), sigs:每个采样位移值。

#原始音频信号,时域信息

sameple_rate,sigs = wf.read('C:\\Users\\Shineion\\Desktop\\新建文件夹\\noised.wav')

print('采样率:{}'.format(sameple_rate))

print('信号点数量:{}'.format(sigs.size))

times = np.arange(len(sigs))/sameple_rate

plt.figure('Filter',facecolor='lightgray')

plt.title('时间域',fontsize=16)

plt.ylabel('信号',fontsize=12)

plt.grid(linestyle=':')

plt.plot(times[:200],sigs[:200],color='dodgerblue',label='噪声信号 ')#查看前200个

plt.legend()

plt.show()

20201007165310257.png

2020100716533346.png

第二步:绘制频域图

fftfreq的说明:

在画频谱图的时候,要给出横坐标的数字频率,这里可以用fftfreq给出,对于fftfreq的说明如下:

scipy.fftpack.fftfreq(n, d=1.0)

第一个参数n是FFT的点数,一般取FFT之后的数据的长度(size)

第二个参数d是采样周期,其倒数就是采样频率Fs,即d=1/Fs

#基于傅里叶变换,获取音频频域信息

#绘制音频频域的: 频域/能量图像

freqs = nf.fftfreq(sigs.size, 1/sameple_rate)

complex_arry = nf.fft(sigs)

pows = np.abs(complex_arry)#取绝对值

plt.title('频率域',fontsize=16)

plt.ylabel('能量',fontsize=12)

plt.grid(linestyle=':')

plt.semilogy(freqs,pows,color='green',label='噪声 Freq')

plt.legend()

plt.show()

20201007170040666.png

第三步:去噪

fun_freq = freqs[pows.argmax()] #获取频率域中能量最高的#1000.0

print('fun_freq',fun_freq)

noised_idx = np.where(freqs != fun_freq)[0] #获取所有噪声的下标

print('noised_idx',noised_idx)

ca = complex_arry[:]#complex_arry为前面傅里叶变换得到的数组

ca[noised_idx] = 0 #高通滤波#将噪声全部设为0

filter_pows = np.abs(complex_arry)#过滤后的傅里叶变换数据,原始数据已被修改,用于化

filter_sigs = nf.ifft(ca)#逆傅里叶变换

plt.title('时域图',fontsize=16)

plt.ylabel('Signal',fontsize=12)

plt.grid(linestyle=':')

plt.plot(times[:200],filter_sigs[:200],color='red',label='降噪后的信号')

plt.legend()

plt.show()

20201007171058504.png

20201007171122895.png

第四步:绘制降噪后的频域能量图

plt.title('频率域',fontsize=16)

plt.ylabel('power',fontsize=12)

plt.grid(linestyle=':')

plt.plot(freqs,filter_pows,color='green',label='降噪后的频谱能量图')#filter_pows为降噪后的数据,尚未进行逆傅里叶变换

plt.legend()

plt.show()

20201007171453928.png

第五步:保存

filter_sigs = filter_sigs.astype('i2')#格式转换

wf.write('C:\\Users\\Shineion\\Desktop\\新建文件夹\\降噪.wav',sameple_rate,filter_sigs)

代码总结

说明 本文是只保留最高的频率,可以修改下面代码保留不同信息。

fun_freq = freqs[pows.argmax()] #获取频率域中能量最高的#1000.0

print(‘fun_freq’,fun_freq)

noised_idx = np.where(freqs != fun_freq)[0] #获取所有噪声的下标

print(‘noised_idx’,noised_idx)

如可以找到最大的几个高频索引,然后保留。

下面为寻找最大几个值索引的方法

import heapq

nums = [1, 8, 2, 23, 7, -4, 18, 23, 24, 37, 2]

# 最大的3个数的索引

max_num_index_list = map(nums.index, heapq.nlargest(3, nums))

# 最小的3个数的索引

min_num_index_list = map(nums.index, heapq.nsmallest(3, nums))

print(list(max_num_index_list))

print(list(min_num_index_list))

20201007174230920.png

总结思路:傅里叶变换,然后绘制频域振幅图,找到高频分量的频率,过滤掉低频,最后逆傅里叶变换。

对于非周期性的数据,有待考量。比较我也没接触过。

参考文献

毕竟第一次接触音频,不知道音频是哈,所以参考啦一下,

感谢大佬,感谢大佬,感谢大佬。

参考他人,让我彻底的感受啦下其中的魅力和门道。

估计就叫这篇博文为学习笔记吧。

文献1:https://blog.csdn.net/a1040193597/article/details/99598173

文献2:https://blog.csdn.net/u014554395/article/details/100922534

算法总结

通过此文

我个人认为傅里叶只能处理偏周期性的,

如果是非周期性信号,可以采用短时傅里叶变换,即将信号分解成很多段,每段分别做傅里叶变换。但有那个功夫,还不如选择小波变换。

也可以应用小波变换进行降噪

代码可以参考我另一篇博文

小波降噪与重构例子 python

等我后面真正涉足音频时再学习其他方法吧。

20201007175117624.jpg#pic_center

电气工程的计算机萌新:余登武。

写博文不容易,如果你觉得本文对你有用,请点个赞支持下,谢谢。

20201007175310626.gif#pic_center

你可能感兴趣的