Melhorando a resolução do espectrograma em Python?

21

Estou usando a specgram()função matplotlibpara gerar espectrogramas de arquivos de ondas de fala em Python, mas a saída é sempre de qualidade muito inferior à que meu software normal de transcrição, Praat, pode gerar. Por exemplo, a seguinte chamada:

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

Gera isso:

insira a descrição da imagem aqui

Enquanto Praat, trabalhando na mesma amostra de áudio com as seguintes configurações:

  • Faixa de visualização: 0-8000Hz
  • Comprimento da janela: 0.005s
  • Faixa dinâmica: 70dB
  • Etapas do tempo: 1000
  • Etapas de frequência: 250
  • Forma da janela: Gaussian

Gera isso:

insira a descrição da imagem aqui

O que estou fazendo errado? Eu tentei mexer com todos os specgram()parâmetros, mas nada parece melhorar a resolução. Não tenho praticamente nenhuma experiência com FFTs.

Alek Storm
fonte
Você poderia fornecer o exemplo das configurações de parâmetro matplotlib.specgram que você tentou? Você fornece um exemplo muito específico dos parâmetros para o Praat, mas não mostra a mesma configuração para matplotlib.specgram?
Christopher Felton

Respostas:

11

Aqui estão os parâmetros 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)

Os parâmetros fornecidos na descrição da pergunta precisam ser convertidos em parâmetros comparáveis ​​de mpl.specgram. A seguir, um exemplo do mapeamento:

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

Se você usar 8ms, obterá uma potência de 2 FFT (128). A seguir, é apresentada a descrição das configurações do Praat no site

Faixa de visualização (Hz) : a faixa de frequências a serem exibidas. O padrão é 0 Hz na parte inferior e 5000 Hz na parte superior. Se essa frequência máxima for maior que a frequência Nyquist do som (que é metade da frequência de amostragem), alguns valores no espectrograma serão zero e as frequências mais altas serão desenhadas em branco. Você pode ver isso se gravar um som em 44100 Hz e definir o intervalo de visualização de 0 Hz a 25000 Hz.

Comprimento da janela : a duração da janela de análise. Se este for 0,005 segundos (o padrão), o Praat usa para cada quadro a parte do som que fica entre 0,0025 segundos antes e 0,0025 segundos após o centro desse quadro (para janelas gaussianas, o Praat realmente usa um pouco mais do que isso). O comprimento da janela determina a largura de banda da análise espectral, ou seja, a largura da linha horizontal no espectrograma de uma onda senoidal pura (veja abaixo). Para uma janela gaussiana, a largura de banda de -3 dB é 2 * sqrt (6 * ln (2)) / (π * comprimento da janela) ou 1,2982804 / comprimento da janela. Para obter um broad-band' spectrogram (bandwidth 260 Hz), keep the standard window length of 5 ms; to get aespectrograma de banda estreita (largura de banda 43 Hz), configure-o para 30 ms (0,03 segundos). As outras formas de janela fornecem valores ligeiramente diferentes.

Faixa dinâmica (dB) : todos os valores acima da faixa dinâmica dB abaixo do máximo (talvez após a compressão dinâmica, consulte Configurações avançadas do espectrograma ...) serão desenhados em branco. Os valores intermediários têm tons apropriados de cinza. Assim, se o pico mais alto do espectrograma tiver uma altura de 30 dB / Hz e a faixa dinâmica for 50 dB (que é o valor padrão), valores abaixo de -20 dB / Hz serão desenhados em branco e os valores entre -20 dB / Hz e 30 dB / Hz serão desenhados em vários tons de cinza.

Link para configurações do Praat

A pergunta do OP pode estar relacionada à diferença de contraste entre o espectrograma Praat e o espectrograma mpl (matplotlib). O Praat possui uma configuração de Faixa dinâmica que afeta o contraste. A função mpl não possui uma configuração / parâmetro semelhante. O mpl.specgram retorna a matriz 2D de níveis de potência (o espectrograma), a faixa dinâmica pode ser aplicada à matriz de retorno e plotada novamente.

A seguir, é um snippet de código para criar os gráficos abaixo. O exemplo é a fala de ~ 1m15s com um chilro de 20Hz a 8000Hz.

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
fonte
1
Pensando um pouco mais sobre isso, o parâmetro Praat "Dynamic Range" pode ser o principal fator para a diferença na aparência das plotagens. O Praat "Dynamic Range" pode estar limitando o alcance (compressão), para que você obtenha um contraste maior na plotagem. O MPL do BOMK não possui um recurso semelhante, mas um pode ser adicionado.
Christopher Felton
6

Parece ser um problema de resolução de tempo / frequência. Seu gráfico do Praat tem uma resolução de frequência pior (você nem consegue ver claramente os harmônicos) e uma resolução de tempo melhor. Tente reduzir o tamanho da janela (NFFT) para 16000 x 0,05 = 80 amostras. Eu sugiro usar uma potência maior de 2 em pad_to (128 ou 256).

pichenettes
fonte