web-dev-qa-db-ja.com

Pythonでの可逆STFTおよびISTFT

短時間フーリエ変換 の汎用的な形式はありますか?対応する逆変換がSciPyまたはNumPyなどに組み込まれていますか?

Matplotlibには、ax.specgram()を呼び出すpyplot specgram関数があり、mlab.specgram()を呼び出す _spectral_helper() を呼び出します。

_#The checks for if y is x are so that we can use the same function to
#implement the core of psd(), csd(), and spectrogram() without doing
#extra calculations.  We return the unaveraged Pxy, freqs, and t.
_

だが

これは、204#psd、csd、およびスペクトログラムの共通性を実装するヘルパー関数です。 [〜#〜]ではない[〜#〜]は、mlabの外部で使用することを意図しています

しかし、これがSTFTおよびISTFTに使用できるかどうかはわかりません。他に何かありますか、または これらのMATLAB関数 のようなものを翻訳する必要がありますか?

私は自分のアドホック実装を作成する方法を知っています。さまざまなウィンドウ処理関数を処理できる(ただし、適切なデフォルトがある)フル機能の何かを探していますが、COLA windows(istft(stft(x))==x)、複数の人によってテストされ、オフバイワンエラーはなく、エンドとゼロパディングを適切に処理し、実際の入力用の高速RFFT実装など.

49
endolith

私はこれに少し遅れていますが、scipyに組み込まれていることに気づきました istft 0.19.0以降の機能

2
Miss Palmer

ここに私のPythonコード、この答えのために簡略化された:

import scipy, pylab

def stft(x, fs, framesz, hop):
    framesamp = int(framesz*fs)
    hopsamp = int(hop*fs)
    w = scipy.hanning(framesamp)
    X = scipy.array([scipy.fft(w*x[i:i+framesamp]) 
                     for i in range(0, len(x)-framesamp, hopsamp)])
    return X

def istft(X, fs, T, hop):
    x = scipy.zeros(T*fs)
    framesamp = X.shape[1]
    hopsamp = int(hop*fs)
    for n,i in enumerate(range(0, len(x)-framesamp, hopsamp)):
        x[i:i+framesamp] += scipy.real(scipy.ifft(X[n]))
    return x

ノート:

  1. list comprehensionは、numpy/scipyで信号のブロック処理をシミュレートするために使用するちょっとしたトリックです。これはMatlabのblkprocに似ています。 forループの代わりに、リスト内包内の信号の各フレームにコマンド(例:fft)を適用し、次にscipy.arrayは2D配列にキャストします。これを使用して、スペクトログラム、クロマグラム、MFCCグラムなどを作成します。
  2. この例では、istftで単純なオーバーラップおよび追加メソッドを使用しています。元の信号を再構成するために、シーケンシャルウィンドウ関数の合計は一定である必要があり、できれば1(1.0)に等しい必要があります。この例では、Hann(またはhanning)ウィンドウと50%のオーバーラップを選択しましたが、これは完全に機能します。詳細は この説明 を参照してください。
  3. おそらく、ISTFTを計算するより原理的な方法があります。この例は、主に教育を目的としています。

テスト:

if __== '__main__':
    f0 = 440         # Compute the STFT of a 440 Hz sinusoid
    fs = 8000        # sampled at 8 kHz
    T = 5            # lasting 5 seconds
    framesz = 0.050  # with a frame size of 50 milliseconds
    hop = 0.025      # and hop size of 25 milliseconds.

    # Create test signal and STFT.
    t = scipy.linspace(0, T, T*fs, endpoint=False)
    x = scipy.sin(2*scipy.pi*f0*t)
    X = stft(x, fs, framesz, hop)

    # Plot the magnitude spectrogram.
    pylab.figure()
    pylab.imshow(scipy.absolute(X.T), Origin='lower', aspect='auto',
                 interpolation='nearest')
    pylab.xlabel('Time')
    pylab.ylabel('Frequency')
    pylab.show()

    # Compute the ISTFT.
    xhat = istft(X, fs, T, hop)

    # Plot the input and output signals over 0.1 seconds.
    T1 = int(0.1*fs)

    pylab.figure()
    pylab.plot(t[:T1], x[:T1], t[:T1], xhat[:T1])
    pylab.xlabel('Time (seconds)')

    pylab.figure()
    pylab.plot(t[-T1:], x[-T1:], t[-T1:], xhat[-T1:])
    pylab.xlabel('Time (seconds)')

STFT of 440 Hz sinusoidISTFT of beginning of 440 Hz sinusoidISTFT of end of 440 Hz sinusoid

60
Steve Tjoa

これが私が使用するSTFTコードです。ここでのSTFT + ISTFTは完全な再構成を与えます(最初のフレームに対しても)。 Steve Tjoaがここに示したコードを少し変更しました。ここで、再構成された信号の大きさは入力信号の大きさと同じです。

import scipy, numpy as np

def stft(x, fftsize=1024, overlap=4):   
    hop = fftsize / overlap
    w = scipy.hanning(fftsize+1)[:-1]      # better reconstruction with this trick +1)[:-1]  
    return np.array([np.fft.rfft(w*x[i:i+fftsize]) for i in range(0, len(x)-fftsize, hop)])

def istft(X, overlap=4):   
    fftsize=(X.shape[1]-1)*2
    hop = fftsize / overlap
    w = scipy.hanning(fftsize+1)[:-1]
    x = scipy.zeros(X.shape[0]*hop)
    wsum = scipy.zeros(X.shape[0]*hop) 
    for n,i in enumerate(range(0, len(x)-fftsize, hop)): 
        x[i:i+fftsize] += scipy.real(np.fft.irfft(X[n])) * w   # overlap-add
        wsum[i:i+fftsize] += w ** 2.
    pos = wsum != 0
    x[pos] /= wsum[pos]
    return x
9
Basj

_librosa.core.stft_istft は、私が探していたものと非常によく似ていますが、当時は存在しませんでした。

librosa.core.stft(y, n_fft=2048, hop_length=None, win_length=None, window=None, center=True, dtype=<type 'numpy.complex64'>)

ただし、正確に反転するわけではありません。端は先細になっています。

3
endolith

上記の答えはどちらもうまくいきませんでしたOOTB=私にとっては。それで、Steve Tjoaの修正をしました。

import scipy, pylab
import numpy as np

def stft(x, fs, framesz, hop):
    """
     x - signal
     fs - sample rate
     framesz - frame size
     hop - hop size (frame size = overlap + hop size)
    """
    framesamp = int(framesz*fs)
    hopsamp = int(hop*fs)
    w = scipy.hamming(framesamp)
    X = scipy.array([scipy.fft(w*x[i:i+framesamp]) 
                     for i in range(0, len(x)-framesamp, hopsamp)])
    return X

def istft(X, fs, T, hop):
    """ T - signal length """
    length = T*fs
    x = scipy.zeros(T*fs)
    framesamp = X.shape[1]
    hopsamp = int(hop*fs)
    for n,i in enumerate(range(0, len(x)-framesamp, hopsamp)):
        x[i:i+framesamp] += scipy.real(scipy.ifft(X[n]))
    # calculate the inverse envelope to scale results at the ends.
    env = scipy.zeros(T*fs)
    w = scipy.hamming(framesamp)
    for i in range(0, len(x)-framesamp, hopsamp):
        env[i:i+framesamp] += w
    env[-(length%hopsamp):] += w[-(length%hopsamp):]
    env = np.maximum(env, .01)
    return x/env # right side is still a little messed up...
1
David

別のSTFTが見つかりましたが、対応する逆関数がありません:

http://code.google.com/p/pytfd/source/browse/trunk/pytfd/stft.py

def stft(x, w, L=None):
    ...
    return X_stft
  • wは配列としてのウィンドウ関数です
  • [〜#〜] l [〜#〜]は、サンプルのオーバーラップです
1
endolith

これもGitHubで見つかりましたが、通常の配列ではなくパイプラインで動作しているようです。

http://github.com/ronw/frontend/blob/master/basic.py#LID281

def STFT(nfft, nwin=None, nhop=None, winfun=np.hanning):
    ...
    return dataprocessor.Pipeline(Framer(nwin, nhop), Window(winfun),
                                  RFFT(nfft))


def ISTFT(nfft, nwin=None, nhop=None, winfun=np.hanning):
    ...
    return dataprocessor.Pipeline(IRFFT(nfft), Window(winfun),
                                  OverlapAdd(nwin, nhop))
0
endolith

Basjの回答の修正バージョン。

import scipy, numpy as np
import matplotlib.pyplot as plt

def stft(x, fftsize=1024, overlap=4):
    hop=fftsize//overlap
    w = scipy.hanning(fftsize+1)[:-1]      # better reconstruction with this trick +1)[:-1]  
    return np.vstack([np.fft.rfft(w*x[i:i+fftsize]) for i in range(0, len(x)-fftsize, hop)])

def istft(X, overlap=4):   
    fftsize=(X.shape[1]-1)*2
    hop=fftsize//overlap
    w=scipy.hanning(fftsize+1)[:-1]
    rcs=int(np.ceil(float(X.shape[0])/float(overlap)))*fftsize
    print(rcs)
    x=np.zeros(rcs)
    wsum=np.zeros(rcs)
    for n,i in Zip(X,range(0,len(X)*hop,hop)): 
        l=len(x[i:i+fftsize])
        x[i:i+fftsize] += np.fft.irfft(n).real[:l]   # overlap-add
        wsum[i:i+fftsize] += w[:l]
    pos = wsum != 0
    x[pos] /= wsum[pos]
    return x

a=np.random.random((65536))
b=istft(stft(a))
plt.plot(range(len(a)),a,range(len(b)),b)
plt.show()

私はscipy.signalがあなたが探しているものを持っていると思います。妥当なデフォルトがあり、複数のウィンドウタイプをサポートしています...

http://docs.scipy.org/doc/scipy-0.17.0/reference/generated/scipy.signal.spectrogram.html

from scipy.signal import spectrogram
freq, time, Spec = spectrogram(signal)
0
meatcomputer