Poprawianie rozdzielczości spektrogramu w Pythonie?

21

Używam tej specgram()funkcji matplotlibdo generowania spektrogramów plików fal mowy w Pythonie, ale wynik jest zawsze znacznie gorszej jakości niż to, co może wygenerować moje normalne oprogramowanie do transkrypcji, Praat. Na przykład następujące połączenie:

specgram(
    fromstring(spf.readframes(-1), 'Int16'),
    Fs=framerate,
    cmap=cm.gray_r,
)

Generuje to:

wprowadź opis zdjęcia tutaj

Podczas korzystania z programu Praat, praca na tym samym próbce audio z następującymi ustawieniami:

  • Zakres widzenia: 0–8000 Hz
  • Długość okna: 0,005s
  • Zakres dynamiczny: 70dB
  • Przedziały czasowe: 1000
  • Kroki częstotliwości: 250
  • Kształt okna: gaussowski

Generuje to:

wprowadź opis zdjęcia tutaj

Co ja robię źle? Próbowałem bawić się wszystkimi specgram()parametrami, ale wydaje się, że nic nie poprawia rozdzielczości. Nie mam praktycznie żadnego doświadczenia z FFT.

Alek Storm
źródło
Czy możesz podać przykład konfiguracji parametrów matplotlib.specgram, które wypróbowałeś? Podajesz bardzo konkretny przykład parametrów dla Praat, ale nie pokazujesz tej samej konfiguracji dla matplotlib.specgram?
Christopher Felton

Odpowiedzi:

11

Oto parametry matplotlib.specgram

matplotlib.mlab.specgram(x, 
                         NFFT=256, 
                         Fs=2, 
                         detrend=<function detrend_none at 0x1dd6410>, 
                         window=<function window_hanning at 0x1e0b1b8>, 
                         noverlap=128, 
                         pad_to=None, 
                         sides='default', 
                         scale_by_freq=None)

Parametry podane w opisie pytania należy przekonwertować na porównywalne parametry mpl.specgram. Oto przykład odwzorowania:

View range: 0-8000Hz            Fs=16000
Window length: 0.005s           NFFT = int(Fs*0.005) = 80
                                noverlap = int(Fs*0.0025) = 40
Dynamic range: 70dB             n/a
Time steps: 1000                n/a
Frequency steps: 250            
Window shape: Gaussian          default window is hanning change to gaussian

Jeśli użyjesz 8ms, otrzymasz moc 2 FFT (128). Poniżej znajduje się opis ustawień Praat z ich strony internetowej

Zakres widoku (Hz) : zakres częstotliwości do wyświetlenia. Standardem jest 0 Hz u dołu i 5000 Hz u góry. Jeśli ta maksymalna częstotliwość jest wyższa niż częstotliwość Nyquista dźwięku (która jest połową częstotliwości próbkowania), niektóre wartości w spektrogramie będą wynosić zero, a wyższe częstotliwości zostaną narysowane na biało. Możesz to zobaczyć, jeśli nagrywasz dźwięk z częstotliwością 44100 Hz i ustawisz zakres widoku od 0 Hz do 25000 Hz.

Długość okna : czas trwania okna analizy. Jeśli jest to 0,005 sekundy (standard), Praat używa dla każdej klatki tej części dźwięku, która znajduje się między 0,0025 sekundy przed i 0,0025 sekundy po środku tej ramki (w przypadku okien gaussowskich Praat faktycznie używa nieco więcej). Długość okna określa szerokość pasma analizy spektralnej, tj. Szerokość linii poziomej w spektrogramie czystej fali sinusoidalnej (patrz poniżej). W przypadku okna gaussowskiego szerokość pasma -3 dB wynosi 2 * sqrt (6 * ln (2)) / (π * długość okna) lub 1,2982804 / długość okna. Aby uzyskać broad-band' spectrogram (bandwidth 260 Hz), keep the standard window length of 5 ms; to get awąskopasmowy spektrogram (szerokość pasma 43 Hz), ustaw go na 30 ms (0,03 sekundy). Pozostałe kształty okien dają nieco inne wartości.

Zakres dynamiczny (dB) : Wszystkie wartości, które przekraczają zakres dynamiczny dB poniżej maksimum (być może po kompresji dynamicznej, patrz Zaawansowane ustawienia spektrogramu ...) zostaną narysowane na biało. Wartości pośrednie mają odpowiednie odcienie szarości. Zatem jeśli najwyższy szczyt w spektrogramie ma wysokość 30 dB / Hz, a zakres dynamiczny wynosi 50 dB (co jest wartością standardową), wówczas wartości poniżej -20 dB / Hz zostaną narysowane na biało, a wartości pomiędzy -20 dB / Hz i 30 dB / Hz będą rysowane w różnych odcieniach szarości.

Link do ustawień Praat

Pytanie OP może dotyczyć różnicy kontrastu między specgramem Praat a specgramem mpl (matplotlib). Praat ma ustawienie zakresu dynamicznego , które wpływa na kontrast. Funkcja mpl nie ma podobnego ustawienia / parametru. Mpl.specgram zwraca tablicę 2D poziomów mocy (spektrogram), zakres dynamiczny można zastosować do tablicy powrotnej i ponownie wykreślić.

Poniżej znajduje się fragment kodu umożliwiający utworzenie poniższych wykresów. Przykładem jest ~ 1m15s mowy z ćwierkaniem od 20 Hz do 8000 Hz.

import numpy
import pylab
import wave
import array
pylab.close('all')
w1 = wave.open('example_no_noise.wav')
w2 = wave.open('example_noise.wav')
# hmmm, probably a better way to do this, scipy.io function?
x1 = numpy.array(array.array('h', w1.readframes(w1.getnframes())))
x2 = numpy.array(array.array('h', w2.readframes(w2.getnframes())))
x1 = x1 / (2.**(16-1))  # normalize
x2 = x2 / (2.**(16-1))  # normalize
Fs = 16000.
NFFT = int(Fs*0.005)  # 5ms window
noverlap = int(Fs*0.0025)
pylab.figure(1)
pylab.specgram(x1, NFFT=NFFT, Fs=Fs, noverlap=noverlap, 
               cmap=pylab.get_cmap('Greys'))
pylab.title('Full 1m15s example min noise')
pylab.figure(2)
pylab.specgram(x2, NFFT=NFFT, Fs=Fs, noverlap=noverlap, 
               cmap=pylab.get_cmap('Greys'))
pylab.title('Full 1m15s example more noise')
pylab.figure(3); n=2100*176;
pylab.specgram(x2[n:n+256*256], NFFT=NFFT, Fs=Fs, noverlap=noverlap, 
               cmap=pylab.get_cmap('Greys'))
pylab.title('Full ~4s example min noise')
pylab.figure(4); pylab.plot(x1[n:n+256*256])

Christopher Felton
źródło
1
Myśląc o tym trochę więcej, parametr „Zakres dynamiczny” Praata może być głównym czynnikiem wpływającym na różnicę w wyglądzie wykresów. „Zakres dynamiczny” Praata może ograniczać zasięg (kompresowanie), aby uzyskać większy kontrast w fabule. BOMK MPL nie ma podobnej funkcji, ale można ją dodać.
Christopher Felton
6

Wydaje się, że jest to problem z rozdzielczością czas / częstotliwość. Twój wykres Praata ma gorszą rozdzielczość częstotliwości (nawet nie widać wyraźnie harmonicznych) i lepszą rozdzielczość czasową. Spróbuj zmniejszyć rozmiar okna (NFFT) do 16000 x 0,05 = 80 próbek. Sugerowałbym użycie większej mocy 2 w pad_to (128 lub 256).

fenenety
źródło