Неправильная спектрограмма при использовании scipy.signal.spectrogram
Когда я использую plt.specgram из matplotlib, используя следующий код, сгенерированная спектрограмма является правильной
import matplotlib.pyplot as plt
from scipy import signal
from scipy.io import wavfile
import numpy as np
sample_rate, samples = wavfile.read('.\\Wav\\test.wav')
Pxx, freqs, bins, im = plt.specgram(samples[:,1], NFFT=1024, Fs=44100, noverlap=900)
Однако, если я сгенерирую спектрограмму, используя пример кода, приведенного на странице scipy со следующим кодом, я получу что-то вроде этого:
import matplotlib.pyplot as plt
from scipy import signal
from scipy.io import wavfile
import numpy as np
sample_rate, samples = wavfile.read('.\\Wav\\test.wav')
frequencies, times, spectrogram = signal.spectrogram(samples[:,1],sample_rate,nfft=1024,noverlap=900, nperseg=1024)
plt.pcolormesh(times, frequencies, spectrogram)
plt.ylabel('Frequency [Hz]')
plt.xlabel('Time [sec]')
Чтобы отладить происходящее, я попытался использовать Pxx
, freqs
, bins
, сгенерированный первым методом, а затем используйте второй метод для построения графика данных:
plt.pcolormesh(bins, freqs, Pxx)
plt.ylabel('Frequency [Hz]')
plt.xlabel('Time [sec]')
Сгенерированный граф почти такой же, как и сгенерированный вторым методом. Так что, похоже, нет проблем с scipy.signal.spectrogram
в конце концов. Проблема в том, как мы строим график. Интересно, если plt.pcolormesh
является правильным способом построения спектрограммы, несмотря на то, что этот метод предложен в документе Scipy
Подобный вопрос был задан здесь, но пока нет решения вопроса.
3 ответа
Режим масштабирования по умолчанию для "Спектрама" - "дБ" (из документации по программе)
масштаб: ['по умолчанию' | "линейный" | 'дБ'] Масштабирование значений в спецификации. "линейный" не масштабирование. "дБ" возвращает значения в масштабе дБ. Когда режим "psd", это мощность дБ (10 * log10). В противном случае это амплитуда дБ (20 * log10). "default" - это "дБ", если mode - "psd" или "magnitude" и "linear" в противном случае. Это должно быть "линейно", если режим "угол" или "фаза".
режим: ['по умолчанию' | "PSD" | "величина" | "угол" | 'фаза'] Какой спектр использовать. По умолчанию используется значение "psd", которое принимает спектральную плотность мощности. 'complex' возвращает комплексный частотный спектр. "Величина" возвращает спектр величин. "угол" возвращает фазовый спектр без развертывания. "фаза" возвращает фазовый спектр с распаковкой.
Для достижения аналогичных результатов с pcolormesh
вам нужно будет масштабировать данные эквивалентно.
plt.pcolormesh(times, frequencies, 10*np.log10(spectrogram))
Я не думаю, что пример pcolormesh правильный в своем масштабе. Вы можете ясно видеть несущую в примере, но добавленный шумовой сигнал не виден.
Вы должны использовать одну из нелинейных цветовых карт в своем
pcolormesh
функция.
Попробуйте установить
norm=matplotlib.colors.LogNorm(vmin=np.amin(spectrogram), vmax=np.amax(spectrogram))
Или
norm=matplotlib.colors.PowerNorm(gamma=0.5)
.
См. Https://matplotlib.org/stable/tutorials/colors/colormapnorms.html для получения дополнительной информации.
Используйте это вместо:
plt.pcolormesh(times, frequencies, spectrogram, norm = matplotlib.colors.Normalize(0,1))
Это нормализует данные перед построением графика, чтобы вы могли правильно визуализировать цвет. В документации на matplotlib.colors.Colormap говорится: "Обычно экземпляры Colormap используются для преобразования значений данных (с плавающей запятой) из интервала [0, 1] в цвет RGBA, который представляет соответствующая Colormap". Если ваши значения находятся за пределами этого диапазона, он, вероятно, отобразит его в темном цвете (я полагаю).