예제 #1
0
def cross_spectral_density(s1,
                           s2,
                           sample_rate,
                           window_length,
                           increment,
                           min_freq=0,
                           max_freq=np.inf):
    """ Computes the cross-spectral density between signals s1 and s2 by computing the product of their power spectra.
        First the Gaussian-windowed spectrograms of s1 and s2 are computed, then the product of the power spectra
        at each time point is computed, then the products are averaged across time to produce an estimate of the
        cross spectral density.

    :param s1: The first signal.
    :param s2: The second signal.
    :param sample_rate: The sample rates of the signals.
    :param window_length: The length of the window used to compute the STFT (units=seconds)
    :param increment: The spacing between the points of the STFT  (units=seconds)
    :param min_freq: The minimum frequency to analyze (units=Hz, default=0)
    :param max_freq: The maximum frequency to analysize (units=Hz, default=nyquist frequency)

    :return: freq,csd: The frequency bands evaluated, and the cross-spectral density.
    """

    # compute the complex-spectrograms of the signals
    t1, freq1, tf1, rms1 = gaussian_stft(s1,
                                         sample_rate,
                                         window_length=window_length,
                                         increment=increment,
                                         min_freq=min_freq,
                                         max_freq=max_freq)

    t2, freq2, tf2, rms2 = gaussian_stft(s2,
                                         sample_rate,
                                         window_length=window_length,
                                         increment=increment,
                                         min_freq=min_freq,
                                         max_freq=max_freq)

    # multiply the complex spectrograms
    csd = tf1 * tf2

    # take the power spectrum
    csd = np.abs(csd)

    # average across time
    csd = csd.mean(axis=1)

    return freq1, csd
예제 #2
0
    def test_delta(self):

        dur = 30.
        sample_rate = 1e3
        nt = int(dur * sample_rate)
        t = np.arange(nt) / sample_rate
        freqs = np.linspace(0.5, 1.5, nt)
        # freqs = np.ones_like(t)*2.
        s = np.sin(2 * np.pi * freqs * t)

        center_freqs = np.arange(0.5, 4.5, 0.5)

        psi = lambda _t, _f, _bw: (np.pi * _bw**2)**(-0.5) * np.exp(
            2 * np.pi * complex(0, 1) * _f * _t) * np.exp(-_t**2 / _bw**2)
        """
        scalogram = np.zeros([len(center_freqs), nt])
        bandwidth = 1.
        nstd = 6
        nwt = int(bandwidth*nstd*sample_rate)
        wt = np.arange(nwt) / sample_rate

        for k,f in enumerate(center_freqs):
            w = psi(wt, f, bandwidth)
            scalogram[k, :] = convolve1d(s, w)
        """

        win_len = 2.
        spec_t, spec_freq, spec, spec_rms = gaussian_stft(
            s, sample_rate, win_len, 100e-3)

        fi = (spec_freq < 10) & (spec_freq > 0)

        plt.figure()
        gs = plt.GridSpec(100, 1)
        ax = plt.subplot(gs[:30, 0])
        plt.plot(t, s, 'k-', linewidth=4.0, alpha=0.7)

        wa = WaveletAnalysis(s, dt=1. / sample_rate, frequency=True)

        ax = plt.subplot(gs[35:, 0])
        power = wa.wavelet_power
        scales = wa.scales
        t = wa.time
        T, S = np.meshgrid(t, scales)
        # ax.contourf(T, S, power, 100)
        # ax.set_yscale('log')
        # plt.imshow(np.abs(scalogram)**2, interpolation='nearest', aspect='auto', cmap=plt.cm.afmhot_r, origin='lower',
        #            extent=[t.min(), t.max(), min(center_freqs), max(center_freqs)])
        plot_spectrogram(spec_t,
                         spec_freq[fi],
                         np.abs(spec[fi, :])**2,
                         ax=ax,
                         colorbar=False,
                         colormap=plt.cm.afmhot_r)
        plt.plot(t, freqs, 'k-', alpha=0.7, linewidth=4.0)
        plt.axis('tight')
        plt.show()
예제 #3
0
    def load_spectrogram(self, stim_id):
        sound = self.sound_manager.reconstruct(stim_id)
        t, freq, timefreq, rms = gaussian_stft(sound.squeeze(),
                                               float(sound.samplerate),
                                               self.window_length,
                                               self.spec_inc,
                                               min_freq=self.freq_range[0],
                                               max_freq=self.freq_range[1])
        timefreq = np.abs(timefreq)**2
        self.specs[stim_id] = (t, freq, timefreq, sound)

        if self.spec_freq is None:
            self.spec_freq = freq
        else:
            assert np.abs(self.spec_freq - freq).sum(
            ) == 0, "Spectrogram frequency differs for stim %d" % stim_id
def spectrogram(s,
                sample_rate,
                spec_sample_rate,
                freq_spacing,
                min_freq=0,
                max_freq=None,
                nstd=2,
                cmplx=True):
    """
        Given a sound pressure waveform, s, compute the complex spectrogram. 
        See documentation on gaussian_stft for additional details.
        Returns: 
          t, freq, timefreq, rms
              t: array of time values to use as x axis
              freq: array of frequencies to use as y axis
              timefreq: the spectrogram (a time-frequency represnetaion)
              rms : the time varying average
              
        Arguments:
            REQUIRED:
                s: sound pressssure waveform
                sample_rate: sampling rate for s in Hz
                spec_sample_rate: sampling rate for the output spectrogram in Hz. This variable sets the overlap for the windows in the STFFT.
                freq_spacing: the time-frequency scale for the spectrogram in Hz. This variable determines the width of the gaussian window. 
            
            OPTIONAL            
                complex = False: returns the absolute value
                use min_freq and max_freq to save space
                nstd = number of standard deviations of the gaussian in one window.
    """

    # We need units here!!
    increment = 1.0 / spec_sample_rate
    window_length = nstd / (2.0 * np.pi * freq_spacing)
    t, freq, timefreq, rms = gaussian_stft(s,
                                           sample_rate,
                                           window_length,
                                           increment,
                                           nstd=nstd,
                                           min_freq=min_freq,
                                           max_freq=max_freq)

    # rms = spec.std(axis=0, ddof=1)
    if cmplx == False:
        timefreq = np.abs(timefreq)

    return t, freq, timefreq, rms
예제 #5
0
def compare_stims(exp_file, stim_file, seg_file, bs_file):
    exp = Experiment.load(exp_file, stim_file)
    spec_colormap()

    all_stim_ids = list()
    for ekey in list(exp.epoch_table.keys()):
        etable = exp.epoch_table[ekey]
        stim_ids = etable['id'].unique()
        all_stim_ids.extend(stim_ids)
    stim_ids = np.unique(all_stim_ids)

    # read the manual segmentation data
    man_segs = dict()
    with open(seg_file, 'r') as f:
        lns = f.readlines()
        for ln in lns:
            x = ln.split(",")
            stim_id = int(x[0])
            stimes = [float(f) for f in x[1:]]
            assert len(
                stimes) % 2 == 0, "Uneven # of syllables for stim %d" % stim_id
            ns = len(stimes) / 2
            man_segs[stim_id] = np.array(stimes).reshape([ns, 2])

    # get the automated segmentation
    algo_segs = dict()
    bst = BiosoundTransform.load(bs_file)
    for stim_id in stim_ids:
        i = bst.stim_df.stim_id == stim_id
        d = list(zip(bst.stim_df[i].start_time, bst.stim_df[i].end_time))
        d.sort(key=operator.itemgetter(0))
        algo_segs[stim_id] = np.array(d)

    for stim_id in stim_ids:

        # get the raw sound pressure waveform
        wave = exp.sound_manager.reconstruct(stim_id)
        wave_sr = wave.samplerate
        wave = np.array(wave).squeeze()
        wave_t = np.arange(len(wave)) / wave_sr

        # compute the amplitude envelope
        amp_env = temporal_envelope(wave, wave_sr, cutoff_freq=200.0)
        amp_env /= amp_env.max()

        # compute the spectrogram
        spec_sr = 1000.
        spec_t, spec_freq, spec, spec_rms = gaussian_stft(wave,
                                                          float(wave_sr),
                                                          0.007,
                                                          1. / spec_sr,
                                                          min_freq=300.,
                                                          max_freq=8000.)
        spec = np.abs(spec)**2
        log_transform(spec, dbnoise=70)

        figsize = (23, 12)
        plt.figure(figsize=figsize)

        ax = plt.subplot(2, 1, 1)
        plot_spectrogram(spec_t,
                         spec_freq,
                         spec,
                         ax=ax,
                         colorbar=False,
                         fmin=300.,
                         fmax=8000.,
                         colormap='SpectroColorMap')
        for k, (stime, etime) in enumerate(algo_segs[stim_id]):
            plt.axvline(stime, c='k', linewidth=2.0, alpha=0.8)
            plt.axvline(etime, c='k', linewidth=2.0, alpha=0.8)
            plt.text(stime, 7000., str(k + 1), fontsize=14)
            plt.text(etime, 7000., str(k + 1), fontsize=14)
        plt.title('Algorithm Segmentation')
        plt.axis('tight')

        ax = plt.subplot(2, 1, 2)
        plot_spectrogram(spec_t,
                         spec_freq,
                         spec,
                         ax=ax,
                         colorbar=False,
                         fmin=300.,
                         fmax=8000.,
                         colormap='SpectroColorMap')
        for k, (stime, etime) in enumerate(man_segs[stim_id]):
            plt.axvline(stime, c='k', linewidth=2.0, alpha=0.8)
            plt.axvline(etime, c='k', linewidth=2.0, alpha=0.8)
            plt.text(stime, 7000., str(k + 1), fontsize=14)
            plt.text(etime, 7000., str(k + 1), fontsize=14)
        plt.title('Manual Segmentation')
        plt.axis('tight')

        plt.show()
예제 #6
0
    def show_stim(self, exp, stim_id):

        # get the raw sound pressure waveform
        wave = exp.sound_manager.reconstruct(stim_id)
        wave_sr = wave.samplerate
        wave = np.array(wave).squeeze()
        wave_t = np.arange(len(wave)) / wave_sr

        # compute the amplitude envelope
        amp_env = temporal_envelope(wave, wave_sr, cutoff_freq=200.0)
        amp_env /= amp_env.max()

        # compute the spectrogram
        spec_sr = 1000.
        spec_t, spec_freq, spec, spec_rms = gaussian_stft(wave,
                                                          float(wave_sr),
                                                          0.007,
                                                          1. / spec_sr,
                                                          min_freq=300.,
                                                          max_freq=8000.)
        spec = np.abs(spec)**2
        log_transform(spec, dbnoise=70)

        spec_ax = None
        click_points = list()
        fig = None

        def _render():
            plt.sca(spec_ax)
            plt.cla()
            plot_spectrogram(spec_t,
                             spec_freq,
                             spec,
                             ax=spec_ax,
                             colorbar=False,
                             fmin=300.,
                             fmax=8000.,
                             colormap='SpectroColorMap')
            plt.plot(wave_t, amp_env * 8000, 'k-', linewidth=3.0, alpha=0.7)
            plt.axis('tight')
            for k, cp in enumerate(click_points):
                snum = int(k / 2)
                plt.axvline(cp, c='k', linewidth=2.0, alpha=0.8)
                plt.text(cp, 7000., str(snum), fontsize=14)
            plt.draw()

        def _onclick(event):
            click_points.append(event.xdata)
            _render()

        def _onkey(event):
            _k = str(event.key)
            if _k == 'delete':
                if len(click_points) > 0:
                    click_points.remove(click_points[-1])
                    _render()

        figsize = (23, 12)
        fig = plt.figure(figsize=figsize)
        bpress_cid = fig.canvas.mpl_connect('button_press_event', _onclick)
        kpress_cid = fig.canvas.mpl_connect('key_press_event', _onkey)

        gs = plt.GridSpec(100, 1)

        ax = plt.subplot(gs[:30, 0])
        plt.plot(wave_t, wave, 'k-')
        plt.plot(wave_t, amp_env * wave.max(), 'r-', linewidth=2.0, alpha=0.8)
        plt.xlabel('Time (s)')
        plt.ylabel('Waveform')
        plt.axis('tight')

        spec_ax = plt.subplot(gs[45:, 0])
        _render()

        plt.show()

        assert len(
            click_points) % 2 == 0, "Must have an even number of click points!"
        return click_points
예제 #7
0
    def transform(self,
                  experiment,
                  stim_types_to_segment=('Ag', 'Di', 'Be', 'DC', 'Te', 'Ne',
                                         'LT', 'Th', 'song'),
                  plot=False,
                  excluded_types=tuple()):

        assert isinstance(
            experiment, Experiment
        ), 'experiment argument must be an instance of class Experiment!'

        self.bird = experiment.bird_name
        all_stim_ids = list()
        # iterate through the segments and get the stim ids from each epoch table
        for seg in experiment.get_all_segments():
            etable = experiment.get_epoch_table(seg)
            stim_ids = etable['id'].unique()
            all_stim_ids.extend(stim_ids)

        stim_ids = np.unique(all_stim_ids)

        stim_data = {
            'stim_id': list(),
            'stim_type': list(),
            'start_time': list(),
            'end_time': list(),
            'order': list()
        }

        for aprop in self.acoustic_props:
            stim_data[aprop] = list()

        # specify type-specific thresholds for segmentation
        seg_params = {
            'default': {
                'min_thresh': 0.05,
                'max_thresh': 0.25
            },
            'Ag': {
                'min_thresh': 0.05,
                'max_thresh': 0.10
            },
            'song': {
                'min_thresh': 0.05,
                'max_thresh': 0.10
            },
            'Di': {
                'min_thresh': 0.15,
                'max_thresh': 0.20
            }
        }

        for stim_id in stim_ids:

            print('Transforming stimulus {}'.format(stim_id))

            # get sound type
            si = experiment.stim_table['id'] == str(stim_id)
            assert si.sum(
            ) == 1, "Zero or more than one stimulus defined for id=%d, (si.sum()=%d)" % (
                stim_id, si.sum())
            stim_type = experiment.stim_table['type'][si].values[0]
            if stim_type == 'call':
                stim_type = experiment.stim_table['callid'][si].values[0]

            if stim_type in excluded_types:
                continue

            # get the stimulus waveform and sample rate
            sound = experiment.sound_manager.reconstruct(stim_id)
            waveform = np.array(sound.squeeze())
            sample_rate = float(sound.samplerate)
            stim_dur = len(waveform) / sample_rate

            if stim_type in stim_types_to_segment:
                # compute the spectrogram of the stim
                spec_sample_rate = 1000.
                spec_t, spec_freq, spec_stft, spec_rms = gaussian_stft(
                    waveform, sample_rate, 0.007, 1.0 / spec_sample_rate)
                spec = np.abs(spec_stft)
                nz = spec > 0
                spec[nz] = 20 * np.log10(spec[nz]) + 50
                spec[spec < 0] = 0

                # compute the amplitude envelope
                amp_env = spec_rms
                amp_env -= amp_env.min()
                amp_env /= amp_env.max()

                # segment the amplitude envelope
                minimum_isi = int(4e-3 * spec_sample_rate)
                if stim_type in seg_params:
                    min_thresh = seg_params[stim_type]['min_thresh']
                    max_thresh = seg_params[stim_type]['max_thresh']
                else:
                    min_thresh = seg_params['default']['min_thresh']
                    max_thresh = seg_params['default']['max_thresh']
                syllable_times = break_envelope_into_events(
                    amp_env,
                    threshold=min_thresh,
                    merge_thresh=minimum_isi,
                    max_amp_thresh=max_thresh)

                if plot:
                    plt.figure()
                    ax = plt.subplot(111)
                    plot_spectrogram(spec_t,
                                     spec_freq,
                                     np.abs(spec),
                                     ax=ax,
                                     fmin=300.0,
                                     fmax=8000.0,
                                     colormap=plt.cm.afmhot,
                                     colorbar=False)
                    sfd = spec_freq.max() - spec_freq.min()
                    amp_env *= sfd
                    amp_env += spec_freq.min()

                    tline = sfd * min_thresh + amp_env.min()
                    tline2 = sfd * max_thresh + amp_env.min()
                    plt.axhline(tline, c='w', alpha=0.50)
                    plt.axhline(tline2, c='w', alpha=0.50)

                    plt.plot(spec_t, amp_env, 'w-', linewidth=2.0, alpha=0.75)
                    for k, (si, ei, max_amp) in enumerate(syllable_times):
                        plt.plot(spec_t[si], 0, 'go', markersize=8)
                        plt.plot(spec_t[ei], 0, 'ro', markersize=8)
                    plt.title('stim %d, %s, minimum_isi=%d' %
                              (stim_id, stim_type, minimum_isi))
                    plt.axis('tight')
                    plt.show()

                the_order = 0
                for k, (si, ei, max_amp) in enumerate(syllable_times):

                    sii = int((si / spec_sample_rate) * sample_rate)
                    eii = int((ei / spec_sample_rate) * sample_rate)

                    s = waveform[sii:eii]
                    if len(s) < 1024:
                        continue

                    bs = BioSound(soundWave=s, fs=sample_rate)
                    bs.spectrum(f_high=8000.)
                    bs.ampenv()
                    bs.fundest()

                    stime = sii / sample_rate
                    etime = eii / sample_rate

                    stim_data['stim_id'].append(stim_id)
                    stim_data['stim_type'].append(stim_type)
                    stim_data['start_time'].append(stime)
                    stim_data['end_time'].append(etime)
                    stim_data['order'].append(the_order)
                    the_order += 1

                    for aprop in self.acoustic_props:
                        aval = getattr(bs, aprop)
                        if aval is None:
                            aval = -1
                        stim_data[aprop].append(aval)

            else:

                bs = BioSound(soundWave=waveform, fs=sample_rate)
                bs.spectrum(f_high=8000.)
                bs.ampenv()
                bs.fundest()

                stim_data['stim_id'].append(stim_id)
                stim_data['stim_type'].append(stim_type)
                stim_data['start_time'].append(0)
                stim_data['end_time'].append(stim_dur)
                stim_data['order'].append(0)

                for aprop in self.acoustic_props:
                    aval = getattr(bs, aprop)
                    stim_data[aprop].append(aval)

            self.stim_data = stim_data
            self.stim_df = pd.DataFrame(self.stim_data)
예제 #8
0
def coherence_jn(s1,
                 s2,
                 sample_rate,
                 window_length,
                 increment,
                 min_freq=0,
                 max_freq=None,
                 return_coherency=False):
    """ Computes the coherence between two signals by averaging across time-frequency representations
        created using a Gaussian-windowed Short-time Fourier Transform. Uses jacknifing to estimate
        the variance of the coherence.

    :param s1: The first signal
    :param s2: The second signal
    :param sample_rate: The sample rates of the signals.
    :param window_length: The length of the window used to compute the STFT (units=seconds)
    :param increment: The spacing between the points of the STFT  (units=seconds)
    :param min_freq: The minimum frequency to analyze (units=Hz, default=0)
    :param max_freq: The maximum frequency to analyze (units=Hz, default=nyquist frequency)
    :param return_coherency: Whether or not to return the time domain coherency (default=False)

    :return: freq,coherence,coherence_var,phase_coherence,phase_coherence_var,[coherency,coherency_t]: freq is an array
             of frequencies that the coherence was computed at. coherence is an array of length len(freq) that contains
             the coherence at each frequency. coherence_var is the variance of the coherence. phase_coherence is the
             average cosine phase difference at each frequency, and phase_coherence_var is the variance of that measure.
             coherency is only returned if return_coherency=True, it is the inverse fourier transform of the complex-
             valued coherency.
    """
    if s1.shape != s2.shape:
        raise AssertionError('s1 and s2 must have the same shape')
    if s1.ndim == 1:
        s1, s2 = [np.array(i, ndmin=2) for i in [s1, s2]]

    tf1, tf2 = list(), list()
    for i, (is1, is2) in enumerate(zip(s1, s2)):
        t1, freq1, itf1, rms1 = gaussian_stft(is1,
                                              sample_rate,
                                              window_length=window_length,
                                              increment=increment,
                                              min_freq=min_freq,
                                              max_freq=max_freq)

        t2, freq2, itf2, rms2 = gaussian_stft(is2,
                                              sample_rate,
                                              window_length=window_length,
                                              increment=increment,
                                              min_freq=min_freq,
                                              max_freq=max_freq)
        tf1.append(itf1)
        tf2.append(itf2)
    tf1, tf2 = [np.hstack(i) for i in [tf1, tf2]]

    cross_spec12 = tf1 * np.conj(tf2)
    ps1 = np.abs(tf1)**2
    ps2 = np.abs(tf2)**2

    # compute the coherence using all the data
    csd = cross_spec12.sum(axis=1)
    denom = ps1.sum(axis=1) * ps2.sum(axis=1)
    c_amp = np.abs(csd) / np.sqrt(denom)
    cohe = c_amp**2

    # compute the phase coherence using all the data
    c_phase = np.cos(np.angle(csd))

    # make leave-one-out estimates of the complex coherence
    jn_estimates_amp = list()
    jn_estimates_phase = list()
    jn_estimates_cohe = list()

    njn = tf1.shape[1]
    for k in range(njn):
        i = np.ones([njn], dtype='bool')
        i[k] = False
        csd = cross_spec12[:, i].sum(axis=1)
        denom = ps1[:, i].sum(axis=1) * ps2[:, i].sum(axis=1)
        c_amp = np.abs(csd) / np.sqrt(denom)
        jn_estimates_amp.append(c_amp)
        jn_estimates_cohe.append(njn * cohe - (njn - 1) * (c_amp**2))

        c_phase = np.cos(np.angle(csd))
        jn_estimates_phase.append(c_phase)

    jn_estimates_amp = np.array(jn_estimates_amp)
    jn_estimates_cohe = np.array(jn_estimates_cohe)
    jn_estimates_phase = np.array(jn_estimates_phase)

    # estimate the variance of the coherence
    jn_mean_amp = jn_estimates_amp.mean(axis=0)
    jn_diff_amp = (jn_estimates_amp - jn_mean_amp)**2
    c_var_amp = ((njn - 1) / float(njn)) * jn_diff_amp.sum(axis=0)
    cohe_unbiased = jn_estimates_cohe.mean(axis=0)
    cohe_se = jn_estimates_cohe.std(axis=0) / np.sqrt(njn)

    # estimate the variance of the phase coherence
    jn_mean_phase = jn_estimates_phase.mean(axis=0)
    jn_diff_phase = (jn_estimates_phase - jn_mean_phase)**2
    c_phase_var = ((njn - 1) / float(njn)) * jn_diff_phase.sum(axis=0)

    assert c_amp.max() <= 1.0, "c_amp.max()=%f" % c_amp.max()
    assert c_amp.min() >= 0.0, "c_amp.min()=%f" % c_amp.min()
    assert np.sum(np.isnan(c_amp)) == 0, "NaNs in c_amp!"

    if return_coherency:
        # compute the complex-valued coherency
        z = csd / denom

        # make the complex-valued coherency symmetric around zero
        sym_z = np.zeros([len(z) * 2 - 1], dtype='complex')
        sym_z[len(z) - 1:] = z
        sym_z[:len(z) - 1] = (z[1:])[::-1]

        # do an fft shift so the inverse fourier transform works
        sym_z = fftshift(sym_z)

        if len(sym_z) % 2 == 1:
            # shift zero from end of shift_lags to beginning
            sym_z = np.roll(sym_z, 1)

        coherency = ifft(sym_z)
        coherency = fftshift(coherency.real)

        dt = 1. / sample_rate
        hc = (len(coherency) - 1) / 2
        coherency_t = np.arange(-hc, hc + 1, 1) * dt
        """
        import matplotlib.pyplot as plt
        plt.figure()
        plt.plot(coherency_t, coherency, 'k-')
        plt.axis('tight')
        plt.show()
        """

        return freq1, c_amp, c_var_amp, c_phase, c_phase_var, coherency, coherency_t, cohe_unbiased, cohe_se
    else:
        return freq1, c_amp, c_var_amp, c_phase, c_phase_var, cohe_unbiased, cohe_se