DCT 和 FFT 的对比:短时傅里叶变换与短时离散余弦变换的实现

在信号处理领域,离散余弦变换(DCT)和快速傅里叶变换(FFT)是两种重要的变换方法,广泛应用于频域分析、特征提取和数据压缩。本文将对 DCT 和 FFT 进行对比,并介绍短时傅里叶变换(STFT)和短时离散余弦变换(STDCT)的物理意义、参数意义、计算公式以及数据输入输出维度。

1. STFT 和 STDCT 的对比

1.1 短时傅里叶变换(STFT)

物理意义

STFT 对信号进行短时分析,将其分为短时间帧并对每帧应用傅里叶变换,以捕捉信号在时间和频率上的变化。

参数意义
  • nperseg:每个分段的长度,决定频率分辨率。
  • noverlap:每个分段之间的重叠样本数,影响时间分辨率。
计算公式

对于信号 x [ n ] x[n] x[n],STFT 的计算公式为:

X [ k , m ] = ∑ n = 0 N − 1 x [ n ] w [ n − m ] e − j 2 π N k n X[k, m] = \sum_{n=0}^{N-1} x[n] w[n-m] e^{-j \frac{2\pi}{N}kn} X[k,m]=n=0N1x[n]w[nm]ejN2πkn

其中 w [ n ] w[n] w[n] 是窗函数。

数据输入输出维度
  • 输入:一维信号,形状为 N N N,其中 N N N 是信号的长度。
  • 输出:二维频谱,形状为 ( N f , N t ) (N_f, N_t) (Nf,Nt),其中 N f N_f Nf 是频率分量的数量, N t N_t Nt 是时间帧的数量。

1.2 短时离散余弦变换(STDCT)

物理意义

STDCT 对信号进行短时分析,将其分为短时间帧并对每帧应用离散余弦变换,有效表示信号的频谱特性,尤其是在信号能量集中在低频部分时。

参数意义
  • frame_length:每个帧的长度,影响频率分辨率。
  • hop_length:帧移,影响时间分辨率。
计算公式

对于信号 x [ n ] x[n] x[n],STDCT 的计算公式为:

X [ k , m ] = ∑ n = 0 N − 1 x [ n ] w [ n − m ] cos ⁡ ( π N ( n + 1 2 ) k ) X[k, m] = \sum_{n=0}^{N-1} x[n] w[n-m] \cos\left(\frac{\pi}{N} \left(n + \frac{1}{2}\right) k\right) X[k,m]=n=0N1x[n]w[nm]cos(Nπ(n+21)k)

数据输入输出维度
  • 输入:一维信号,形状为 N N N,其中 N N N 是信号的长度。
  • 输出:二维频谱,形状为 ( N d , N t ) (N_d, N_t) (Nd,Nt),其中 N d N_d Nd 是 DCT 系数的数量(通常为帧长度), N t N_t Nt 是时间帧的数量。

2. Python 代码实现

以下是一个完整的 Python 示例代码,展示如何生成随机信号,计算 STFT 和 STDCT,并重建信号。

import numpy as np
import matplotlib.pyplot as plt
import librosa
import librosa.display
from scipy.fftpack import dct, idct

# 1. 生成随机信号
fs = 16000 # 采样率
t = np.linspace(0, 1, fs, endpoint=False)  # 1秒钟的时间
signal = np.random.randn(fs)  # 生成标准正态分布的随机信号

# 2. 使用 librosa 计算 STFT
n_fft = 256  # FFT 的长度
hop_length = 128  # 帧移,默认情况下可以设置为 n_fft // 2
Zxx_librosa = librosa.stft(signal, n_fft=n_fft, hop_length=hop_length)
print(Zxx_librosa.shape)

# 计算频率和时间轴
frequencies_librosa = np.fft.rfftfreq(n_fft, d=1 / fs)  # 计算频率
times_librosa = np.arange(Zxx_librosa.shape[1]) * hop_length / fs  # 计算时间

print("Number of frames (times_librosa.shape):", times_librosa.shape[0])


# 计算 STDCT
def short_time_dct(signal, frame_length=256, hop_length=128):
    # 窗函数
    window = np.hanning(frame_length)
    # 计算帧数
    num_frames = (len(signal) - frame_length) // hop_length + 1
    # 初始化 DCT 结果
    dct_result = np.zeros((num_frames, frame_length))

    for i in range(num_frames):
        start = i * hop_length
        frame = signal[start:start + frame_length] * window
        dct_result[i, :] = dct(frame, norm='ortho')

    return dct_result


stdct_result = short_time_dct(signal)
print("STDCT shape:", stdct_result.shape)


# 3. 重建信号
def reconstruct_signal_from_stft(Zxx, hop_length):
    # 使用 librosa 的 istft
    reconstructed_signal = librosa.istft(Zxx, hop_length=hop_length)
    return reconstructed_signal


def reconstruct_signal_from_stdct(dct_result, hop_length, frame_length):
    num_frames = dct_result.shape[0]
    reconstructed_signal = np.zeros(num_frames * hop_length + frame_length)
    window = np.hanning(frame_length)

    for i in range(num_frames):
        start = i * hop_length
        frame = idct(dct_result[i, :], norm='ortho')
        reconstructed_signal[start:start + frame_length] += frame * window

    return reconstructed_signal


reconstructed_signal_stft = reconstruct_signal_from_stft(Zxx_librosa, hop_length=hop_length)
reconstructed_signal_stdct = reconstruct_signal_from_stdct(stdct_result, hop_length=128, frame_length=256)

# 4. 可视化
plt.figure(figsize=(12, 10))

# 原始信号
plt.subplot(4, 1, 1)
plt.plot(t, signal)
plt.title('Original Signal')
plt.xlabel('Time [s]')
plt.ylabel('Amplitude')

# STFT 结果
plt.subplot(4, 1, 2)
plt.pcolormesh(times_librosa, frequencies_librosa, np.abs(Zxx_librosa), shading='gouraud')
plt.title('STFT Magnitude using librosa')
plt.xlabel('Time [s]')
plt.ylabel('Frequency [Hz]')

# STDCT 结果
plt.subplot(4, 1, 3)
plt.imshow(np.abs(stdct_result.T), aspect='auto', origin='lower', extent=[0, t[-1], 0, 256])
plt.title('STDCT Magnitude')
plt.xlabel('Time [s]')
plt.ylabel('DCT Coefficients')

# 重建信号
plt.subplot(4, 1, 4)
plt.plot(t, reconstructed_signal_stft[:len(t)], label='Reconstructed from STFT', alpha=0.7)
plt.plot(t, reconstructed_signal_stdct[:len(t)], label='Reconstructed from STDCT', alpha=0.7)
plt.title('Reconstructed Signals')
plt.xlabel('Time [s]')
plt.ylabel('Amplitude')
plt.legend()

plt.tight_layout()
plt.show()

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值