【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.20 傅里叶变换:从时域到频域的算法实现

在这里插入图片描述

2.20 傅里叶变换:从时域到频域的算法实现

目录
Syntax error in textmermaid version 10.9.0

2.20.1 FFT算法原理

傅里叶变换(Fourier Transform,FT)是一种将时域信号转换为频域信号的数学工具,而快速傅里叶变换(Fast Fourier Transform,FFT)则是实现傅里叶变换的一种高效算法。FFT 算法通过递归分治的方法,将一个大问题分解为多个小问题,从而显著减少计算复杂度。

  • 傅里叶变换的基本概念:时域信号和频域信号的概念。
  • FFT算法的数学原理:详细解释 FFT 算法的数学公式和步骤。
  • FFT算法的实现:如何使用 NumPy 实现 FFT 算法。
graph LR
    A[FFT算法原理] --> B[基本概念]
    B --> C[时域信号]
    B --> D[频域信号]
    A --> E[数学原理]
    E --> F[离散傅里叶变换 (DFT)]
    E --> G[快速傅里叶变换 (FFT)]
    A --> H[实现]
    H --> I[使用 NumPy]
    H --> J[示例代码]
2.20.1.1 傅里叶变换的基本概念

傅里叶变换可以将一个在时间域内表示的信号转换为在频率域内表示的信号。

2.20.1.1.1 时域信号

时域信号是随时间变化的信号,通常表示为 ( x(t) )。

import numpy as np
import matplotlib.pyplot as plt

# 生成时域信号
t = np.linspace(0, 1, 1000, endpoint=False)  # 时间轴
x = np.sin(2 * np.pi * 5 * t) + 0.5 * np.sin(2 * np.pi * 10 * t)  # 两个正弦波的叠加

# 绘制时域信号
plt.plot(t, x)
plt.xlabel('时间 (s)')
plt.ylabel('振幅')
plt.title('时域信号')
plt.grid(True)
plt.show()  # 显示图表
2.20.1.1.2 频域信号

频域信号是通过傅里叶变换得到的信号,通常表示为 ( X(f) )。

# 计算频域信号
X = np.fft.fft(x)  # 使用 NumPy 进行快速傅里叶变换
freqs = np.fft.fftfreq(t.shape[-1], d=t[1] - t[0])  # 计算频率轴

# 绘制频域信号
plt.plot(freqs, np.abs(X))
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度')
plt.title('频域信号')
plt.grid(True)
plt.show()  # 显示图表
2.20.1.2 FFT算法的数学原理

离散傅里叶变换(Discrete Fourier Transform,DFT)的公式如下:

[ X[k] = \sum_{n=0}^{N-1} x[n] e^{-j 2 \pi k n / N} ]

其中,( N ) 是信号的长度,( x[n] ) 是时域信号,( X[k] ) 是频域信号。

快速傅里叶变换(FFT)通过递归分治的方法,将 DFT 的计算复杂度从 ( O(N^2) ) 降低到 ( O(N \log N) )。

2.20.1.2.1 离散傅里叶变换 (DFT)
# 手动实现 DFT
def dft(x):
    N = len(x)
    X = np.zeros(N, dtype=np.complex128)
    for k in range(N):
        for n in range(N):
            X[k] += x[n] * np.exp(-2j * np.pi * k * n / N)  # 计算 DFT
    return X

# 计算 DFT
X_dft = dft(x)
freqs_dft = np.fft.fftfreq(t.shape[-1], d=t[1] - t[0])

# 绘制 DFT 结果
plt.plot(freqs_dft, np.abs(X_dft))
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度')
plt.title('离散傅里叶变换 (DFT)')
plt.grid(True)
plt.show()  # 显示图表
2.20.1.2.2 快速傅里叶变换 (FFT)
# 使用 NumPy 的 fft 函数实现 FFT
X_fft = np.fft.fft(x)
freqs_fft = np.fft.fftfreq(t.shape[-1], d=t[1] - t[0])

# 绘制 FFT 结果
plt.plot(freqs_fft, np.abs(X_fft))
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度')
plt.title('快速傅里叶变换 (FFT)')
plt.grid(True)
plt.show()  # 显示图表
2.20.1.3 FFT算法的实现

使用 NumPy 的 fft 模块可以轻松实现 FFT 算法。

# 计算 FFT
X_fft = np.fft.fft(x)
freqs_fft = np.fft.fftfreq(t.shape[-1], d=t[1] - t[0])

# 绘制 FFT 结果
plt.plot(freqs_fft, np.abs(X_fft))
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度')
plt.title('快速傅里叶变换 (FFT)')
plt.grid(True)
plt.show()  # 显示图表

2.20.2 复数数组存储优化

复数数组在存储和计算中占据一定的空间和时间。通过合理的存储优化,可以提高性能。

  • 复数数组的存储方式:NumPy 中复数数组的存储方式。
  • 存储优化技巧:如何优化复数数组的存储。
Syntax error in textmermaid version 10.9.0
2.20.2.1 复数数组的存储方式

NumPy 中的复数数组通常使用 np.complex128np.complex64 类型。

# 创建复数数组
complex_data = np.array([1 + 2j, 3 + 4j, 5 + 6j], dtype=np.complex128)

# 查看复数数组的类型和内存占用
print(f"复数数组类型: {complex_data.dtype}")
print(f"复数数组内存占用: {complex_data.nbytes} 字节")
2.20.2.2 存储优化技巧

可以通过选择不同的数据类型和使用内存映射来优化复数数组的存储。

2.20.2.2.1 使用 float64
# 使用 float64 存储复数数组
real_part = np.array([1, 3, 5], dtype=np.float64)
imag_part = np.array([2, 4, 6], dtype=np.float64)
complex_data_optimized = real_part + 1j * imag_part

# 查看优化后的复数数组的类型和内存占用
print(f"优化后的复数数组类型: {complex_data_optimized.dtype}")
print(f"优化后的复数数组内存占用: {complex_data_optimized.nbytes} 字节")
2.20.2.2.2 使用 float32
# 使用 float32 存储复数数组
real_part = np.array([1, 3, 5], dtype=np.float32)
imag_part = np.array([2, 4, 6], dtype=np.float32)
complex_data_optimized = real_part + 1j * imag_part

# 查看优化后的复数数组的类型和内存占用
print(f"优化后的复数数组类型: {complex_data_optimized.dtype}")
print(f"优化后的复数数组内存占用: {complex_data_optimized.nbytes} 字节")
2.20.2.2.3 内存映射

对于大规模数据,可以使用内存映射来优化存储和计算性能。

# 创建内存映射文件
filename = 'complex_data_mmap.npy'
size = 10000000  # 大规模数据
complex_data_mmap = np.memmap(filename, dtype=np.complex128, mode='w+', shape=(size,))

# 填充示例数据
np.random.seed(42)
complex_data_mmap[:] = np.random.rand(size) + 1j * np.random.rand(size)

# 读取内存映射数据
complex_data_read = np.memmap(filename, dtype=np.complex128, mode='r', shape=(size,))

# 查看内存占用
print(f"内存映射数据内存占用: {complex_data_read.nbytes / (1024 * 1024):.2f} MB")

2.20.3 频域滤波案例

频域滤波是一种常见的信号处理技术,通过在频域中操作信号来去除噪声或提取有用信息。

  • 频域滤波的基本原理:如何在频域中去除噪声。
  • 低通滤波器实现:如何实现一个低通滤波器。
  • 高通滤波器实现:如何实现一个高通滤波器。
Syntax error in textmermaid version 10.9.0
2.20.3.1 频域滤波的基本原理

频域滤波的基本原理是在频域中对信号进行操作,去除不需要的频率成分。

2.20.3.1.1 去除噪声
# 生成带有噪声的时域信号
t = np.linspace(0, 1, 1000, endpoint=False)  # 时间轴
x = np.sin(2 * np.pi * 5 * t) + 0.5 * np.sin(2 * np.pi * 10 * t) + 0.1 * np.random.randn(t.shape[0])  # 添加噪声

# 计算 FFT
X = np.fft.fft(x)
freqs = np.fft.fftfreq(t.shape[-1], d=t[1] - t[0])

# 绘制带噪声的时域信号
plt.plot(t, x)
plt.xlabel('时间 (s)')
plt.ylabel('振幅')
plt.title('带有噪声的时域信号')
plt.grid(True)
plt.show()  # 显示图表

# 绘制带噪声的频域信号
plt.plot(freqs, np.abs(X))
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度')
plt.title('带有噪声的频域信号')
plt.grid(True)
plt.show()  # 显示图表
2.20.3.1.2 保留有用信息

通过去除高频噪声,可以保留有用的信息。

# 设计低通滤波器
def low_pass_filter(X, freqs, cutoff):
    X_filtered = X.copy()
    X_filtered[np.abs(freqs) > cutoff] = 0  # 高于截止频率的频率成分设为 0
    return X_filtered

# 应用低通滤波器
cutoff = 15  # 截止频率
X_filtered = low_pass_filter(X, freqs, cutoff)

# 计算逆 FFT
x_filtered = np.fft.ifft(X_filtered)

# 绘制去噪后的时域信号
plt.plot(t, np.real(x_filtered))
plt.xlabel('时间 (s)')
plt.ylabel('振幅')
plt.title('去噪后的时域信号')
plt.grid(True)
plt.show()  # 显示图表

# 绘制去噪后的频域信号
plt.plot(freqs, np.abs(X_filtered))
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度')
plt.title('去噪后的频域信号')
plt.grid(True)
plt.show()  # 显示图表
2.20.3.2 低通滤波器实现

低通滤波器可以去除高频噪声,保留低频信号。

2.20.3.2.1 设计滤波器
# 设计低通滤波器
def low_pass_filter(X, freqs, cutoff):
    X_filtered = X.copy()
    X_filtered[np.abs(freqs) > cutoff] = 0  # 高于截止频率的频率成分设为 0
    return X_filtered

2.20.3.3.2 应用滤波器

高通滤波器可以去除低频噪声,保留高频信号。

2.20.3.3.1 设计滤波器
# 设计高通滤波器
def high_pass_filter(X, freqs, cutoff):
    X_filtered = X.copy()
    X_filtered[np.abs(freqs) < cutoff] = 0  # 低于截止频率的频率成分设为 0
    return X_filtered
2.20.3.3.2 应用滤波器
# 应用高通滤波器
cutoff = 15  # 截止频率
X_filtered = high_pass_filter(X, freqs, cutoff)

# 计算逆 FFT
x_filtered = np.fft.ifft(X_filtered)

# 绘制去噪后的时域信号
plt.plot(t, np.real(x_filtered))
plt.xlabel('时间 (s)')
plt.ylabel('振幅')
plt.title('去噪后的时域信号')
plt.grid(True)
plt.show()  # 显示图表

# 绘制去噪后的频域信号
plt.plot(freqs, np.abs(X_filtered))
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度')
plt.title('去噪后的频域信号')
plt.grid(True)
plt.show()  # 显示图表

2.20.4 音频处理案例

音频信号的处理是傅里叶变换的一个重要应用领域。通过频域处理,可以实现音频去噪、音频压缩等操作。

  • 音频信号的基本处理:如何读取和写入音频文件。
  • 音频去噪:如何使用 FFT 在频域中去除音频中的噪声。
  • 音频压缩:如何使用 FFT 实现音频的简单压缩。
Syntax error in textmermaid version 10.9.0
2.20.4.1 音频信号的基本处理
2.20.4.1.1 读取音频文件
from scipy.io import wavfile

# 读取音频文件
sample_rate, audio_data = wavfile.read('noisy_audio.wav')

# 绘制音频信号
plt.plot(audio_data)
plt.xlabel('样本点')
plt.ylabel('振幅')
plt.title('读取的音频信号')
plt.grid(True)
plt.show()  # 显示图表
2.20.4.1.2 写入音频文件
# 写入音频文件
wavfile.write('filtered_audio.wav', sample_rate, np.real(x_filtered).astype(np.int16))

# 读取并绘制去噪后的音频信号
sample_rate, filtered_audio_data = wavfile.read('filtered_audio.wav')
plt.plot(filtered_audio_data)
plt.xlabel('样本点')
plt.ylabel('振幅')
plt.title('去噪后的音频信号')
plt.grid(True)
plt.show()  # 显示图表
2.20.4.2 音频去噪
2.20.4.2.1 读取带有噪声的音频文件
# 读取带有噪声的音频文件
sample_rate, noisy_audio_data = wavfile.read('noisy_audio.wav')

# 绘制带有噪声的音频信号
plt.plot(noisy_audio_data)
plt.xlabel('样本点')
plt.ylabel('振幅')
plt.title('带有噪声的音频信号')
plt.grid(True)
plt.show()  # 显示图表

# 计算 FFT
X_audio = np.fft.fft(noisy_audio_data)
freqs_audio = np.fft.fftfreq(noisy_audio_data.shape[0], d=1.0 / sample_rate)

# 绘制带有噪声的频域信号
plt.plot(freqs_audio, np.abs(X_audio))
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度')
plt.title('带有噪声的频域信号')
plt.grid(True)
plt.show()  # 显示图表
2.20.4.2.2 应用低通滤波器
# 应用低通滤波器
cutoff_audio = 1000  # 截止频率
X_audio_filtered = low_pass_filter(X_audio, freqs_audio, cutoff_audio)

# 计算逆 FFT
filtered_audio_data = np.fft.ifft(X_audio_filtered)

# 绘制去噪后的音频信号
plt.plot(np.real(filtered_audio_data))
plt.xlabel('样本点')
plt.ylabel('振幅')
plt.title('去噪后的音频信号')
plt.grid(True)
plt.show()  # 显示图表

# 绘制去噪后的频域信号
plt.plot(freqs_audio, np.abs(X_audio_filtered))
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度')
plt.title('去噪后的频域信号')
plt.grid(True)
plt.show()  # 显示图表
2.20.4.2.3 写入去噪后的音频文件
# 写入去噪后的音频文件
wavfile.write('filtered_audio.wav', sample_rate, np.real(filtered_audio_data).astype(np.int16))

# 读取并绘制去噪后的音频信号
sample_rate, filtered_audio_data = wavfile.read('filtered_audio.wav')
plt.plot(filtered_audio_data)
plt.xlabel('样本点')
plt.ylabel('振幅')
plt.title('去噪后的音频信号')
plt.grid(True)
plt.show()  # 显示图表
2.20.4.3 音频压缩
2.20.4.3.1 读取音频文件
# 读取音频文件
sample_rate, audio_data = wavfile.read('original_audio.wav')

# 绘制原始音频信号
plt.plot(audio_data)
plt.xlabel('样本点')
plt.ylabel('振幅')
plt.title('原始音频信号')
plt.grid(True)
plt.show()  # 显示图表

# 计算 FFT
X_audio = np.fft.fft(audio_data)
freqs_audio = np.fft.fftfreq(audio_data.shape[0], d=1.0 / sample_rate)

# 绘制原始频域信号
plt.plot(freqs_audio, np.abs(X_audio))
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度')
plt.title('原始频域信号')
plt.grid(True)
plt.show()  # 显示图表
2.20.4.3.2 应用频域压缩
# 应用频域压缩
def compress_audio(X, freqs, compression_ratio):
    N = X.shape[0]
    cutoff = int(N * compression_ratio)
    X_compressed = X.copy()
    X_compressed[cutoff:-cutoff] = 0  # 将中间部分频率成分设为 0
    return X_compressed

# 压缩比例
compression_ratio = 0.3  # 保留 30% 的频率成分

# 应用频域压缩
X_audio_compressed = compress_audio(X_audio, freqs_audio, compression_ratio)

# 计算逆 FFT
compressed_audio_data = np.fft.ifft(X_audio_compressed)

# 绘制压缩后的音频信号
plt.plot(np.real(compressed_audio_data))
plt.xlabel('样本点')
plt.ylabel('振幅')
plt.title('压缩后的音频信号')
plt.grid(True)
plt.show()  # 显示图表

# 绘制压缩后的频域信号
plt.plot(freqs_audio, np.abs(X_audio_compressed))
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度')
plt.title('压缩后的频域信号')
plt.grid(True)
plt.show()  # 显示图表
2.20.4.3.3 写入压缩后的音频文件
# 写入压缩后的音频文件
wavfile.write('compressed_audio.wav', sample_rate, np.real(compressed_audio_data).astype(np.int16))

# 读取并绘制压缩后的音频信号
sample_rate, compressed_audio_data = wavfile.read('compressed_audio.wav')
plt.plot(compressed_audio_data)
plt.xlabel('样本点')
plt.ylabel('振幅')
plt.title('压缩后的音频信号')
plt.grid(True)
plt.show()  # 显示图表

2.20.5 与 CUFFT 性能对比

CUFFT 是 NVIDIA 提供的用于 GPU 加速的 FFT 库。通过与 NumPy 的 FFT 性能对比,可以了解在不同场景下的适用性。

  • CUFFT 的基本原理:CUFFT 的工作原理和使用方法。
  • 性能测试:在不同规模的数据上进行性能测试。
  • 结论:对比结果和适用场景。
Syntax error in textmermaid version 10.9.0
2.20.5.1 CUFFT 的基本原理

CUFFT 通过在 GPU 上并行计算 FFT,可以显著提高计算速度。

2.20.5.1.1 工作原理

CUFFT 利用 GPU 的并行计算能力,将 FFT 计算任务分解为多个并行子任务,从而实现高性能计算。

2.20.5.1.2 使用方法
import pycuda.autoinit
import pycuda.driver as cuda
import numpy as np
import cufft

# 创建数据并传输到 GPU
size = 10000000
x_gpu = cuda.to_device(np.random.rand(size).astype(np.float32))

# 创建 CUFFT 计划
plan = cufft.Plan1d(size, cufft.CUFFT_R2C, 1)

# 执行 CUFFT
X_gpu = cuda.empty_like(x_gpu, dtype=np.complex64)
plan.forward(x_gpu, X_gpu)

# 从 GPU 传输回 CPU
X_cpu = X_gpu.get()

# 计算频域信号
freqs = np.fft.fftfreq(size, d=1.0 / 1000)  # 假设采样率为 1000 Hz

# 绘制频域信号
plt.plot(freqs, np.abs(X_cpu))
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度')
plt.title('CUFFT 计算的频域信号')
plt.grid(True)
plt.show()  # 显示图表
2.20.5.2 性能测试
2.20.5.2.1 不同规模数据
sizes = [1000, 10000, 100000, 1000000, 10000000]  # 不同数据规模
numpy_times = []
cufft_times = []

for size in sizes:
    # 生成测试数据
    x = np.random.rand(size).astype(np.float32)
    x_gpu = cuda.to_device(x)

    # 创建 CUFFT 计划
    plan = cufft.Plan1d(size, cufft.CUFFT_R2C, 1)

    # NumPy FFT 性能测试
    import time
    start = time.time()
    X_numpy = np.fft.fft(x)
    end = time.time()
    numpy_times.append(end - start)

    # CUFFT 性能测试
    start = time.time()
    X_gpu = cuda.empty_like(x_gpu, dtype=np.complex64)
    plan.forward(x_gpu, X_gpu)
    end = time.time()
    cufft_times.append(end - start)
2.20.5.2.2 测试代码
import pycuda.autoinit
import pycuda.driver as cuda
import numpy as np
import cufft
import time

sizes = [1000, 10000, 100000, 1000000, 10000000]  # 不同数据规模
numpy_times = []
cufft_times = []

for size in sizes:
    # 生成测试数据
    x = np.random.rand(size).astype(np.float32)
    x_gpu = cuda.to_device(x)

    # 创建 CUFFT 计划
    plan = cufft.Plan1d(size, cufft.CUFFT_R2C, 1)

    # NumPy FFT 性能测试
    start = time.time()
    X_numpy = np.fft.fft(x)
    end = time.time()
    numpy_times.append(end - start)

    # CUFFT 性能测试
    start = time.time()
    X_gpu = cuda.empty_like(x_gpu, dtype=np.complex64)
    plan.forward(x_gpu, X_gpu)
    end = time.time()
    cufft_times.append(end - start)

# 绘制性能对比图
plt.plot(sizes, numpy_times, label='NumPy FFT')
plt.plot(sizes, cufft_times, label='CUFFT')
plt.xlabel('数据规模')
plt.ylabel('计算时间 (s)')
plt.title('NumPy FFT 与 CUFFT 性能对比')
plt.legend()
plt.grid(True)
plt.show()  # 显示图表
2.20.5.2.3 测试结果

通过性能测试,可以看到在大规模数据上,CUFFT 的性能显著优于 NumPy FFT。

Syntax error in textmermaid version 10.9.0
2.20.5.3 结论
  • 对比结果:在小规模数据上,NumPy FFT 和 CUFFT 的性能差异不大;但在大规模数据上,CUFFT 的性能显著优于 NumPy FFT。
  • 适用场景:对于大规模数据处理,推荐使用 CUFFT;对于小规模数据处理,NumPy FFT 足够使用且更简单易用。

2.20.6 总结

本文详细介绍了傅里叶变换(FFT)的原理、复数数组的存储优化、频域滤波案例以及音频处理案例,并通过性能测试对比了 NumPy FFT 和 CUFFT 的性能。希望这些内容能帮助你在信号处理和音频处理中更好地应用傅里叶变换。

2.20.7 参考文献

参考资料名称链接
NumPy 官方文档NumPy Documentation
SciPy 官方文档SciPy Documentation
CUFFT 官方文档CUFFT Documentation
Python 数据科学手册Python Data Science Handbook
傅里叶变换原理Fourier Transform
高效傅里叶变换实现Efficient Fourier Transform Implementation
信号处理基础Signal Processing Basics
音频处理指南Audio Processing Guide
傅里叶变换在音频处理中的应用Fourier Transform in Audio Processing
高频滤波器设计High-pass Filter Design
低频滤波器设计Low-pass Filter Design
音频压缩技术Audio Compression Techniques
傅里叶变换与卷积Fourier Transform and Convolution
傅里叶变换在图像处理中的应用Fourier Transform in Image Processing

这篇文章包含了详细的原理介绍、代码示例、源码注释以及案例等。希望这对您有帮助。如果有任何问题请随私信或评论告诉我。

posted @   爱上编程技术  阅读(10)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示