Source code for cortex.quickflat.view

import io
import os
import tempfile
import binascii
import numpy as np

from .. import utils
from .. import dataset
from .utils import make_flatmap_image
from . import composite


default_colorbar_locations = {
    'left': (.0, .07, .2, .04),
    'center': (.4, .07, .2, .04),
    'right': (.7, .07, .2, .04)
}


def _check_colorbar_location(colorbar_location):
    if isinstance(colorbar_location, (tuple, list)):
        return colorbar_location

    if colorbar_location not in default_colorbar_locations:
        raise ValueError("colorbar_location must be one of {}".format(
            list(default_colorbar_locations.keys())))

    return default_colorbar_locations[colorbar_location]


[docs]def make_figure(braindata, recache=False, pixelwise=True, thick=32, sampler='nearest', height=1024, dpi=100, depth=0.5, with_rois=True, with_sulci=False, with_labels=True, with_colorbar=True, with_borders=False, with_dropout=False, with_curvature=False, extra_disp=None, with_connected_vertices=False, overlay_file=None, linewidth=None, linecolor=None, roifill=None, shadow=None, labelsize=None, labelcolor=None, cutout=None, curvature_brightness=None, curvature_contrast=None, curvature_threshold=None, fig=None, extra_hatch=None, colorbar_ticks=None, colorbar_location='center', roi_list=None, nanmean=False, **kwargs): """Show a Volume or Vertex on a flatmap with matplotlib. Note that **kwargs are ONLY present now for backward compatibility / warnings. No kwargs should be used. Parameters ---------- braindata : Dataview (e.g. instance of cortex.Volume, cortex.Vertex,...) the data you would like to plot on a flatmap recache : boolean Whether or not to recache intermediate files. Takes longer to plot this way, potentially resolves some errors. Useful if you've made changes to the alignment pixelwise : bool Use pixel-wise mapping thick : int Number of layers through the cortical sheet to sample. Only applies for pixelwise = True sampler : str Name of sampling function used to sample underlying volume data. Options include 'trilinear', 'nearest', 'lanczos'; see functions in cortex.mapper.samplers.py for all options height : int Height of the image to render. Automatically scales the width for the aspect of the subject's flatmap depth : float Value between 0 and 1 for how deep to sample the surface for the flatmap (0 = gray/white matter boundary, 1 = pial surface) with_rois, with_labels, with_colorbar, with_borders, with_dropout, with_curvature, etc : bool, optional Display the rois, labels, colorbar, annotated flatmap borders, etc cutout : str Name of flatmap cutout with which to clip the full flatmap. Should be the name of a sub-layer of the 'cutouts' layer in <filestore>/<subject>/overlays.svg Other Parameters ---------------- dpi : int DPI of the generated image. Only applies to the scaling of matplotlib elements, specifically the colormap linewidth : int, optional Width of ROI lines. Defaults to roi options in your local `options.cfg` linecolor : tuple of float, optional (R, G, B, A) specification of line color roifill : tuple of float, optional (R, G, B, A) sepcification for the fill of each ROI region shadow : int, optional Standard deviation of the gaussian shadow. Set to 0 if you want no shadow labelsize : str, optional Font size for the label, e.g. "16pt" labelcolor : tuple of float, optional (R, G, B, A) specification for the label color curvature_brightness : float, optional Mean* brightness of background. 0 = black, 1 = white, intermediate values are corresponding grayscale values. If None, Defaults to config file value. (*this does not precisely specify the mean; the actual mean luminance of the curvature depends on the value for `curvature_contrast`. It's easiest to think about it as the mean brightness, though.) curvature_contrast : float, optional Contrast of curvature. 1 = maximal contrast (black/white), 0 = no contrast (solid color for curvature equal to `curvature_brightness`). cvmax : float, optional [DEPRECATED! use `curvature_brightness` and `curvature_contrast` instead] Maximum value for background curvature colormap. Defaults to config file value. cvthr : bool, optional [DEPRECATED! use `curvature_threshold` instead] Apply threshold to background curvature extra_disp : tuple, optional Optional extra display layer from external .svg file. Tuple specifies (filename, layer) filename should be a full path. External svg file should be structured exactly as overlays.svg for the subject. (Best to just copy overlays.svg somewhere else and add layers to it.) Default value is None. extra_hatch : tuple, optional Optional extra crosshatch-textured layer, given as (DataView, [r, g, b]) tuple. colorbar_location : str or tuple, optional Location of the colorbar. Default locations are one of 'left', 'center', 'right' (default 'center'). Alternatively, a tuple with four floats between 0 and 1 can be passed indicating (left, bottom, width, height). colorbar_ticks : array-like, optional For 1D colormaps indicates the ticks of the colorbar. If None, it defaults to equally spaced values between vmin and vmax. This parameter is not used for 2D colormaps, and it defaults to the vmin, vmax specified in the Volume2D object. fig : figure or ax figure into which to plot flatmap nanmean : bool, optional (default = False) If True, NaNs in the data will be ignored when averaging across layers. """ from matplotlib import pyplot as plt dataview = dataset.normalize(braindata) if not isinstance(dataview, dataset.Dataview): raise TypeError('Please provide a Dataview (e.g. an instance of cortex.Volume, cortex.Vertex, etc), not a Dataset') if fig is None: fig_resize = True fig = plt.figure() ax = fig.add_axes((0, 0, 1, 1)) elif isinstance(fig, plt.Figure): fig_resize = False fig = plt.figure(fig.number) ax = fig.add_axes((0, 0, 1, 1)) elif isinstance(fig, plt.Axes): fig_resize = False ax = fig fig = ax.figure # Add data data_im, extents = composite.add_data(ax, dataview, pixelwise=pixelwise, thick=thick, sampler=sampler, height=height, depth=depth, recache=recache, nanmean=nanmean) layers = dict(data=data_im) # Add curvature if with_curvature: # backward compatibility if any([x in kwargs for x in ['cvmin', 'cvmax', 'cvthr']]): import warnings warnings.warn(("Use of `cvmin`, `cvmax`, and `cvthr` is deprecated! Please use \n" "`curvature_brightness`, `curvature_contrast`, and `curvature_threshold`\n" "to set appearance of background curvature.")) legacy_mode = True if ('cvmin' in kwargs) and ('cvmax' in kwargs): # Assumes that if one is specified, both are; weird case where only one is # specified will still break. curvature_lims = (kwargs.pop('cvmin'), kwargs.pop('cvmax')) else: curvature_lims = 0.5 if 'cvthr' in kwargs: curvature_threshold = kwargs.pop('cvthr') else: curvature_lims = 0.5 legacy_mode = False curv_im = composite.add_curvature(ax, dataview, extents, brightness=curvature_brightness, contrast=curvature_contrast, threshold=curvature_threshold, curvature_lims=curvature_lims, legacy_mode=legacy_mode, recache=recache) layers['curvature'] = curv_im # Add dropout if with_dropout is not False: # Support old api: if isinstance(with_dropout, dataset.Dataview): hatch_data = with_dropout else: hatch_data = None dropout_power = 20 if with_dropout is True else with_dropout if hatch_data is None: hatch_data = utils.get_dropout(dataview.subject, dataview.xfmname, power=dropout_power) drop_im = composite.add_hatch(ax, hatch_data, extents=extents, height=height, sampler=sampler, recache=recache) layers['dropout'] = drop_im # Add extra hatching if extra_hatch is not None: hatch_data2, hatch_color = extra_hatch hatch_im = composite.add_hatch(ax, hatch_data2, extents=extents, height=height, sampler=sampler, recache=recache) layers['hatch'] = hatch_im # Add rois if with_rois: roi_im = composite.add_rois(ax, dataview, extents=extents, height=height, linewidth=linewidth, linecolor=linecolor, roifill=roifill, shadow=shadow, labelsize=labelsize, labelcolor=labelcolor, with_labels=with_labels, overlay_file=overlay_file, roi_list=roi_list) layers['rois'] = roi_im # Add sulci if with_sulci: sulc_im = composite.add_sulci(ax, dataview, extents=extents, height=height, linewidth=linewidth, linecolor=linecolor, shadow=shadow, labelsize=labelsize, labelcolor=labelcolor, with_labels=with_labels, overlay_file=overlay_file) layers['sulci'] = sulc_im # Add custom if extra_disp is not None: svgfile, layer = extra_disp custom_im = composite.add_custom(ax, dataview, svgfile, layer, height=height, extents=extents, linewidth=linewidth, linecolor=linecolor, shadow=shadow, labelsize=labelsize, labelcolor=labelcolor, with_labels=with_labels) layers['custom'] = custom_im # Add connector lines btw connected vertices if with_connected_vertices: vertex_lines = composite.add_connected_vertices(ax, dataview, recache=recache) ax.axis('off') ax.set_xlim(extents[0], extents[1]) ax.set_ylim(extents[2], extents[3]) if fig_resize: imsize = fig.get_axes()[0].get_images()[0].get_size() fig.set_size_inches(np.array(imsize)[::-1] / float(dpi)) # Add (apply) cutout of flatmap if cutout is not None: extents = composite.add_cutout(ax, cutout, dataview, layers) if with_colorbar: colorbar_location = _check_colorbar_location(colorbar_location) # Allow 2D colorbars: if isinstance(dataview, dataset.view2D.Dataview2D): colorbar_ticks = np.round([ dataview.vmin, dataview.vmax, dataview.vmin2, dataview.vmax2 ], 2) colorbar = composite.add_colorbar_2d( ax, dataview.cmap, colorbar_ticks, colorbar_location=colorbar_location) else: colorbar = composite.add_colorbar( ax, data_im, colorbar_location=colorbar_location, colorbar_ticks=colorbar_ticks ) # Reset axis to main figure axis plt.sca(ax) return fig
[docs]def make_png(fname, braindata, recache=False, pixelwise=True, sampler='nearest', height=1024, bgcolor=None, dpi=100, **kwargs): """Create a PNG of the VertexData or VolumeData on a flatmap. Parameters ---------- fname : str Filename for where to save the PNG file braindata : Dataview (e.g. instance of cortex.Volume, cortex.Vertex, ...) the data you would like to plot on a flatmap recache : boolean Whether or not to recache intermediate files. Takes longer to plot this way, potentially resolves some errors. Useful if you've made changes to the alignment pixelwise : bool Use pixel-wise mapping thick : int Number of layers through the cortical sheet to sample. Only applies for pixelwise = True sampler : str Name of sampling function used to sample underlying volume data height : int Height of the image to render. Automatically scales the width for the aspect of the subject's flatmap depth : float Value between 0 and 1 for how deep to sample the surface for the flatmap (0 = gray/white matter boundary, 1 = pial surface) with_rois, with_labels, with_colorbar, with_borders, with_dropout : bool, optional Display the rois, labels, colorbar, annotated flatmap borders, and cross-hatch dropout? sampler : str Name of sampling function used to sample underlying volume data. Options include 'trilinear', 'nearest', 'lanczos'; see functions in cortex.mapper.samplers.py for all options Other Parameters ---------------- dpi : int DPI of the generated image. Only applies to the scaling of matplotlib elements, specifically the colormap bgcolor : matplotlib colorspec Color of background of image. `None` gives transparent background. linewidth : int, optional Width of ROI lines. Defaults to roi options in your local `options.cfg` linecolor : tuple of float, optional (R, G, B, A) specification of line color roifill : tuple of float, optional (R, G, B, A) sepcification for the fill of each ROI region shadow : int, optional Standard deviation of the gaussian shadow. Set to 0 if you want no shadow labelsize : str, optional Font size for the label, e.g. "16pt" labelcolor : tuple of float, optional (R, G, B, A) specification for the label color """ from matplotlib import pyplot as plt fig = make_figure(braindata, recache=recache, pixelwise=pixelwise, sampler=sampler, height=height, **kwargs) imsize = fig.get_axes()[0].get_images()[0].get_size() fig.set_size_inches(np.array(imsize)[::-1] / float(dpi)) if bgcolor is None: fig.savefig(fname, transparent=True, dpi=dpi) else: fig.savefig(fname, facecolor=bgcolor, transparent=False, dpi=dpi) fig.clf() plt.close(fig)
[docs]def make_svg(fname, braindata, with_labels=False, with_curvature=True, layers=['rois'], height=1024, overlay_file=None, with_dropout=False, **kwargs): """Save an svg file of the desired flatmap. This function creates an SVG file with vector graphic ROIs overlaid on a single png image. Ideally, this function would layer different images (curvature, data, dropout, etc), but that has been left to implement at a future date if anyone really wants it. Parameters ---------- fname : string file name to save braindata : Dataview the data you would like to plot on a flatmap with_labels : bool Whether to display text labels on ROIs with_curvature : bool Whether to include background curvature layers : list List of layer names to show height : int Height of PNG in pixels overlay_file : str Custom ROI overlays file to use with_dropout : bool or Dataview If True or a cortex.Dataview object, hatches will be overlaid on top of the flatmap to indicate areas with dropout. If set to True, the dropout areas will be estimated from the intensity of the reference image. If set to a cortex.Dataview object, values in the dataset will be considered dropout areas. The transparency of the hatches is proportional to the intensity of the values in the dropout dataset. """ fp = io.BytesIO() from matplotlib.pyplot import imsave ## Render PNG file & retrieve image data arr, extents = make_flatmap_image(braindata, height=height, **kwargs) # Set nans to alpha = 0. to enable transparency when saving as PNG mask_nans = np.isnan(arr[..., 3]) arr[mask_nans, 3] = 0. if hasattr(braindata, 'cmap'): imsave(fp, arr, cmap=braindata.cmap, vmin=braindata.vmin, vmax=braindata.vmax) else: imsave(fp, arr) fp.seek(0) pngdata = binascii.b2a_base64(fp.read()) image_data = [pngdata] if with_curvature: # no options. learn to love it. from cortex import db fpc = io.BytesIO() curv_vertices = db.get_surfinfo(braindata.subject) curv_arr, _ = make_flatmap_image(curv_vertices, height=height) mask = np.isnan(curv_arr) curv_arr = np.where(curv_arr > 0, 0.5, 0.25) curv_arr[mask] = np.nan imsave(fpc, curv_arr, cmap='Greys_r', vmin=0, vmax=1) fpc.seek(0) image_data = [binascii.b2a_base64(fpc.read()), pngdata] # Add dropout -- modified from quickflat.view.make_figure if with_dropout: dataview = dataset.normalize(braindata) # Support old api: if isinstance(with_dropout, dataset.Dataview): hatch_data = with_dropout else: hatch_data = utils.get_dropout(dataview.subject, dataview.xfmname) sampler = kwargs.get("sampler", "nearest") recache = kwargs.get("recache", False) hatch_space = 4 hatch_color = (0, 0, 0) hatchim = composite._make_hatch_image( hatch_data, height, sampler, recache=recache, hatch_space=hatch_space ) hatchim[:, :, 0] = hatch_color[0] hatchim[:, :, 1] = hatch_color[1] hatchim[:, :, 2] = hatch_color[2] fpc = io.BytesIO() imsave(fpc, hatchim) fpc.seek(0) # Add dropout above data layer image_data += [binascii.b2a_base64(fpc.read())] ## Create and save SVG file roipack = utils.db.get_overlay(braindata.subject, overlay_file) roipack.get_svg(fname, layers=layers, labels=with_labels, with_ims=image_data)
def make_gif(output_destination, volumes, frame_duration=1, **figure_kwargs): """Make an animated gif from several pycortex volumes Parameters ---------- output_destination : str or stream-like The destination for the created gif. If a str, saves to a file. If stream-like (file handle or io.BytesIO), writes to the stream volumes : dict of pycortex Volumes duration : float The duration of each frame in seconds **figure_kwargs Passed to `cortex.quickflat.make_figure` Returns ------- If output_destination is a file path, return the path. If stream-like, return the stream data. """ import imageio from matplotlib import pyplot as plt tmpdir = tempfile.TemporaryDirectory() images = [] for i, name in enumerate(volumes): fig = plt.figure(figsize=(12, 6), dpi=100) _ = make_figure(volumes[name], fig=fig, **figure_kwargs) _ = fig.suptitle(name) path = os.path.join(tmpdir.name, str(i) + '.png') fig.savefig(path) images.append(imageio.imread(path)) _ = plt.close(fig) tmpdir.cleanup() imageio.mimsave(output_destination, images, format='gif', duration=frame_duration) if hasattr(output_destination, 'seek'): output_destination.seek(0) def show(*args, **kwargs): """Wrapper for make_figure()""" return make_figure(*args, **kwargs) def make_movie(name, data, subject, xfmname, recache=False, height=1024, sampler='nearest', dpi=100, tr=2, interp='linear', fps=30, vcodec='libtheora', bitrate="8000k", vmin=None, vmax=None, **kwargs): """Create a movie of an 4D data set""" raise NotImplementedError import sys import shlex import shutil import tempfile import subprocess as sp import multiprocessing as mp from scipy.interpolate import interp1d # Make the flatmaps ims, extents = make_flatmap_image(data, subject, xfmname, recache=recache, height=height, sampler=sampler) if vmin is None: vmin = np.nanmin(ims) if vmax is None: vmax = np.nanmax(ims) # Create the matplotlib figure fig = make_figure(ims[0], subject, vmin=vmin, vmax=vmax, **kwargs) fig.set_size_inches(np.array([ims.shape[2], ims.shape[1]]) / float(dpi)) img = fig.axes[0].images[0] # Set up interpolation times = np.arange(0, len(ims)*tr, tr) interp = interp1d(times, ims, kind=interp, axis=0, copy=False) frames = np.linspace(0, times[-1], (len(times)-1)*tr*fps+1) try: path = tempfile.mkdtemp() impath = os.path.join(path, "im{:09d}.png") for frame, frame_time in enumerate(frames): img.set_data(interp(frame_time)) fig.savefig(impath.format(frame), transparent=True, dpi=dpi) # avconv might not be relevant function for all operating systems. # Introduce operating system check here? cmd = "avconv -i {path} -vcodec {vcodec} -r {fps} -b {br} {name}".format(path=impath, vcodec=vcodec, fps=fps, br=bitrate, name=name) sp.call(shlex.split(cmd)) finally: shutil.rmtree(path)