How to convert a spectrogram to 3d plot. Python
Asked Answered
E

1

8

I am trying to achieve waterfall graph of wav file. In my attempts I noticed that this is basically a spectogram in 3d (or as close to what I need). I am trying to do this in Python with numpy and matplotlib.

My main problem is I don't know how to change the plot of specgram from matplotlib into a 3d plot.

Sample of my "code":

sample ,data = wavfile.read('file.wav')
F = Figure()
a = F.add_subplot(111,projection='3d') 
Spec, t, freq, im = a.specgram(data,Fs=2)

I've got this far and have no clue what to do next. I wanna change already existing plot into 3d. I have no code of changing it to 3d, due to lack of my knowledge.

Is it possible to convert 2d plot to 3d ? If so how ? Am i better off constructing a new plot with data from specgram?

The desired outcome would be something like the following: desired result another desired result Thanks for any replies.

Equine answered 3/2, 2018 at 15:13 Comment(8)
Okay. Sorry for being too broad. I improved my question/Equine
Have you tried plot_surface? The actual problem is still not clear to me.Nepos
While the images will probably help a mathematician to understand the higher order problem you are trying to solve, they don't add that much to understanding what specific programming question you have. The best way to get specific answers on Stack Overflow is to include actual code that you wrote trying to solve your problem.Surety
... and to add, of course code needs to be a minimal reproducible example.Nepos
No, you cannot "convert" a 2D matplotlib plot to a 3D matplotlib plot. Those are different things. You need to create a 3D plot and plot some data to it using any of the methods provided by the Axes3D class. As said in a previous comment, I'd use plot_surface for that.Nepos
From my understanding of the question you're looking to plot a heightmap. This question suggests that Mayavi has a solution for this #30707419Shayn
Thanks for the answer ! @Nepos i should be able to take output of specgram and use it to plot surface, right ?Equine
You may use spec, freqs, t = matplotlib.mlab.specgram(...) to generate the data without automatically creating a plot.Nepos
D
3

Here are 3D & 2D spectrogram plots of an example signal from scipy that you can find at the end of this page.

enter image description here

enter image description here

from matplotlib import mlab
import matplotlib.pyplot as plt
import numpy as np

# Fixing random state for reproducibility
np.random.seed(666)

title = ('2 Vrms sine wave with modulated frequency around 3kHz, '
         'corrupted by white noise of exponentially decreasing '
         'magnitude sampled at 10 kHz.')

fs = 10e3
N = 1e5
amp = 2 * np.sqrt(2)
noise_power = 0.01 * fs / 2
t = np.arange(N) / float(fs)
mod = 500*np.cos(2*np.pi*0.25*t)
carrier = amp * np.sin(2*np.pi*3e3*t + mod)
noise = np.random.normal(scale=np.sqrt(noise_power), size=t.shape)
noise *= np.exp(-t/5)
y = carrier + noise

def specgram3d(y, srate=44100, ax=None, title=None):
  if not ax:
    ax = plt.axes(projection='3d')
  ax.set_title(title, loc='center', wrap=True)
  spec, freqs, t = mlab.specgram(y, Fs=srate)
  X, Y, Z = t[None, :], freqs[:, None],  20.0 * np.log10(spec)
  ax.plot_surface(X, Y, Z, cmap='viridis')
  ax.set_xlabel('time (s)')
  ax.set_ylabel('frequencies (Hz)')
  ax.set_zlabel('amplitude (dB)')
  ax.set_zlim(-140, 0)
  return X, Y, Z

def specgram2d(y, srate=44100, ax=None, title=None):
  if not ax:
    ax = plt.axes()
  ax.set_title(title, loc='center', wrap=True)
  spec, freqs, t, im = ax.specgram(y, Fs=fs, scale='dB', vmax=0)
  ax.set_xlabel('time (s)')
  ax.set_ylabel('frequencies (Hz)')
  cbar = plt.colorbar(im, ax=ax)
  cbar.set_label('Amplitude (dB)')
  cbar.minorticks_on()
  return spec, freqs, t, im

fig1, ax1 = plt.subplots()
specgram2d(y, srate=fs, title=title, ax=ax1)

fig2, ax2 = plt.subplots(subplot_kw={'projection': '3d'})
specgram3d(y, srate=fs, title=title, ax=ax2)
  
plt.show()

BONUS:

You can listen to the signal by creating a wav file using scipy:

from scipy.io import wavfile
wavfile.write('sig.wav', int(fs), y)
Deration answered 16/3, 2021 at 4:54 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.