reassignment

Exploring reassignment with libtfr, a nicely documented library from Dan Meliza, available on GitHub.

Prelims


In [1]:
import numpy as np
from numpy.fft import fft, fftfreq
import matplotlib.pyplot as plt
%matplotlib inline

from pylab import specgram

Basic use

Using the Homer simpson voice from the Glowing Python blog.


In [2]:
# Import the libraries we'll need
import urllib2
import StringIO
import zipfile
 
# Read the data from the web.
web_data = urllib2.urlopen('http://www.thesoundarchive.com/simpsons/homer/mcrumble.wav')
 
# Convert the bytes into a file-like object in memory
wav_in_memory = StringIO.StringIO(web_data.read())

In [3]:
import scipy.io.wavfile as wavfile

rate,data = wavfile.read(wav_in_memory)

plt.figure(figsize=(12,8))
plt.subplot(411)
plt.plot(range(len(data)),data)
plt.subplot(412)

# NFFT is the number of data points used in each block for the FFT
# and noverlap is the number of points of overlap between blocks
plt.specgram(data, NFFT=128, noverlap=64) # small window
plt.subplot(413)
plt.specgram(data, NFFT=512, noverlap=128) 
plt.subplot(414)
plt.specgram(data, NFFT=1024, noverlap=512) # big window

plt.show()


/Library/Python/2.7/site-packages/scipy-0.14.0.dev_7cefb25-py2.7-macosx-10.9-intel.egg/scipy/io/wavfile.py:172: WavFileWarning: Chunk (non-data) not understood, skipping it.
  WavFileWarning)

In [4]:
data = np.array(data, dtype=float)
data


Out[4]:
array([ 128.,  128.,  128., ...,  128.,  128.,  128.])

In [5]:
data.shape


Out[5]:
(39424,)

Short-time Fourier transform spectrogram in libtfr


In [6]:
import libtfr

In [7]:
N = 256
step = 10
NW = 3.5

In [8]:
w = np.hamming(N)
S_s = libtfr.stft(data, w, step)
S_s.shape


Out[8]:
(129, 3916)

In [9]:
S_up = np.flipud(S_s)

plt.figure(figsize=(18,5))
plt.imshow(20*np.log10(S_up), aspect=10)
plt.colorbar(shrink=0.9)
#ax1.set_ylim((0,64))
plt.show()


Multi-taper transform spectrogram in libtfr

Using discrete prolate spherical sequence (DPSS) tapers.

We can also do the 1D transform:


In [10]:
J = libtfr.mtfft(data, NW)
J.shape


Out[10]:
(39424, 6)

I don't know what this is.

Let's try the spectrogram...


In [11]:
S_m = libtfr.mtm_spec(data, N, step, NW)
S_m.shape


Out[11]:
(129, 3916)

In [12]:
S_mup = np.flipud(S_m)

plt.figure(figsize=(18,5))
plt.imshow(20*np.log10(S_mup), aspect=10)
plt.colorbar(shrink=0.9)
#ax1.set_ylim((0,64))
plt.show()


Reassignment spectrogram again


In [13]:
# Input signal is real
N = 1024      # number of frequency points
Np = 512     # window size, should be <= N
step = 40    # step size, in time points
K = 6        # number of tapers, default 6
tm = 1.0     # time support of tapers, default 6.0
flock = 0.01  # freq locking; power is not reassigned more than this value, default 0.01
tlock = 5    # time lock, in frames, default 5

# Output freq bins, monotonically increasing,
# 1.0 is Nyquist. Default linear scale with N points.
fgrid = np.linspace(0.1, 0.475, 512)

In [14]:
#S = libtfr.tfr_spec(data, N, shift, Np, K, tm, flock, tlock)
S_t = libtfr.tfr_spec(data, N, step, Np)
S_t.shape


Out[14]:
(513, 972)

In [15]:
S_tup = np.flipud(S_t)

plt.figure(figsize=(18,5))
plt.imshow(20*np.log10(S_tup), aspect=0.67)
plt.colorbar(shrink=0.6)
plt.show()


-c:4: RuntimeWarning: divide by zero encountered in log10

Two chirps


In [16]:
import scipy.signal

In [17]:
timebase = np.arange(0,6,0.001)

f0 = 1.0
t1 = 6.0
f1 = 64.0
ch = scipy.signal.chirp(timebase, f0, t1, f1, method='quadratic')

f02 = 128.0
t12 = 6.0
f12 = 8.0
ch2 = scipy.signal.chirp(timebase, f02, t12, f12, method='logarithmic')

In [18]:
ax1 = plt.subplot(111)
plt.specgram(ch+ch2, Fs=1000)[3]
ax1.set_ylim((0,128))
plt.show()



In [19]:
N = 2048      # number of frequency points
Np = 512     # window size, should be <= N
step = 40    # step size, in time points
K = 6        # number of tapers, default 6
tm = 6.0     # time support of tapers, default 6.0

chrp = ch + ch2

CHRP_t = libtfr.tfr_spec(chrp, N, step, Np, K, tm)
CHRP_tup = np.flipud(CHRP_t)[-256:,:]

plt.figure(figsize=(8,5))
plt.imshow(20*np.log10(CHRP_tup), aspect=0.4)
plt.colorbar(shrink=0.6)
plt.show()


-c:13: RuntimeWarning: divide by zero encountered in log10

Reading benchmark signals

Trying to read the signals for the workshop...

Speech

1400 samples, $f_\mathrm{s} = 8000 \mathrm{Hz}$


In [20]:
speech_filename = 'benchmark_signals/speech.txt'
with open(speech_filename) as f:
    s = f.readlines()
s[:10]


Out[20]:
['  -1.5500000e+02\r\n',
 '  -4.5000000e+01\r\n',
 '   1.3900000e+02\r\n',
 '   1.7100000e+02\r\n',
 '   5.7000000e+01\r\n',
 '  -3.3000000e+01\r\n',
 '  -4.0000000e+00\r\n',
 '   1.5500000e+02\r\n',
 '   2.1100000e+02\r\n',
 '   2.1100000e+02\r\n']

In [21]:
speech = np.loadtxt(speech_filename)

In [22]:
speech.size


Out[22]:
1400

In [23]:
fs_speech = 8000 # sampling frequency, in Hz

In [153]:
# Let's make a function to plot a signal and its specgram

def tf(signal, fs, w=256, wtime=False, poverlap=None, ylim=None, colorbar=False, vmin=None, vmax=None):
    '''
    Plot a 1D signal and its spectrogram
    
    signal: a 1D array of amplitudes
    fs: the sampling frequency
    w: the window length
    '''
    
    dt = 1./fs
    n = signal.size
    t = np.arange(0.0, n*dt, dt)
        
    # add some noise into the mix
    #nse = 0.01*np.random.randn(len(t))
    
    if wtime:
        # Then the window length is time so change to samples
        w *= fs
        w = int(w)
        
    if poverlap:
        # Then overlap is a percentage
        noverlap = int(w * poverlap/100.)
    else:
        noverlap = w - 1
        
    # Pxx is the segments x freqs array of instantaneous power, freqs is
    # the frequency vector, bins are the centers of the time bins in which
    # the power is computed, and im is the matplotlib.image.AxesImage
    # instance
    
    plt.figure(figsize=(12,8))
    plt.subplot(211)
    plt.plot(t, signal)
    ax1 = plt.subplot(212)
    Pxx, freqs, bins, im = plt.specgram(signal, NFFT=w, Fs=fs, noverlap=noverlap, cmap='Greys', vmin=vmin, vmax=vmax)
    if colorbar: plt.colorbar()
    if ylim:
        ax1.set_ylim((0,ylim))
    plt.show()

In [147]:
tf(speech, 8000)


Synthetic


In [142]:
synthetic = np.loadtxt('benchmark_signals/synthetic.txt')

In [143]:
tf(synthetic, 800, w=128, ylim=256, vmin=-30)



In [144]:
# Input signal is real
N = 2048      # number of frequency points
Np = 512     # window size, should be <= N
step = 40    # step size, in time points
K = 5        # number of tapers, default 6
tm = 6.0     # time support of tapers, default 6.0
flock = 0.01  # freq locking; power is not reassigned more than this value, default 0.01
tlock = 5    # time lock, in frames, default 5

# Output freq bins, monotonically increasing,
# 1.0 is Nyquist. Default linear scale with N points.
fgrid = np.linspace(0.1, 0.475, 512)

In [59]:
#S = libtfr.tfr_spec(data, N, shift, Np, K, tm, flock, tlock)
SYN_t = libtfr.tfr_spec(synthetic, N, step, Np, K, tm)
SYN_t.shape


Out[59]:
(1025, 192)

In [97]:
SYN_tup = np.flipud(SYN_t)

plt.figure(figsize=(18,5))
plt.imshow(20*np.log10(SYN_tup), cmap="Greys", aspect=0.1, vmin=-10)
plt.colorbar(shrink=0.6)
plt.show()


Seismic trace


In [98]:
seismic = np.loadtxt('benchmark_signals/seismic_trace.txt')

In [103]:
tf(seismic, 500, w=128, ylim=128, vmin=40)



In [33]:
# Input signal is real
N = 1024      # number of frequency points
Np = 128     # window size, should be <= N
step = 20    # step size, in time points
K = 6        # number of tapers, default 6
tm = 6.0     # time support of tapers, default 6.0
flock = 0.01  # freq locking; power is not reassigned more than this value, default 0.01
tlock = 5    # time lock, in frames, default 5

# Output freq bins, monotonically increasing,
# 1.0 is Nyquist. Default linear scale with N points.
fgrid = np.linspace(0.1, 0.475, 512)

In [34]:
#S = libtfr.tfr_spec(data, N, shift, Np, K, tm, flock, tlock)
SEIS_t = libtfr.tfr_spec(seismic, N, step, Np, K, tm)
SEIS_t.shape


Out[34]:
(513, 31)

In [108]:
SEIS_tup = np.flipud(SEIS_t)

plt.figure(figsize=(18,5))
plt.imshow(20*np.log10(SEIS_tup), aspect=0.02, cmap="Greys", vmin=140)
plt.colorbar(shrink=0.6)
plt.show()


Volcanic tremor


In [109]:
tremor = np.loadtxt('benchmark_signals/tremor.txt')

In [125]:
tf(tremor, 100, vmin=10)



In [124]:
# Input signal is real
N = 2048      # number of frequency points
Np = 512     # window size, should be <= N
step = 40    # step size, in time points
K = 6        # number of tapers, default 6
tm = 6.0     # time support of tapers, default 6.0

TREMOR_t = libtfr.tfr_spec(tremor, N, step, Np, K, tm)
TREMOR_tup = np.flipud(TREMOR_t)

plt.figure(figsize=(18,5))
plt.imshow(20*np.log10(TREMOR_tup), aspect=0.5, cmap="Greys", vmin=50)
plt.colorbar(shrink=0.6)
plt.show()


Microseismic event


In [126]:
microseismic = np.loadtxt('benchmark_signals/microseismic.txt')

In [152]:
tf(microseismic, 2000, w=0.03, wtime=True, poverlap=99, vmin=0)


Tohoku earthquake


In [41]:
tohoku = np.loadtxt('benchmark_signals/tohoku.txt')

In [163]:
tf(tohoku, 20, w=100, wtime=True, ylim=0.5, vmin=70)



In [ ]: