Source code for cortex.dataset.view2D

import os
import json
import warnings

import numpy as np

from .. import options
from .views import Dataview, Volume, Vertex, VolumeRGB, VertexRGB
from .braindata import VolumeData, VertexData

default_cmap2D = options.config.get("basic", "default_cmap2D")

class Dataview2D(Dataview):
    """Abstract base class for 2-dimensional data views.
    """
    def __init__(self, description="", cmap=None, vmin=None, vmax=None, vmin2=None, vmax2=None, state=None, **kwargs):
        self.cmap = cmap or default_cmap2D
        self.vmin = vmin
        self.vmax = vmax
        self.vmin2 = vmin if vmin2 is None else vmin2
        self.vmax2 = vmax if vmax2 is None else vmax2

        self.state = state
        self.attrs = kwargs
        if 'priority' not in self.attrs:
            self.attrs['priority'] = 1
        self.description = description

    def uniques(self, collapse=False):
        yield self.dim1
        yield self.dim2

    def _write_hdf(self, h5, name="data"):
        self._cls._write_hdf(self.dim1, h5)
        self._cls._write_hdf(self.dim2, h5)

        viewnode = Dataview._write_hdf(self, h5, name=name)
        viewnode[0] = json.dumps([[self.dim1.name, self.dim2.name]])
        viewnode[3] = json.dumps([[self.vmin, self.vmin2]])
        viewnode[4] = json.dumps([[self.vmax, self.vmax2]])
        return viewnode

    def to_json(self, simple=False):
        sdict = dict(data=[[self.dim1.name, self.dim2.name]],
            state=self.state, 
            attrs=self.attrs, 
            desc=self.description,
            cmap=[self.cmap] )

        d1js = self.dim1.to_json()
        d2js = self.dim2.to_json()
        sdict.update(dict(
            vmin = [[self.vmin or d1js['vmin'][0], self.vmin2 or d2js['vmin'][0]]],
            vmax = [[self.vmax or d1js['vmax'][0], self.vmax2 or d2js['vmax'][0]]],
            ))

        if "xfm" in d1js:
            sdict['xfm'] = [[d1js['xfm'][0], d2js['xfm'][0]]]

        return sdict

    def _to_raw(self, data1, data2):
        from matplotlib import pyplot as plt
        from matplotlib.colors import Normalize
        cmapdir = options.config.get("webgl", "colormaps")
        cmap = plt.imread(os.path.join(cmapdir, "%s.png"%self.cmap))
        _warn_non_perceptually_uniform_colormap(self.cmap)

        norm1 = Normalize(self.vmin, self.vmax)
        norm2 = Normalize(self.vmin2, self.vmax2)
        
        d1 = np.clip(norm1(data1), 0, 1)
        d2 = np.clip(1 - norm2(data2), 0, 1)
        dim1 = np.round(d1 * (cmap.shape[1]-1))
        # Nans in data seemed to cause weird interaction with conversion to uint32
        dim1 = np.nan_to_num(dim1).astype(np.uint32) 
        dim2 = np.round(d2 * (cmap.shape[0]-1))
        dim2 = np.nan_to_num(dim2).astype(np.uint32)

        colored = cmap[dim2.ravel(), dim1.ravel()]
        # map r, g, b, a values between 0 and 255 to avoid problems with
        # VolumeRGB when plotting flatmaps with quickflat
        colored = (colored * 255).astype(np.uint8)
        r, g, b, a = colored.T
        r.shape = dim1.shape
        g.shape = dim1.shape
        b.shape = dim1.shape
        a.shape = dim1.shape
        # Preserve nan values as alpha = 0
        aidx = np.logical_or(np.isnan(data1), np.isnan(data2))
        a[aidx] = 0
        # Code from main, to handle alpha input, prob better here but not tested.
        # # Possibly move this above setting nans to alpha = 0;
        # # Possibly multiply specified alpha by alpha in colormap??
        # if 'alpha' in self.attrs:
        #     # Over-write alpha from colormap / nans with alpha arg if provided.
        #     # Question: Might it be important tokeep alpha as an attr?
        #     a = self.attrs.pop('alpha')
        return r, g, b, a

    @property
    def subject(self):
        return self.dim1.subject

[docs] class Volume2D(Dataview2D): """ Contains two 3D volumes for simultaneous visualization. Includes information on how the volumes should be jointly colormapped. Parameters ---------- dim1 : ndarray or Volume The first volume. Can be a 1D or 3D array (see Volume for details), or a Volume. dim2 : ndarray or Volume The second volume. Can be a 1D or 3D array (see Volume for details), or a Volume. subject : str, optional Subject identifier. Must exist in the pycortex database. If not given, dim1 must be a Volume from which the subject can be extracted. xfmname : str, optional Transform name. Must exist in the pycortex database. If not given, dim1 must be a Volume from which the subject can be extracted. description : str, optional String describing this dataset. Displayed in webgl viewer. cmap : str, optional Colormap (or colormap name) to use. If not given defaults to the `default_cmap2d` in your pycortex options.cfg file. vmin : float, optional Minimum value in colormap for dim1. If not given defaults to TODO:WHAT vmax : float, optional Maximum value in colormap for dim1. If not given defaults to TODO:WHAT vmin2 : float, optional Minimum value in colormap for dim2. If not given defaults to TODO:WHAT vmax2 : float, optional Maximum value in colormap for dim2. If not given defaults to TODO:WHAT **kwargs All additional arguments in kwargs are passed to the VolumeData and Dataview """ _cls = VolumeData
[docs] def __init__(self, dim1, dim2, subject=None, xfmname=None, description="", cmap=None, vmin=None, vmax=None, vmin2=None, vmax2=None, **kwargs): if isinstance(dim1, self._cls): if subject is not None or xfmname is not None: raise TypeError("Subject and xfmname cannot be specified with Volumes") if not isinstance(dim2, self._cls) or dim2.subject != dim1.subject: raise TypeError("Invalid data for second dimension") self.dim1 = dim1 self.dim2 = dim2 else: self.dim1 = Volume(dim1, subject, xfmname, vmin=vmin, vmax=vmax) self.dim2 = Volume(dim2, subject, xfmname, vmin=vmin2, vmax=vmax2) vmin = self.dim1.vmin if vmin is None else vmin vmin2 = self.dim2.vmin if vmin2 is None else vmin2 vmax = self.dim1.vmax if vmax is None else vmax vmax2 = self.dim2.vmax if vmax2 is None else vmax2 super(Volume2D, self).__init__(description=description, cmap=cmap, vmin=vmin, vmax=vmax, vmin2=vmin2, vmax2=vmax2, **kwargs)
def __repr__(self): return "<2D volumetric data for (%s, %s)>"%(self.dim1.subject, self.dim1.xfmname) def _write_hdf(self, h5, name="data"): viewnode = super(Volume2D, self)._write_hdf(h5, name) viewnode[7] = json.dumps([[self.dim1.xfmname, self.dim2.xfmname]]) return viewnode @property def raw(self): """VolumeRGB object containing the colormapped data from this object. """ if self.dim1.xfmname != self.dim2.xfmname: raise ValueError("Both Volumes must have same xfmname to generate single raw volume") if ((self.dim1.linear and self.dim2.linear) and (self.dim1.mask.shape == self.dim2.mask.shape) and np.all(self.dim1.mask == self.dim2.mask)): r, g, b, a = self._to_raw(self.dim1.data, self.dim2.data) else: r, g, b, a = self._to_raw(self.dim1.volume, self.dim2.volume) # Allow manual override of alpha channel kws = dict(subject=self.dim1.subject, xfmname=self.dim1.xfmname, state=self.state, description=self.description, **self.attrs) if not 'alpha' in self.attrs: kws['alpha'] = a return VolumeRGB(r, g, b, **kws) @property def xfmname(self): return self.dim1.xfmname
[docs] class Vertex2D(Dataview2D): """ Contains two vertex maps for simultaneous visualization. Includes information on how the maps should be jointly colormapped. Parameters ---------- dim1 : ndarray or Vertex The first vertex map. Can be a 1D array (see Vertex for details), or a Vertex. dim2 : ndarray or Vertex The second vertex map. Can be a 1D array (see Vertex for details), or a Vertex. subject : str, optional Subject identifier. Must exist in the pycortex database. If not given, dim1 must be a Vertex from which the subject can be extracted. description : str, optional String describing this dataset. Displayed in webgl viewer. cmap : str, optional Colormap (or colormap name) to use. If not given defaults to the `default_cmap2d` in your pycortex options.cfg file. vmin : float, optional Minimum value in colormap for dim1. If not given defaults to TODO:WHAT vmax : float, optional Maximum value in colormap for dim1. If not given defaults to TODO:WHAT vmin2 : float, optional Minimum value in colormap for dim2. If not given defaults to TODO:WHAT vmax2 : float, optional Maximum value in colormap for dim2. If not given defaults to TODO:WHAT **kwargs All additional arguments in kwargs are passed to the VolumeData and Dataview """ _cls = VertexData blend_curvature = _cls.blend_curvature # hacky inheritance
[docs] def __init__(self, dim1, dim2, subject=None, description="", cmap=None, vmin=None, vmax=None, vmin2=None, vmax2=None, **kwargs): if isinstance(dim1, VertexData): if subject is not None: raise TypeError("Subject cannot be specified with Volumes") if not isinstance(dim2, VertexData) or dim2.subject != dim1.subject: raise TypeError("Invalid data for second dimension") self.dim1 = dim1 self.dim2 = dim2 else: self.dim1 = Vertex(dim1, subject, vmin=vmin, vmax=vmax) self.dim2 = Vertex(dim2, subject, vmin=vmin2, vmax=vmax2) vmin = self.dim1.vmin if vmin is None else vmin vmin2 = self.dim2.vmin if vmin2 is None else vmin2 vmax = self.dim1.vmax if vmax is None else vmax vmax2 = self.dim2.vmax if vmax2 is None else vmax2 super(Vertex2D, self).__init__(description=description, cmap=cmap, vmin=vmin, vmax=vmax, vmin2=vmin2, vmax2=vmax2, **kwargs)
def __repr__(self): return "<2D vertex data for (%s)>"%self.dim1.subject @property def raw(self): """VertexRGB object containing the colormapped data from this object. """ r, g, b, a = self._to_raw(self.dim1.data, self.dim2.data) # Allow manual override of alpha channel kws = dict(subject=self.dim1.subject) if not 'alpha' in self.attrs: kws['alpha'] = a return VertexRGB(r, g, b, **kws) @property def vertices(self): return self.raw.vertices
def _warn_non_perceptually_uniform_colormap(cmap): mapping = { "BuOr_2D": "PU_BuOr_covar", "RdBu_covar": "PU_RdBu_covar", "RdBu_covar2": "PU_BuOr_covar", "RdBu_covar_alpha": "PU_RdBu_covar_alpha", "RdGn_covar": "PU_RdGn_covar", "hot_alpha": "fire_alpha", } if cmap in mapping: warnings.warn("Colormap %r is not perceptually uniform. Consider using" " %r instead." % (cmap, mapping[cmap]), UserWarning)