傅里叶变换,一度被推上了玄学的位置。
这位说,了解了它,能改变你,认识世界的方式。那个说,我喜欢信号系统,但是看了2周的傅里叶变换,放弃了。
也有很多人,写了科普教程,有零基础教程、保证看懂教程、掐死教程。但是,当他们亮出数学公式的那一刻,很多人陷入沉思:我……是从哪里开始,就已经看不懂了……
这些现象,激发了我强烈的表演欲望,我要登上舞台,为大家表演。我的表演,如果你看不懂,请掐死我。
二、写博客:傅里叶变换进行时
我正写博客,没有骗你,真的在写,有图为证(是的,用vscode写博客第一人)。
此时正是深夜,我屋内的电脑,发出“嗡嗡”的散热声。同时,窗外的马路,飞驰而过一辆渣土车,发出“嘀~”一声刺耳的鸣笛。
我,是如何,听到,声音的。
物体振动发出的声音,在空气中传播,进入我的耳朵,推动我的耳膜,声音大就推的强,声音小就推的弱,经过一系列处理,我就听到了电脑和汽车。
仔细想一下,有点奇怪,我居然根据耳膜被推动的情况,分析出了声音里,包含电脑散热风扇和汽车喇叭两种物体。其实除此之外,我还听到了楼上放电视、窗外的虫子叫、电动车报警器响了……
这个声音发出和接收的场景,就如同你用手指在桌子上有规律、有轻重地敲击。我相信,你能听出这番敲击是打快板,那番敲击是非洲鼓。但是,你很难通过敲桌子的节奏,听出来:你的同事在敲键盘,同时路上响起了救护车的声音。
先给你3分钟的时间思考一下,我去上个厕所先。
人类几万年的历史,不知道是否有人想过这个问题,拿根棍子捅你的耳膜,你就能分辨出十几样不同的发声物体,这到底该怎么来解释呢?
其实,这——就是傅里叶变换(我能让你猜着,这突然地转场?)。
如下图所示:推动你耳膜的力度就是左侧的波形,你的大脑经过傅里叶变换,分析出了十几种不同的发声物体。
好了,现在拿出傅里叶变换的定义:
傅里叶变换,表示能将满足一定条件的某个函数表示成三角函数(正弦和/或余弦函数)或者它们的积分的线性组合。
定义看不懂没关系,只要能看懂前面说的,分辨声音的那个例就行。
关于什么是傅里叶,此处打个标记叫[A]。如果你看不懂,就说从[A]开始就糊涂了,来掐我时,好让我死得明白。
但是,有一点谈资我们要知道,这个理论是法国人傅里叶在1807年提出来的。那时,嘉庆皇帝刚抄了和珅的家没过几年。
三、描述一个波:都有独特的气质
你糊弄我?你拿人这种有灵性的生物,来举数学的例子,数学公式能解决道德问题吗?
再说,声音混在一起,就如同染料混在一个缸里,你还能分出来?我就不信了!
我感觉有人跟我对话,充满了质疑和不屑。
那好,我换一个例子,收音机大家都知道吧。
全国几百家广播台,各家都往外发送无线电波。
我们并非生活在多个平行宇宙。因此,这些电波没有VIP通道,肯定都是混在一起在空中传播的。就像下面这样:
那……收音机,可以收听指定电台的节目。
这个是机器,它的收音设备,没有你的耳朵那么复杂,也没有神经元,这一点儿灵性也没有,但它是可以分解指定波段的。
好了,继续看我表演,说一下声音的两个属性:频率、振幅。
3.1 声音的频率
声音是由振动产生的,振动一般都是……就……就像这样:
一个点围绕着某个相对固定的中心,周期性地移动。
比如,一个弹簧吊着一个小球,上下振动。
如果,一边记录时间,一边记录位置,其实它的形状就是弦曲线(我保证这个词,已是本文最专业的术语),也可以叫波。
有些物体振动的快,1秒钟反复来回1000次。有些物体振动的慢,1秒钟才往返1个周期。
这个单位时间内振动一个周期的次数,我们就叫
频率
。
因为材质不同,不同的物体振动的频率是不一样,波形也不一样。
下面是物理课上,敲击音叉振动的声波。
下面是用嘴吹试管的声波。
只要我们听到的声音不一样,基本上它们声波的频率也是不一样的。
就算都是口琴,低音区是这样的:
高音区是这样的:
我们发现,高音区比较密集,相同单位内振动的次数更多,也就是频率大。反之,低音区频率小,比较稀疏。
你听到的每种不同类型声音,都有自己固定的频率,就像每个地方菜的口味一样,是有差别的。此处打个标记叫[B],如果你看不懂,就说从[B]开始,就乱了,来告诉我。
3.2 声音的振幅
声音的振幅就是声音的大小。大小和频率没有任何关系。
频率是某个时间内,推了耳膜多少次。振幅是这次推动,花了多少力气。
还是拿音叉举例子,声音的振幅只影响音量,波形、频率都不变。你敲出原子弹爆炸的音量,它也是那个形状。
声音大时,音叉的波形:
声音小时,音叉的波形:
这很好理解,不管我小声说话,还是大声说话,你都能听出是我。但是,我大声说话,你会震得耳朵疼(耳膜遭受一记重拳)。
再来点儿谈资吧。我们人类能听到的频率范围是20Hz(1秒钟振动20个周期)到20000Hz。超过20000Hz为超声波,低于20Hz为次声波。我们常说,蝙蝠发出的超声波,不是因为它声音太小或太大,导致我们听不见。其实是因为它振动得太快,蝙蝠已经说了5000字了,但是我们只收到一个“的”。这是没有意义的。同样,我们人类能发出的声音频率也是有范围的,范围在250Hz-4000Hz之间,这是由我们人类声带的肉质(额……不知道为啥突然出来这个词)决定的。
振幅和频率的区别,此处打个标记叫[C]。
如果你了解了,频率和振幅这两个波的属性,后面就好说了。
四、说傅里叶变换吧:我不抗拒了!
傅里叶发现,这个世界上的人,对事物的理解不透彻,描述的也不科学。
世人都以时间为轴线,来描述事物。比如,描述一段1分钟的录音:第1秒说了什么,第2秒说了什么……。
世人摇摇头,叹了口气:傅里叶,你想咋着?
4.1 时域
傅里叶说,以时间作为参考的描述,叫时域分析。
比如下面这图,这是个时域图。我们看到这1秒内,它很忙,来来回回,从0到1秒跑着,从-1跑到1,反复了5次。所以,这段波的频率是5,最大振幅是1。整图描述的是,每一个时刻的信号值:
如果这段波,不是1秒,而是1个小时,那么它就是3600个这样的图拼在一起。
时域的概念,打个标记叫[D]。不懂的话,就看看表,再看看路上的汽车,刚才在你左边,1分钟后到了你的右边。2分钟后,它还在原地等红绿灯,每个时间都有对应的位置。这也是我们普通人认识世界的方式。
4.2 频域
还是那段波,而如果用频域来描述,那就是这样的:
横坐标表示频率,纵坐标表示振幅
。上面这个图表示:这里面有一段波,频率为5,振幅为1。
傅里叶告诉世人,我这个频域,可以抵得过时域的千年万年。两者说的是同一件事,只是分析的角度和描述手段不一样。
呵呵哈……哈呵呵,傅里叶冷笑着离开了,他连笑,都带着波形。
频域,打个标记叫[E]。
4.3 波的合成和分解
世界上的波,不会是上面说的那么单纯,实际上是很复杂的。
波,根据固定的频率振动,原本很单纯。只是,大家都在同一个空间内振动,相互之间发生了抵消和促进,相互融合,进行叠加,导致它很复杂。
波的叠加,打个标记叫[F]。不明白的话,赶紧评论区输入[F],让我知道,我会加强细节描述。
既然能叠加,那么能拆解吗?
其实,是可以的。不只是听觉,人类的味觉也做到了,你吃一口菜,可以尝出来里面有糖,有醋,有辣椒。它的理论基础就是,味觉具有一定的标准,辣就是辣,混到哪里都是辣。
波也一样,大家都是振动,震动就是弦曲线,只是频率不一样。
我们可以通过转啊转,各种组合,你护拢我,我护拢你,总能拼起来。
规律的波,并非一定得是“U”形,下面这几种情况,也都是规律的波形。
甚至,下面这个形状,也是规律的波形。可以拆解为若干组波的叠加。
二维空间里没有规律,就去从三维空间里面找。即便实在无法分解,我们也可以认为它的周期无限长,我有规律,只是本次循环还没到头呢?着啥急!
波的分解,不用明白具体是怎么分的。知道能分就行,打个标记叫[G]。
4.4 时域拆解频域
一段复杂的波,可以分解成,多段,规律的单纯波的集合。
然后,对这些规律的波从频域进行描述,就有了整段波的谱线图。
下图是一个综述。
f
指的是频率维度。这张图,很清晰地说明了波形、时域和频域的关系。很多教程,开局就拿出这张图,可解万难。但是,我说了这么久,才敢亮出来。
此图打个标记叫[H],还不明白来掐我。
4.5 频域复原时域
根据频域,也能复原成时域(此处用理想波,便于理解概念,还有相位等问题,不敢具体展开了)。
上面图里绿色的频率和振幅,可以描述一个波。如果把谱线上描述的波,依次画出来,然后做叠加。这样就复原了时域的波形。这个过程就像是泡发木耳。
波的复原,打个标记叫[I],有疑问请评论(别的平台复制本文可能不回复,但是掘金TF男孩一定给你回复,且回复的比你问的还要多)。
五、傅里叶变换有什么用?
傅里叶变换,在生物领域,你已经享受到它的益处了,你能分辨出一段声音里不同的声源,一口食物里不同的味道。
除此之外,我们可以拿时域转频域,对频域的特征进行二次加工,然后再恢复成时域,以此来做文章。
比如那些演唱会上调节声卡的人,如果你高音不足,可以给你升上去,低音太高,可以给你降下来。
举个例子,比如变声软件。把一段音频,分离出男声和女声,将男声改为女声的频率,然后还原回去,实现男声变女声。
再比如,声音压缩。将一些低频波形,合成一条。如下图蓝色线条,可以替代其下方的其他多条波形,而又不会影响人们的听觉判断,以此实现了数据的压缩。
除了在音频和信号的应用之外,在视觉上也有广泛应用,比如轮廓提取、美颜磨皮等等。
甚至,我还有一个想法,研发一款不会颠簸的汽车:
应用部分,如需讨论,打个标记叫[J]。
六、代码实践
如果,你已掌握了上面说的傅里叶变换。那么,下面,我们就要实践一下了。
实践的目的在于应用,表示我们已经可以利用代码,进行傅里叶变换了。这可以加深理解,并储备知识。
以下代码,采用python语言演示。
6.1 生成波形
傅里叶变换,是对波形做变换。因此,要变换我们首先得有一段波形。比如这一段音频,这是单词“stop”的声音波形(3个小高峰,s-tɒ-p):
信息的读取和绘制都很简单:
from scipy.io import wavfile
import matplotlib.pyplot as plt
fs, audio_wave = wavfile.read("stop.wav")
plt.plot(audio_wave)
plt.show()
这样看的不直观,我们选取开头的160个点,plt.plot(audio_wave[0:160])
来绘制这一小段,这样看就是线条的波形了:
从总图看,开头那部分是一马平川。但是,当我们放大这部分细节时,我们看到的却是满眼的崎岖和坎坷。
我们看到上面的wavfile.read
函数返回两个值,一个叫采样率,一个叫音频数据。
声音的读取和波形绘制,此处打个标记叫[K]。
6.1.1 采样率
采样率就是采样的频率,描述多长时间记录一次数据。
还记得打点计时器吗?
那个在纸上打点的频率,就叫采样率。
比如,1秒钟打60个点,采样率就是60。
60的采样率,能记录上信息吗?
这得看对方信号有多快。
如果对方太慢,会导致一个地方频繁打点,其实打一个点和打100个点没有区别,这很浪费。
如果对方太快,可能会记录不全,甚至它跑了好多周期了,我们一个点都采不上。蝙蝠的超声波就是频率太快了,我们的耳膜采样不上。
下图是采样率为1000和100时,记录的图:
可见,信号不复杂时,采样率高和超高,没有什么变化,都能记录全了。区别只是,用1万个点来描述,还是用100个点来描述。
下图是采样率为20时,绘制的同一段信号的图:
采样率一旦比信号低(小于信号最高频的2倍)了,有些信息的细节就记录不上了。
对于录音机,我们说,它是否能把声音的细节记录上,主要看它的采样率高不高。
关于采样率,此处打个标记叫[L],你是否get全了呢?
为什么要了解这些概念?因为我们要制造声波数据,需要先了解它。
为什么要造数据(这自问自答,我也是不要脸了)?因为我们造的数据,是我们已知的参数,便于我们和求出的结果做对照。
要造数据,还需要了解弦函数sin(θ)
。
就是横轴上一个点,对应纵轴上一个值,把它们画出来,就是一个波:
6.1.2 绘制波形
现在,我们来搞一系列的横纵坐标点。
import numpy as np
Fs = 1000
T = 1/Fs
L = 1200
t = np.arange(L)*T
S = np.sin(2*np.pi*t)
上面代码,t是横轴坐标,S是纵轴坐标。我们先来看1秒内发生的故事,因为我们设置的是1秒一个周期,其他时间段都是循环的。
我们的采样率是1000,也就是1秒记录1000个点。
1秒内,t的值是[0.000,0.001,0.002,……,1.0]
,把一个2π(完整周期)分成了1000份。对这1000个点求函数的值,也就是S = [0,…0.5,…1,…0,…-0.5,…-1,…,0]
。
如果用代码画出来的话。
import matplotlib.pyplot as plt
plt.plot(t[:1000], S[:1000])
plt.show()
数据就是下图所示:
上面的例子中,1秒进行了一个周期,频率是1。
如果,我们要一个频率为5的波形,那又该怎么整呢?
很简单,让S = np.sin(2*np.pi*t*5)
,里面乘以5,再画出来,就是下图这样:
如果想要一个频率为8的波形呢?乘以8就可以了!
关于波形数据的生成和绘制,此处打个标记叫[M]。如果大脑过载的话,要及时上报啊。
下面,我们准备了一组复合波数据,作为测试样本。
这批样本,其实就是一段频率为5的波,叠加一段频率为20的波。
其中有点插曲(故意设计的),这个频率为20的波,他的振幅为0.5(频率5的振幅是1),波形是这样的。
两段波相加,混合起来是这样的:
S_5 = np.sin(2*np.pi*5*t)
S_20 = 0.5*np.sin(2*np.pi*20*t)
S_25 = S_5 + S_20
好了,这个就是我们自己造的测试数据。
下面,我们来做傅里叶变换,看看能不能分析出来它的组成。
关于波的叠加,此处打个标记叫[N]。
6.2 傅里叶变换 FFT
今天,我们是用傅里叶变换,不是写傅里叶变换。所以过程会很简单,简单到就3个字母fft
。
python库scipy.fftpack
中有fft
,它就是专门处理傅里叶变换的。
from scipy.fftpack import fft
Y = fft(S_25)
p2 = np.abs(Y)
p1 = np.abs(Y)[:int(L/2)]
f = np.arange(int(L/2))*Fs/L
plt.plot(f,2*p1/L)
plt.show()
采用傅里叶变换,对测试波形进行分析,得出了这个谱线。
从图上我们看出,整段波里,存在2个频率的波形,一个频率为5,振幅为1;另一个频率为20,振幅是0.5(刚才就是按照这个规则造的假数据)。
这说明,分析的很对。
此时,我们可以玩耍的就多了。比如增大高频的频率,或者把低频的删除,然后再还原为波形,就做到了对声音的增强、提取和调整。
关于谱线,能看懂就行,此处打个标记叫[O],看不懂来掐我。
等会……这里面存在一个问题,那就是:这个分析结果,把时间维度给忽略了。
然而,时间是一个很重要的参考。
上面演示的是S20和S5叠加的情况。如果要是S20追加S5呢?意思就是有先后顺序,就像这样:
S_20_5 = np.append(S_20, S_5)
这段波形,先是一段小声(振幅小)的高频(频率大),然后是一段大声(振幅大)的低频(频率小)。
我分析啊,应该先是女生小声嘀咕,然后男生大声呵斥。这个时间顺序里面,是包含着故事的。
波形分析里没有时间维度,就相当于食品说明里,只有组成成分,没有含量占比。这,没法判断它的口味和营养。
于是,短时傅里叶变换就出现了。
6.3 短时傅里叶变换 STFT
本文的毕业考核,就是最终能看懂下面这张图。
不着急,我慢慢说。
为了加上时间(序列)维度,才有了短时傅里叶。
其实,它的本质还是傅里叶,只是它从时域信号中,依次选取一段样本,进行傅里叶分析。分析完了,再合并起来,这样就有了时间维度了。
短时傅里叶变换的调用依然很简单,比傅里叶变换多一个字母,它是stft
。传入的参数是要分析的波形数据,以及采样频率,返回值是:时间t、频率f、振幅Z。
from scipy.signal import stft
f, t, Z = stft(S_20_5, fs=1000)
z = np.abs(Z)
plt.figure(figsize=(10,10))
plt.xlabel("author:handroid@126.com Time(s)")
plt.ylabel("frequency")
plt.ylim(ymin=0, ymax= 30)
plt.title("from https://juejin.cn/user/615370768790158")
c = plt.pcolormesh(t, f, z, cmap ='Greens', vmin = 0, vmax = 1)
plt.colorbar(c)
由短时傅里叶变换得出数据生成的这个图,叫三维频谱图。
相比于谱线图,它加入了时间维度。
其中,横坐标是时间维度,这个时间可以不和波形图的时间对应。它和上面的fs
采样点参数有关。纵坐标,则表示波形的频率。它虽然是一幅2维图,但可以表示3维的数据。
他的第三维度就是色块的色值,表示振幅。色值越亮,振幅越大。
如何来分析这个图:
从纵坐标入手:这段信号,包含2段频率,一段频率是20,另一段频率是5。其中20频率段振幅较小,5频率段的振幅较大。
从横坐标入手:1.2s之前,只有高频信号;1.2s之后,高频消失,出现了低频信号,低频音量较大。
从振幅入手:声音较大(色值较重)的主要出现在后半段,且是低频信号发出,推测是男生发火了。
关于三维频谱图的解读,此处打个标记叫[P],看不懂就告诉我。
好了,试试毕业考试吧,看下面这幅频谱图。这是一段音乐,上面两个波形是左右声道,下面火红的颜色是频谱,右侧坐标是音调(频率),比如C调,G5调等。
这段音乐,哪段音域最活跃呢?这个歌手是女高音,还是男低音?
七、保命条款
朋友们,我讲的很初级,真的,一点都不谦虚。没讲相位,没讲函数的每一个参数,能省就省。这只是为了科普,让大家知道有这么一个东西,不但要知道,最好能吸收进去一些知识。学到的知识才是自己的。
本文的目标读者,就是那些连sin
和cos
都还给初中数学老师的朋友们。他们真的找不到通俗的资料去学习,但是他们又有强烈的求知欲,尽管很多医生……不是,很多研究生,大呼他们已经尽力了。
我不会再往深里讲了,因为本身我也不太懂。但是,我相信本文也是能帮到一些人的,尤其本文上传了将近50张图,很多动态图。
如果有错误,欢迎大家指正,我会做修改,同时这也是我学习的机会。