一、说明
我努力理解傅里叶变换,直到我将这个概念映射到现实世界的直觉上。这是一系列技术性越来越强的解释中的第一篇文章。我希望直觉也能帮助你!
二、傅里叶变换中频域简介
声音是一种机械波,是空气中的振动或其他介质。音符对应于波的频率。高频波对应于高音符(高压和低压之间的变化较快),低频波对应于低音符(高压和低压之间的较慢变化)。
这些波随着时间的推移在空间中传播。当波到达麦克风时,它会摆动一种称为振膜的结构。隔膜的运动产生电流,这使我们能够记录加压和减压的速率。
如果我们考虑一个静止的麦克风,当波通过它时,记录的压力变化是时间的函数。时间域中的这种表示与我们的直觉非常吻合:随着时间的推移,压力会以与频率相对应的速度周期性地增加和减少。更复杂的音调是多个频率的总和组合(图 1)。
信号在时域中的这种表示是密集的:对于大多数域,它不为零。但我们知道,我们可以更简洁地谈论音乐基调,而不是描述每个时间点的压力。相反,我们可以简单地命名一个音符,或者等效地以赫兹 (Hz) 表示频率:每秒的周期数。
在频域中,x轴表示频率,y轴是信号中的频率量。对于图1中描述的简单音调,频域中的这种描述是稀疏的:对于大多数域,它为零。
傅里叶变换是一种数学运算,它将我们在时域或空间域中的信号映射到频域中的函数。
傅里叶变换正是我们想要的!它采用我们在图1中绘制的密集时间信号,并给出图2在频域中的稀疏描述。从图中看,每个分量频率都很明显。
如果我们将图1中信号的时间持续时间视为一秒,则图2中的x轴值对应于赫兹。傅里叶变换为第一个图恢复 5Hz,为第二个图恢复 25Hz,为第三个复音恢复组合。对于所有其他频率值,傅里叶变换的值约为零。目前,我们只考虑量级,我们将在以后的帖子中探索更多!
我发现在实时音频数据上试验傅里叶变换最有价值!我把一个小的Python存储库放在一起,试验傅里叶变换的结果,应用于从计算机麦克风记录的短片段。
查看来源!尝试乐器和环境声音。
三、傅里叶变换---了解相位角
在上一节中,可视化专门讨论了傅里叶变换的大小。在这里,我们将扩展缺失的部分:相位!在上节中,我展示了如何使用傅里叶变换的幅度来估计信号的频率分量。然而,仅凭频率,我们无法完美地重建信号;我们需要相位!
图 2-1:三个相同的正弦波相位偏移。图片由作者提供。
如果我们查看纯音音频文件,但将信号从左向右移动怎么办?
在我们前面的音频示例中,这带来了挑战。左侧的所有三个信号(图1)具有相同的频率。高压和低压之间的振荡速率是相同的。唯一的区别是起始位置,从左到右移动。
我们可以将其表示为“相移”。三个信号是相同的频率,只是在振荡的起始位置偏移。查看三个生成方程,分别 — 0.5 * cos(5x)、05 * cos(5x + 1) 和 0.5 * cos(5x + 2) — 此偏移量表示为余弦表达式中的求和值,即 0、1 和 2。
图 2-2:使用傅里叶变换的 np.angle 恢复的近似相位。图片由作者提供。
该相位信息也用傅里叶变换表示,可以使用numpy“角度”函数恢复。如果我们查看与具有最大幅度的频率相同的指数处的相位值,我们可以确定与该频率分量相关的相位偏移。在这里(图 2),傅里叶变换从上面的方程中恢复 0、1 和 2!
四、傅里叶变换,应用(3):复杂数据中的幅度和相位编码
4.1 复数基础知识
在本系列的后面,我们将在处理复数时获得更多技术性。不过,就目前而言,我们只需要将复数识别为两部分的复合:实分量和虚分量。
我们很快就会回到复数的一些神奇性质(并介绍一位名叫欧拉的特别嘉宾),但为了达到量级和相位,我们可以将复数视为简单的二维值。每个复数都是一对实值和一个虚值。像任何二维一样,我们可以绘制这些复杂的对。按照惯例,我们通常将实部放在 x 轴上,将虚部放在 y 轴上。
图 1:可视化幅度和相位。图片由作者提供。
我们复数的虚部是 i 的倍数(或 j,取决于你问谁):√(-1)。在左边,我可视化了复数 1+1j。实值为 1,虚值为 1 的复数。同样,实部分量在 x 轴上,虚部分量在 y 轴上。正如预期的那样,这使我们直接处于第一象限。
现在,我们不需要太担心为什么 y 轴表示为 j 的系数。 只要你接受我们可以将这个虚值视为第二维度,几何直觉就会起作用。我们会回来的,我保证!
傅里叶变换返回的值很复杂。在这些复数值上调用 np.abs 会给我们每个复值的大小。对这些复数值调用 np.angle 会给出每个复数值的相位角。通过这种方式,幅度和相位被编码在傅里叶变换的复数值中。
4.2 重新实现 np.abs
复数的大小只是到原点的欧几里得距离 (0+0j):平方和的平方根。如上图所示,对于 1+1j,这是 √(1² + 1²)≈1.41(想想勾股定理!下面,我包含了任意复数的逻辑:
def abs(complex_number: complex) -> float: # pylint: disable=redefined-builtin
"""
Get the magnitude (abs) of complex number.
Args:
complex_number: Complex number.
Returns:
Magnitude.
"""
real_component = complex_number.real
imaginary_component = complex_number.imag
# calculate distance to origin
sum_of_squares = (real_component ** 2) + (imaginary_component ** 2)
sqrt_of_sum_of_squares = sum_of_squares ** (1 / 2)
return sqrt_of_sum_of_squares
assert abs(1 + 1j) == np.abs(1 + 1j)
assert abs(2 * (1 - 1j)) == np.abs(2 * (1 - 1j))
assert abs(3 * (-1 + 1j)) == np.abs(3 * (-1 + 1j))
assert abs(4 * (-1 - 1j)) == np.abs(4 * (-1 - 1j))
4.3 重新实现 np.angle
复数的相位是标准角(从右顺时针方向)。如上图所示,对于 1+1j,这是 arctan2(1, 1)≈0.79 弧度。检查度数(0.79弧度 * 180° / π弧度≈ 45°),该值正确反映了上图的角度。如果 arctan 使您的血压飙升,请花一分钟时间查看允许我们恢复这个角度的逆三角恒等式!
下面,我包含了任意复数的逻辑:
def angle(complex_number: complex) -> float:
"""
Get phase (angle) of complex number.
Args:
complex_number: Complex number.
Returns:
Phase angle.
"""
real_component = complex_number.real
imaginary_component = complex_number.imag
return np.arctan2(imaginary_component, real_component)
assert angle(1 + 1j) == np.angle(1 + 1j)
assert angle(2 * (1 - 1j)) == np.angle(2 * (1 - 1j))
assert angle(3 * (-1 + 1j)) == np.angle(3 * (-1 + 1j))
assert angle(4 * (-1 - 1j)) == np.angle(4 * (-1 - 1j))
伟大!盘点一下,我们现在对傅里叶变换的大小和相位的含义有了很好的直观把握。我们对复数有一个基本的处理方法,以及从中恢复幅度和相位信息的方式。
接下来,我们将研究傅里叶逆变换(IFFT),并展示我们可以使用迄今为止开发的工具包完成什么!
如果代码或文本对您有价值,请为文章鼓掌!感谢您的阅读!
五、傅里叶变换应用 :让 FFT 发挥作用
现在,我们拥有了演示FFT的一些实际用例所需的所有工具!
5.1 介绍金融交易
到目前为止,我们已经将傅里叶变换视为数学黑盒运算。同样,我们现在将介绍傅里叶逆变换,而不剖析实现细节。傅里叶逆变换(IFFT)让我们反转FFT!
傅里叶逆变换是一种数学运算,它将频域中的函数映射到时域或空间域中的函数信号。
arr = np.random.normal(loc=0, scale=10, size=20)
np.allclose(np.fft.ifft(np.fft.fft(arr)), arr)
# >>> True
IFFT(FFT(x)) ≈ x,反性质成立!
至关重要的是,这种逆运算允许我们在频域和时空/空间域之间跳转,以最方便的方式操作我们的数据。
5.2 傅里叶变换的应用
让我们开始玩弄傅里叶变换的实际应用吧!
5.2.1 滤波和去噪
FFT为我们提供了宝贵的“去噪”技术,以抑制或消除数据中的伪影。首先,让我们考虑一个近似静止的信号,它受到一些低功耗白噪声的影响。为了进行演示,我们可以使用与之前相同的纯音信号(图 1,“无噪声信号”)。此外,让我们在该信号中添加一些高斯噪声(图 1,“噪声信号”)。
就像我们之前所做的那样,我们可以使用傅里叶变换来查看频率分量(图 1,“无噪声 FFT”和“噪声 FFT”)。
图 1:简单的去噪,用于消除纯音信号中的低功耗噪声。图片由作者提供。
超级有趣!在FFT中,我们可以看到我选择添加的噪声的一些属性。我添加的高斯噪声是零平均加性“白”噪声:它在整个频率上的强度大致相等。
尽管噪声在时域中看起来非常可怕,但在频域中很容易分离出来。查看傅里叶变换的幅度值,我们可以将振幅可视化为频率的函数。在这个简单的情况下,我们可以很容易地完美地恢复我们的无噪音信号!由于信号和噪声之间有清晰的分离,我们可以设置一个硬阈值并降低(即设置为零)低幅度分量。将值保持在阈值以上(图 1,绿色的“噪声 FFT”)并将值放在阈值之前(图 1,红色的“噪声 FFT”),我们可以得到一个很好的无噪声信号重建(图 1,“去噪信号”)!
我们通常不会那么幸运。现实世界的噪音可能更令人讨厌!在许多情况下,噪声可能比我们的信号大。不过,我们还可以使用许多其他方法!
为了介绍一种常见的滤波方法,让我们看一下带通滤光片。带通滤波器由两个阈值组成:(1)去除低于某个阈值的频率的滤波器,允许更高的频率通过(即“高通”)和(2)去除高于某个阈值的频率的滤波器,允许较低频率通过(即“低通”)。这两个滤波器组合在一起,只允许低频截止和高频截止之间的频率“带”通过。
让我们在信号中添加一些高频分量。在实践中,这些可能是我们环境中不需要的音损(例如,在我们的声音示例中,环境中不受控制的音频源)。
图 2:简单的去噪,可去除纯音信号中的高频伪影。图片由作者提供。
现在,我们不是在最小幅度上设置硬阈值,而是将通带之外的频率的分量设置为零(图 2,“噪声 FFT”)。再一次,我们从噪音中恢复信号!
5.2.2 特征提取
我们还可以使用傅里叶变换来提取数据的特征。如果你以前遇到过小波卷积的想法,就会有很深的数学联系!如果您对深入探讨卷积定理的文章的评论感兴趣,请告诉我!
不过,现在,我们可以通过可视化不同的频率分量来专注于一个简单的示例。当我们在这里时,让我们尝试傅里叶变换的 2D 推广!
我们建立的所有直觉仍然适用!靠近原点的值将对应于低频,远离原点的值将对应于高频。和以前一样,np.abs将恢复幅度,np.angle将恢复相位。
如果我们只想检查图像中的边缘,我们可以查看高频值,将原点附近的傅里叶变换分量的振幅设置为零(图 4)。
图 4:轻松去除低频图像分量,可视化高频。按作者绘制。照片CC0由摄影师Lav Varshney拍摄。
我们还可以通过将远离原点的值设置为零来查看缓慢变化的低频分量!
图 5:轻松去除高频图像分量,可视化低频。按作者绘制。照片CC0由摄影师Lav Varshney拍摄。
我们可以使用这些技巧来提取各种有趣的统计属性。相同的方法也适用于一维时间或空间数据,使我们能够将缓慢移动的低频趋势与时间序列数据的突然突然变化(例如,股票价值或整体市场分析)分开。
5.3 其他应用
还有无数其他用途,但如果您有兴趣进一步阅读,这里有一些会想到:
- 有损图像压缩(如JPEG)通常使用对引擎盖下的频域的变换,
- 物理系统建模(如热力学!撇开,3blue1brown绝对是一个灵感),
- 对齐和注册方法,使照片(或时间序列数据!
- 估计和预测周期性生物现象(例如,呼吸频率、心率),以及
- 无线电数据分析和处理。
我们已经对傅里叶变换有了足够的了解,这是危险的!我们已经设法充分发展了我们的直觉,可以解决现实世界的问题。如果您想解释其他傅立叶概念,请发表评论。感谢您的阅读!
接下来,我们将介绍常见操作的傅里叶“备忘单”及其在工具包中的位置。