用文字描述逆短时傅立叶逆变换算法


20

我试图从概念上理解将正向和反向短时傅立叶变换(STFT)应用于离散时域信号时发生的情况。我找到了Allen和Rabiner(1977)的经典论文,以及Wikipedia的文章(链接)。我相信,还有另一个好文章被发现在这里

我对计算Gabor变换感兴趣,这无非是带有高斯窗口的STFT。

这是我对前向 STFT的了解:

  1. 从信号中选择一个子序列,该子序列由时域元素组成。
  2. 使用时域中的逐点乘法,将子序列乘以窗口函数。
  3. 使用FFT将相乘的子序列带入频域。
  4. 通过选择连续的重叠子序列,并重复上述过程,我们得到了一个具有mn列的矩阵。每一列是在给定时间计算的子序列。这可以用于计算频谱图。

但是,对于 STFT,论文讨论了重叠分析部分的求和。我发现可视化这里发生的事情非常具有挑战性。为了能够计算 STFT(如上所述,按逐步顺序),我必须做什么?

前向STFT

我创建了一个图,显示了我认为前向STFT正在进行的工作。我不了解的是如何组装每个子序列,以便获得原始时间序列。有人可以修改此图形或给出一个方程式来显示如何添加子序列吗?正向变换

逆变换

这是我对逆变换的了解。使用IFFT将每个连续的窗口带回到时域。然后,将每个窗口移动步长,然后将其添加到上一个移动的结果中。下图显示了此过程。相加的输出是时域信号。

逆变换

代码示例

下面的Matlab代码生成一个合成的时域信号,然后测试STFT过程,证明在数值舍入误差内,逆是正向变换的对。信号的开始和结尾都进行零填充,以确保窗口的中心可以位于时域信号的第一个和最后一个元素。

ñ+ñ0-1个ñ0

% The code computes the STFT (Gabor transform) with step size = 1
% This is most useful when modifications of the signal is required in
% the frequency domain

% The Gabor transform is a STFT with a Gaussian window (w_t in the code)

% written by Nicholas Kinar

% Reference:
% [1] J. B. Allen and L. R. Rabiner, 
% “A unified approach to short-time Fourier analysis and synthesis,” 
% Proceedings of the IEEE, vol. 65, no. 11, pp. 1558 – 1564, Nov. 1977.

% generate the signal
mm = 8192;                  % signal points
t = linspace(0,1,mm);       % time axis

dt = t(2) - t(1);           % timestep t
wSize = 101;                % window size


% generate time-domain test function
% See pg. 156
% J. S. Walker, A Primer on Wavelets and Their Scientific Applications, 
% 2nd ed., Updated and fully rev. Boca Raton: Chapman & Hall/CRC, 2008.
% http://www.uwec.edu/walkerjs/primer/Ch5extract.pdf
term1 = exp(-400 .* (t - 0.2).^2);
term2 = sin(1024 .* pi .* t);
term3 = exp(-400.*(t- 0.5).^2);
term4 = cos(2048 .* pi .* t);
term5 = exp(-400 .* (t-0.7).^2);
term6 = sin(512.*pi.*t) - cos(3072.*pi.*t);
u = term1.*term2  + term3.*term4 + term5.*term6; % time domain signal
u = u';

figure;
plot(u)

Nmid = (wSize - 1) / 2 + 1;    % midway point in the window
hN = Nmid - 1;                 % number on each side of center point       


% stores the output of the Gabor transform in the frequency domain
% each column is the FFT output
Umat = zeros(wSize, mm);     


% generate the Gaussian window 
% [1] Y. Wang, Seismic inverse Q filtering. Blackwell Pub., 2008.
% pg. 123.
T = dt * hN;                    % half-width
sp = linspace(dt, T, hN); 
targ = [-sp(end:-1:1) 0 sp];    % this is t - tau
term1 = -((2 .* targ) ./ T).^2;
term2 = exp(term1);
term3 = 2 / (T * sqrt(pi));
w_t = term3 .* term2;
wt_sum = sum ( w_t ); % sum of the wavelet


% sliding window code
% NOTE that the beginning and end of the sequence
% are padded with zeros 
for Ntau = 1:mm

    % case #1: pad the beginning with zeros
    if( Ntau <= Nmid )
        diff = Nmid - Ntau;
        u_sub = [zeros(diff,1); u(1:hN+Ntau)];
    end

    % case #2: simply extract the window in the middle
    if (Ntau < mm-hN+1 && Ntau > Nmid)
        u_sub = u(Ntau-hN:Ntau+hN);
    end

    % case #3: less than the end
    if(Ntau >= mm-hN+1)
        diff = mm - Ntau;
        adiff = hN - diff;
        u_sub = [ u(Ntau-hN:Ntau+diff);  zeros(adiff,1)]; 
    end   

    % windowed trace segment
    % multiplication in time domain with
    % Gaussian window  function
    u_tau_omega = u_sub .* w_t';

    % segment in Fourier domain
    % NOTE that this must be padded to prevent
    % circular convolution if some sort of multiplication
    % occurs in the frequency domain
    U = fft( u_tau_omega );

    % make an assignment to each trace
    % in the output matrix
    Umat(:,Ntau) = U;

end

% By here, Umat contains the STFT (Gabor transform)

% Notice how the Fourier transform is symmetrical 
% (we only need the first N/2+1
% points, but I've plotted the full transform here
figure;
imagesc( (abs(Umat)).^2 )


% now let's try to get back the original signal from the transformed
% signal

% use IFFT on matrix along the cols
us = zeros(wSize,mm);
for i = 1:mm 
    us(:,i) = ifft(Umat(:,i));
end

figure;
imagesc( us );

% create a vector that is the same size as the original signal,
% but allows for the zero padding at the beginning and the end of the time
% domain sequence
Nuu = hN + mm + hN;
uu = zeros(1, Nuu);

% add each one of the windows to each other, progressively shifting the
% sequence forward 
cc = 1; 
for i = 1:mm
   uu(cc:cc+wSize-1) = us(:,i) + uu(cc:cc+wSize-1)';
   cc = cc + 1;
end

% trim the beginning and end of uu 
% NOTE that this could probably be done in a more efficient manner
% but it is easiest to do here

% Divide by the sum of the window 
% see Equation 4.4 of paper by Allen and Rabiner (1977)
% We don't need to divide by L, the FFT transform size since 
% Matlab has already taken care of it 
uu2 = uu(hN+1:end-hN) ./ (wt_sum); 

figure;
plot(uu2)

% Compare the differences bewteen the original and the reconstructed
% signals.  There will be some small difference due to round-off error
% since floating point numbers are not exact
dd = u - uu2';

figure;
plot(dd);

2
很大的问题-但是,您是如何快速动态制作这些图的?...
Spacey

2
我将Adobe Illustrator用于图表,将Mathtype用于希腊字符。
尼古拉斯·基纳尔

1
“我对计算Gabor变换感兴趣,这无非是带有高斯窗口的STFT。” 请记住,Gabor变换是一个连续的积分,并且高斯窗口扩展到无穷大。STFT的典型实现使用离散的重叠块,并且必须使用有限长度的窗口。
endlith 2015年

感谢您指出这一点,endolith。在进行信号处理时,我倾向于以非常离散的方式思考。
Nicholas Kinar

Answers:


11

STFT变换对可以通过4个不同的参数来表征:

  1. FFT大小(N)
  2. 步长(M)
  3. 分析窗口(尺寸N)
  4. 合成窗(尺寸N)

流程如下:

  1. 从当前输入位置获取N(fft大小)样本
  2. 应用分析窗口
  3. 进行FFT
  4. 在频域中做任何您想做的事
  5. 逆FFT
  6. 应用综合窗口
  7. 在当前输出位置添加到输出
  8. 将输入和输出位置提前M(步长)样本

重叠添加算法就是一个很好的例子。在这种情况下,步长为N,FFT大小为2 * N,分析窗口为矩形,其中N个为1,后跟N个0,并且合成窗口为全1。

为此还有许多其他选择,在某些条件下,正向/反向传输可以完全重构(即,您可以将原始信号返回)。

这里的关键是每个输出样本通常会从一个以上的逆FFT接收加性贡献。需要在多个帧上累积输出。贡献帧的数量仅由FFT大小除以步长即可得出(必要时向上舍入)。


非常感谢您的深刻见解。我了解重叠添加方法。综合窗口应使用什么?有方程式吗?如果我知道分析窗口函数(例如高斯窗口),如何计算综合窗口?我了解重叠叠加方法如何用于卷积,但是我不了解它如何用于STFT。如果步长为step = 1,如何将帧加在一起?有方程式吗?
Nicholas Kinar

如果分析窗口功能以每个步长为1的样本为中心,是否对时域序列的开始和结尾进行零填充,以使窗口的中间以每个样本(包括第一个和最后一个)为中心时域序列中的样本)?
Nicholas Kinar

您可以根据应用程序的特定需求选择步长,ftf大小,分析和综合窗口。一个示例是步长N,FFT大小2 * N,分析汉宁,综合全1。您可以修改它以分析sqrt(hanning)和综合sqrt(hanning)。任一个都会起作用。我归结为您在频域中所做的工作以及您可能创建的哪种类型的工件(例如时域混叠)。
希尔马

@Hilmar:我需要能够对信号进行频域修改,然后采用IFFT来获取时域信号。我想尽量减少时域混叠。我仍然不明白如何将每个子序列带回时域,然后将它们加在一起。
Nicholas Kinar

我已经编写了一些测试代码,然后更新了原始问题。
尼古拉斯·基纳尔

2

在首次提出此问题七年后,我遇到了类似于@Nicholas Kinar的困惑。在此,我想提供一些“非正式的”和“没有完全保证的正确性”的个人感性想法和解释。

为了更好的理解,以下语句的标题被夸大了。

  1. STFT的正向过程并不是要保留原始信号。
    • 当使用具有非平凡窗口(不是全1)的STFT时,FFT的输入信号是原始信号片段的倾斜/拉伸版本。
    • 这对于特征提取很有用,其中可以过滤掉无用/冗余的数据。像在音节检测中一样,并非所有时间数据都需要检测语音中的某些特定音调。
    • 窗口矢量中的峰值代表算法应注意的音频信号中的少数位置。
  2. 因此,反向STFT的原始结果可能是我们可能无法凭直觉期望的结果。
    • STFT功能的外观应该是加窗的信号片段。
  3. 为了获得原始的非窗口信号片段,可以将反窗口应用于ifft的原始输出。
    • 设计一个可以消除汉/汉明窗效应的映射功能很容易。
  4. 然后涉及合成窗口以处理时间碎片重叠
    • 由于原始的非窗口信号片段可以看作已经获得,因此可以使用任何“转换权重”对重叠部分进行插值。
  5. 如果您想考虑开窗语音的ftf可能不太尊重微弱的信号,但却喜欢那些有力的信号,那么可能有一种方法可以设计相应的综合窗口。
  6. 同样,可以通过应用以下原理给出简单的合成窗口生成算法:
    • 如果此位置的分析窗口值较高,则权重较高(与该位置重叠的其他片段相比)。
    • 如果此位置的分析窗口值较低,则权重会降低该位置,而其他重叠片段会以较大的分析窗口值来兑现此位置。

1
这些有趣的陈述绝对可以帮助鼓励人们思考STFT。
尼古拉斯·基纳尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.