コード例 #1
0
    def __init__(self, camera, parent=None, fcn=None):
        # Initialize PrepareData :
        PrepareData.__init__(self, axis=0)

        # Keep camera :
        self._camera = camera
        self._rect = (0., 0., 0., 0.)
        self._fcn = fcn

        # Time-frequency map
        self.tf = TFmapsMesh(parent=parent)
        # Spectrogram
        self.mesh = scene.visuals.Image(np.zeros((2, 2)), parent=parent,
                                        name='Fourier transform')
        self.mesh.transform = vist.STTransform()
コード例 #2
0
    def __init__(self,
                 time,
                 sf,
                 sh,
                 axis,
                 form='line',
                 line_rendering='gl',
                 parent=None):
        """Init."""
        self.form = form
        self._time = time
        self._sf = sf
        self._axis = axis
        self._index = 0  # selected index of the 3-d array
        self.rect = (0., 0., 1., 1.)
        self._prep = PrepareData(way='filtfilt')
        # Build navigation index :
        if len(sh) in [2, 3]:
            sh = list(sh)
            del sh[axis]
            self._navidx = list(product(*tuple(range(k) for k in sh)))
        else:
            self._navidx = [[]]

        # Create visuals :
        pos = np.random.rand(2, 2)
        posh = np.random.rand(2, )
        self._th = scene.visuals.Line(pos=pos, name='threshold', parent=parent)
        self._line = scene.visuals.Line(pos=pos,
                                        name='line',
                                        parent=parent,
                                        method=line_rendering)
        self._mark = scene.visuals.Markers(pos=pos,
                                           name='marker',
                                           parent=parent)
        self._hist = scene.visuals.Histogram(data=posh,
                                             orientation='h',
                                             parent=parent,
                                             name='histogram')
        self._tf = TFmapsMesh(parent=parent)
        self._th.visible = False

        # Initialize annotations :
        SignalAnnotations.__init__(self, parent)
コード例 #3
0
class SignalVisual(SignalAnnotations):
    """1-D signal class creation.

    Parameters
    ----------
    time : array_like
        Time vector of shape (N,)
    sf : float
        The sampling frequency.
    sh : tuple
        Shape of the 2-D array.
    form : {'line', 'marker', 'histogram'}
        Plotting type.
    color : array_like/string/tuple | 'black'
        Color of the plot.
    lw : float | 2.
        Line width (form='line').
    symbol : string | 'o'
        Marker symbol (form='marker').
    size : float | 10.
        Marker size (form='marker').
    nbins : int | 10
        Number of bins for the histogram (form='histogram')
    line_rendering : {'gl', 'agg'}
        Specify the line rendering method. Use 'gl' for a fast but lower
        quality lines and 'agg', looks better but slower.
    parent : VisPy.parent | None
        Parent of the mesh.
    """
    def __init__(self,
                 time,
                 sf,
                 sh,
                 axis,
                 form='line',
                 line_rendering='gl',
                 parent=None):
        """Init."""
        self.form = form
        self._time = time
        self._sf = sf
        self._axis = axis
        self._index = 0  # selected index of the 3-d array
        self.rect = (0., 0., 1., 1.)
        self._prep = PrepareData(way='filtfilt')
        # Build navigation index :
        if len(sh) in [2, 3]:
            sh = list(sh)
            del sh[axis]
            self._navidx = list(product(*tuple(range(k) for k in sh)))
        else:
            self._navidx = [[]]

        # Create visuals :
        pos = np.random.rand(2, 2)
        posh = np.random.rand(2, )
        self._th = scene.visuals.Line(pos=pos, name='threshold', parent=parent)
        self._line = scene.visuals.Line(pos=pos,
                                        name='line',
                                        parent=parent,
                                        method=line_rendering)
        self._mark = scene.visuals.Markers(pos=pos,
                                           name='marker',
                                           parent=parent)
        self._hist = scene.visuals.Histogram(data=posh,
                                             orientation='h',
                                             parent=parent,
                                             name='histogram')
        self._tf = TFmapsMesh(parent=parent)
        self._th.visible = False

        # Initialize annotations :
        SignalAnnotations.__init__(self, parent)

    def __str__(self):
        """String representation of the currently selected index."""
        lst = [str(k) for k in self._navidx[self._index]]
        lst.insert(self._axis, ':')
        return '(' + ', '.join(lst) + ')'

    def set_data(self,
                 data,
                 index,
                 color='black',
                 lw=2.,
                 nbins=10,
                 symbol='disc',
                 size=10.,
                 form='line',
                 th=None,
                 norm=None,
                 window=None,
                 overlap=0.,
                 baseline=None,
                 clim=None,
                 cmap='viridis',
                 interpolation='gaussian',
                 nperseg=256,
                 noverlap=128):
        """Set data to the plot.

        Parameters
        ----------
        data : array_like
            Raw data vector of shape (N,)
        index : int | 0
            Index of the 3-d array.
        color : array_like/string/tuple | None
            Color of the plot.
        lw : float | None
            Line width (form='line').
        symbol : string | None
            Marker symbol (form='marker').
        size : float | None
            Marker size (form='marker').
        nbins : int | None
            Number of bins for the histogram (form='histogram')
        form : {'line', 'marker', 'histogram', 'tf'}
            Plotting type.
        th : tuple | None
            Tuple of floats for line thresholding.
        norm : int | None
            Normalization method for (form='tf').
        window : tuple | None
            Averaging window (form='tf').
        overlap : float | 0.
            Overlap between successive windows (form='tf').
        baseline : tuple | None
            Baseline period for the normalization (form='tf').
        """
        # Update variable :
        self.form = form
        self._index = index
        color = color2vb(color)

        # Get data index :
        if data.ndim == 1:
            idx = slice(None)
        elif data.ndim in [2, 3]:
            idx = list(self._navidx[index])
            idx.insert(self._axis, slice(None))
            idx = tuple(idx)

        # Convert data to be compatible with VisPy and prepare data :
        data_c = vispy_array(data[idx]).copy()
        _data = self._prep._prepare_data(self._sf, data_c, self._time)

        # Set data :
        if form in ['line', 'marker', 'psd', 'butterfly']:  # line and marker
            # Get position array :
            pos = np.c_[self._time, _data]
            # Send position :
            if form in ['line', 'psd']:
                if form == 'psd':
                    fmax = self._sf / 4.
                    f, pxx = welch(_data,
                                   self._sf,
                                   nperseg=nperseg,
                                   noverlap=noverlap)
                    f_sf4 = abs(f - fmax)
                    f_1 = abs(f - 1.)
                    fidx_sf4 = np.where(f_sf4 == f_sf4.min())[0][0]
                    fidx_1 = np.where(f_1 == f_1.min())[0][0]
                    pos = np.c_[f[fidx_1:-fidx_sf4], pxx[fidx_1:-fidx_sf4]]
                # Threshold :
                is_th = isinstance(th, (tuple, list, np.ndarray))
                col = color2vb(color, length=pos.shape[0])
                if is_th:
                    # Build threshold segments :
                    t_min, t_max = self._time.min(), self._time.max()
                    pos_th = np.vstack(
                        ([t_min, th[0]], [t_max,
                                          th[0]], [t_min,
                                                   th[1]], [t_max, th[1]]))
                    self._th.set_data(pos_th,
                                      connect='segments',
                                      color=color2vb('#ab4642'))
                    # Build line color :
                    col = color2vb(color, length=len(_data))
                    cond = np.logical_or(_data < th[0], _data > th[1])
                    col[cond, :] = color2vb('#ab4642')
                self._th.visible = is_th
                self._line.set_data(pos, width=lw, color=col)
                self._line.update()
            elif form == 'marker':
                self._mark.set_data(pos,
                                    face_color=color,
                                    symbol=symbol,
                                    size=size,
                                    edge_width=0.)
                self._mark.update()
            elif form == 'butterfly':
                # Get soe shape related variables :
                n, m = len(self._time), int(np.prod(data.shape))
                n_rep = int(m / n)
                data = vispy_array(data)
                # Build position :
                pos = np.c_[np.tile(self._time.ravel(), n_rep), data.ravel()]
                # Disconnect some points :
                connect = np.c_[np.arange(m - 1), np.arange(1, m)]
                to_delete = np.linspace(n - 1, m - 1, n_rep)
                connect = np.delete(connect, to_delete, axis=0)
                # Build color :
                col = color2vb(color, length=m)
                # singcol = np.random.uniform(size=(n_rep, 3), low=.2,
                #                             high=.8).astype(np.float32)
                # col = np.repeat(singcol, n, 0)
                # Send data :
                self._line.set_data(pos, width=lw, color=col, connect=connect)
                self._line.update()
            # Get camera rectangle :
            t_min, t_max = pos[:, 0].min(), pos[:, 0].max()
            d_min, d_max = pos[:, 1].min(), pos[:, 1].max()
            off = .05 * (d_max - d_min)
            self.rect = (t_min, d_min - off, t_max - t_min,
                         d_max - d_min + 2 * off)
        elif form == 'histogram':  # histogram
            # Compute the mesh :
            mesh = scene.visuals.Histogram(_data, nbins)
            # Get the vertices and faces of the mesh :
            vert = mesh.mesh_data.get_vertices()
            faces = mesh.mesh_data.get_faces()
            # Pass vertices and faces to the histogram :
            self._hist.set_data(vert, faces, color=color)
            # Compute the histogram :
            raw, xvec = np.histogram(_data, nbins)
            # Get camera rectangle :
            t_min, t_max = xvec.min(), xvec.max()
            d_min, d_max = 0.9 * raw[np.nonzero(raw)].min(), 1.01 * raw.max()
            self.rect = (t_min, d_min, t_max - t_min, d_max - d_min)
            # Update object :
            self._hist.update()
        elif form == 'tf':  # time-frequency map
            self._tf.set_data(_data,
                              self._sf,
                              cmap=cmap,
                              contrast=.5,
                              norm=norm,
                              baseline=baseline,
                              n_window=window,
                              overlap=overlap,
                              window='hanning',
                              clim=clim)
            self._tf.interpolation = interpolation
            self.rect = self._tf.rect

        # Hide non form elements :
        self._visibility()

        # Update annotations :
        self.update_annotations(str(self))

    def update_annotations(self, name):
        """Update annotations."""
        is_annotated = self.is_event_annotated(name)
        if is_annotated:
            c = self.get_event_coord(name)
            z = np.full((c.shape[0], ), -10.)
            c = np.c_[c, z].astype(np.float32)
            msize = self._annot_mark._data['a_size'][0]
            mcolor = self._annot_mark._data['a_bg_color'][0, :]
            self._annot_mark.set_data(c,
                                      face_color=mcolor,
                                      edge_width=0.,
                                      size=msize)
        self._annot_mark.visible = is_annotated
        self._annot_text.visible = is_annotated

    def clean_annotations(self):
        """Clean annotations."""
        self._annot_mark.set_data(pos=np.random.rand(2, 2))
        self._annotations = {}
        self._annotations_txt = {}

    def _get_signal_index(self, signal):
        """Get index of a signal.

        This method turn a '(k, n, :)' string into an index used by the
        instance _navidx.
        """
        # Process signal :
        signal = signal.replace(', :', '').replace(':, ', '')[1:-1]
        # Find index :
        idx = tuple(int(k) for k in signal.split(', '))
        return self._navidx.index(idx)

    def _visibility(self):
        self._line.visible = self.form in ['line', 'psd', 'butterfly']
        self._mark.visible = self.form == 'marker'
        self._hist.visible = self.form == 'histogram'
        self._tf.visible = self.form == 'tf'
コード例 #4
0
class Spectrogram(PrepareData):
    """Create and manage a Spectrogram object.

    After object creation, use the set_data() method to pass new data, new
    color, new frequency / time range, new settings...
    """
    def __init__(self, camera, parent=None, fcn=None):
        # Initialize PrepareData :
        PrepareData.__init__(self, axis=0)

        # Keep camera :
        self._camera = camera
        self._rect = (0., 0., 0., 0.)
        self._fcn = fcn

        # Time-frequency map
        self.tf = TFmapsMesh(parent=parent)
        # Spectrogram
        self.mesh = scene.visuals.Image(np.zeros((2, 2)),
                                        parent=parent,
                                        name='Fourier transform')
        self.mesh.transform = vist.STTransform()

    def set_data(self,
                 sf,
                 data,
                 time,
                 method='Fourier transform',
                 cmap='rainbow',
                 nfft=30.,
                 overlap=0.,
                 fstart=.5,
                 fend=20.,
                 contrast=.5,
                 interp='nearest',
                 norm=0):
        """Set data to the spectrogram.

        Use this method to change data, colormap, spectrogram settings, the
        starting and ending frequencies.

        Parameters
        ----------
        sf: float
            The sampling frequency.
        data: array_like
            The data to use for the spectrogram. Must be a row vector.
        time: array_like
            The time vector.
        method: string | 'Fourier transform'
            Computation method.
        cmap : string | 'viridis'
            The matplotlib colormap to use.
        nfft : float | 30.
            Number of fft points for the spectrogram (in seconds).
        overlap : float | .5
            Ovelap proprotion (0 <= overlap <1).
        fstart : float | .5
            Frequency from which the spectrogram have to start.
        fend : float | 20.
            Frequency from which the spectrogram have to finish.
        contrast : float | .5
            Contrast of the colormap.
        interp : string | 'nearest'
            Interpolation method.
        norm : int | 0
            Normalization method for TF.
        """
        # =================== PREPARE DATA ===================
        # Prepare data (only if needed)
        if self:
            data = self._prepare_data(sf, data.copy(), time)

        nperseg = int(round(nfft * sf))

        # =================== TF // SPECTRO ===================
        if method == 'Wavelet':
            self.tf.set_data(data,
                             sf,
                             f_min=fstart,
                             f_max=fend,
                             cmap=cmap,
                             contrast=contrast,
                             n_window=nperseg,
                             overlap=overlap,
                             window='hamming',
                             norm=norm)
            self.tf._image.interpolation = interp
            self.rect = self.tf.rect
            self.freq = self.tf.freqs
        else:
            # =================== CONVERSION ===================
            overlap = int(round(overlap * nperseg))

            if method == 'Multitaper':
                from lspopt import spectrogram_lspopt
                freq, _, mesh = spectrogram_lspopt(data,
                                                   fs=sf,
                                                   nperseg=nperseg,
                                                   c_parameter=20,
                                                   noverlap=overlap)
            elif method == 'Fourier transform':
                freq, _, mesh = scpsig.spectrogram(data,
                                                   fs=sf,
                                                   nperseg=nperseg,
                                                   noverlap=overlap,
                                                   window='hamming')
            mesh = 20 * np.log10(mesh)

            # =================== FREQUENCY SELECTION ===================
            # Find where freq is [fstart, fend] :
            f = [0., 0.]
            f[0] = np.abs(freq - fstart).argmin() if fstart else 0
            f[1] = np.abs(freq - fend).argmin() if fend else len(freq)
            # Build slicing and select frequency vector :
            sls = slice(f[0], f[1] + 1)
            freq = freq[sls]
            self._fstart, self._fend = freq[0], freq[-1]

            # =================== COLOR ===================
            # Get clim :
            _mesh = mesh[sls, :]
            contrast = 1. if contrast is None else contrast
            clim = (contrast * _mesh.min(), contrast * _mesh.max())
            # Turn mesh into color array for selected frequencies:
            self.mesh.set_data(_mesh)
            _min, _max = _mesh.min(), _mesh.max()
            _cmap = cmap_to_glsl(limits=(_min, _max), clim=clim, cmap=cmap)
            self.mesh.cmap = _cmap
            self.mesh.clim = 'auto'
            self.mesh.interpolation = interp

            # =================== TRANSFORM ===================
            tm, th = time.min(), time.max()
            # Re-scale the mesh for fitting in time / frequency :
            fact = (freq.max() - freq.min()) / len(freq)
            sc = (th / mesh.shape[1], fact, 1)
            tr = [0., freq.min(), 0.]
            self.mesh.transform.translate = tr
            self.mesh.transform.scale = sc
            # Update object :
            self.mesh.update()
            # Get camera rectangle :
            self.rect = (tm, freq.min(), th - tm, freq.max() - freq.min())
            self.freq = freq
        # Visibility :
        self.mesh.visible = 0 if method == 'Wavelet' else 1
        self.tf.visible = 1 if method == 'Wavelet' else 0

    def clean(self):
        """Clean indicators."""
        pos = np.zeros((3, 4), dtype=np.float32)
        self.mesh.set_data(pos)
        self.mesh.parent = None
        self.mesh = None

    # ----------- RECT -----------
    @property
    def rect(self):
        """Get the rect value."""
        return self._rect

    @rect.setter
    def rect(self, value):
        """Set rect value."""
        self._rect = value
        self._camera.rect = value

    # ----------- INTERP -----------
    @property
    def interp(self):
        """Get the interp value."""
        return self._interp

    @interp.setter
    def interp(self, value):
        """Set interp value."""
        self._interp = value
        self.mesh.interpolation = value
        self.mesh.update()
        self.tf.interpolation = value
        self.tf.update()
コード例 #5
0
ファイル: visuals.py プロジェクト: EtienneCmb/visbrain
class Spectrogram(PrepareData):
    """Create and manage a Spectrogram object.

    After object creation, use the set_data() method to pass new data, new
    color, new frequency / time range, new settings...
    """

    def __init__(self, camera, parent=None, fcn=None):
        # Initialize PrepareData :
        PrepareData.__init__(self, axis=0)

        # Keep camera :
        self._camera = camera
        self._rect = (0., 0., 0., 0.)
        self._fcn = fcn

        # Time-frequency map
        self.tf = TFmapsMesh(parent=parent)
        # Spectrogram
        self.mesh = scene.visuals.Image(np.zeros((2, 2)), parent=parent,
                                        name='Fourier transform')
        self.mesh.transform = vist.STTransform()

    def set_data(self, sf, data, time, method='Fourier transform',
                 cmap='rainbow', nfft=30., overlap=0., fstart=.5, fend=20.,
                 contrast=.5, interp='nearest', norm=0):
        """Set data to the spectrogram.

        Use this method to change data, colormap, spectrogram settings, the
        starting and ending frequencies.

        Parameters
        ----------
        sf: float
            The sampling frequency.
        data: array_like
            The data to use for the spectrogram. Must be a row vector.
        time: array_like
            The time vector.
        method: string | 'Fourier transform'
            Computation method.
        cmap : string | 'viridis'
            The matplotlib colormap to use.
        nfft : float | 30.
            Number of fft points for the spectrogram (in seconds).
        overlap : float | .5
            Ovelap proprotion (0 <= overlap <1).
        fstart : float | .5
            Frequency from which the spectrogram have to start.
        fend : float | 20.
            Frequency from which the spectrogram have to finish.
        contrast : float | .5
            Contrast of the colormap.
        interp : string | 'nearest'
            Interpolation method.
        norm : int | 0
            Normalization method for TF.
        """
        # =================== PREPARE DATA ===================
        # Prepare data (only if needed)
        if self:
            data = self._prepare_data(sf, data.copy(), time)

        nperseg = int(round(nfft * sf))

        # =================== TF // SPECTRO ===================
        if method == 'Wavelet':
            self.tf.set_data(data, sf, f_min=fstart, f_max=fend, cmap=cmap,
                             contrast=contrast, n_window=nperseg,
                             overlap=overlap, window='hamming', norm=norm)
            self.tf._image.interpolation = interp
            self.rect = self.tf.rect
            self.freq = self.tf.freqs
        else:
            # =================== CONVERSION ===================
            overlap = int(round(overlap * nperseg))

            if method == 'Multitaper':
                from lspopt import spectrogram_lspopt
                freq, _, mesh = spectrogram_lspopt(data, fs=sf,
                                                   nperseg=nperseg,
                                                   c_parameter=20,
                                                   noverlap=overlap)
            elif method == 'Fourier transform':
                freq, _, mesh = scpsig.spectrogram(data, fs=sf,
                                                   nperseg=nperseg,
                                                   noverlap=overlap,
                                                   window='hamming')
            mesh = 20 * np.log10(mesh)

            # =================== FREQUENCY SELECTION ===================
            # Find where freq is [fstart, fend] :
            f = [0., 0.]
            f[0] = np.abs(freq - fstart).argmin() if fstart else 0
            f[1] = np.abs(freq - fend).argmin() if fend else len(freq)
            # Build slicing and select frequency vector :
            sls = slice(f[0], f[1] + 1)
            freq = freq[sls]
            self._fstart, self._fend = freq[0], freq[-1]

            # =================== COLOR ===================
            # Get clim :
            _mesh = mesh[sls, :]
            is_finite = np.isfinite(_mesh)
            _mesh[~is_finite] = np.percentile(_mesh[is_finite], 5)
            contrast = 1. if contrast is None else contrast
            clim = (contrast * _mesh.min(), contrast * _mesh.max())
            # Turn mesh into color array for selected frequencies:
            self.mesh.set_data(_mesh)
            _min, _max = _mesh.min(), _mesh.max()
            _cmap = cmap_to_glsl(limits=(_min, _max), clim=clim, cmap=cmap)
            self.mesh.cmap = _cmap
            self.mesh.clim = 'auto'
            self.mesh.interpolation = interp

            # =================== TRANSFORM ===================
            tm, th = time.min(), time.max()
            # Re-scale the mesh for fitting in time / frequency :
            fact = (freq.max() - freq.min()) / len(freq)
            sc = (th / mesh.shape[1], fact, 1)
            tr = [0., freq.min(), 0.]
            self.mesh.transform.translate = tr
            self.mesh.transform.scale = sc
            # Update object :
            self.mesh.update()
            # Get camera rectangle :
            self.rect = (tm, freq.min(), th - tm, freq.max() - freq.min())
            self.freq = freq
        # Visibility :
        self.mesh.visible = 0 if method == 'Wavelet' else 1
        self.tf.visible = 1 if method == 'Wavelet' else 0

    def clean(self):
        """Clean indicators."""
        pos = np.zeros((3, 4), dtype=np.float32)
        self.mesh.set_data(pos)
        self.mesh.parent = None
        self.mesh = None

    # ----------- RECT -----------
    @property
    def rect(self):
        """Get the rect value."""
        return self._rect

    @rect.setter
    def rect(self, value):
        """Set rect value."""
        self._rect = value
        self._camera.rect = value

    # ----------- INTERP -----------
    @property
    def interp(self):
        """Get the interp value."""
        return self._interp

    @interp.setter
    def interp(self, value):
        """Set interp value."""
        self._interp = value
        self.mesh.interpolation = value
        self.mesh.update()
        self.tf.interpolation = value
        self.tf.update()