def save_sprite(img, output_sprite, output_cmap=None, output_json=None, vmax=None, vmin=None, cmap='Greys', threshold=None, n_colors=256, format='png', resample=True, interpolation='nearest'): """ Generate a sprite from a 3D Niimg-like object. Parameters ---------- img : Niimg-like object See http://nilearn.github.io/manipulating_images/input_output.html output_file : string or file-like Path string to a filename, or a Python file-like object. If *format* is *None* and *fname* is a string, the output format is deduced from the extension of the filename. output_cmap : string, file-like or None, optional (default None) Path string to a filename, or a Python file-like object. The color map will be saved in that file (unless it is None). If *format* is *None* and *fname* is a string, the output format is deduced from the extension of the filename. output_json : string, file-like or None, optional (default None) Path string to a filename, or a Python file-like object. The parameters of the sprite will be saved in that file (unless it is None): Y and Z sizes, vmin, vmax, affine transform. vmax : float, or None, optional (default None) max value for mapping colors. vmin : float, or None, optional (default None) min value for mapping color. cmap : name of a matplotlib colormap, optional (default 'Greys') The colormap for the sprite. A matplotlib colormap can also be passed directly in cmap. threshold : a number, None, or 'auto', optional (default None) If None is given, the image is not thresholded. If a number is given, it is used to threshold the image: values below the threshold (in absolute value) are plotted as transparent. If auto is given, the threshold is determined magically by analysis of the image. n_colors : integer, optional (default 256) The number of discrete colors to use in the colormap, if it is generated. format : string, optional (default 'png') One of the file extensions supported by the active backend. Most backends support png, pdf, ps, eps and svg. resample : boolean, optional (default True) Resample to isotropic voxels, with a LR/AP/VD orientation. This is necessary for proper rendering of arbitrary Niimg volumes, but not necessary if the image is in an isotropic standard space. interpolation : string, optional (default nearest) The interpolation method for resampling See nilearn.image.resample_img black_bg : boolean, optional If True, the background of the image is set to be black. Returns ---------- sprite : numpy array with the sprite """ # Get cmap if isinstance(cm, str): cmap = plt.cm.get_cmap(cmap) img = check_niimg_3d(img, dtype='auto') # resample to isotropic voxel with standard orientation if resample: img = _resample_to_self(img, interpolation) # Read data data = _safe_get_data(img, ensure_finite=True) if np.isnan(np.sum(data)): data = np.nan_to_num(data) # Deal with automatic settings of plot parameters if threshold == 'auto': # Threshold epsilon below a percentile value, to be sure that some # voxels pass the threshold threshold = fast_abs_percentile(data) - 1e-5 # threshold threshold = float(threshold) if threshold is not None else None # Get vmin vmax show_nan_msg = False if vmax is not None and np.isnan(vmax): vmax = None show_nan_msg = True if vmin is not None and np.isnan(vmin): vmin = None show_nan_msg = True if show_nan_msg: nan_msg = ('NaN is not permitted for the vmax and vmin arguments.\n' 'Tip: Use np.nanmax() instead of np.max().') warnings.warn(nan_msg) if vmax is None: vmax = np.nanmax(data) if vmin is None: vmin = np.nanmin(data) # Create sprite sprite = _data2sprite(data) # Mask sprite if threshold is not None: if threshold == 0: sprite = np.ma.masked_equal(sprite, 0, copy=False) else: sprite = np.ma.masked_inside(sprite, -threshold, threshold, copy=False) # Save the sprite imsave(output_sprite, sprite, vmin=vmin, vmax=vmax, cmap=cmap, format=format) # Save the parameters if type(vmin).__module__ == 'numpy': vmin = vmin.tolist() # json does not deal with numpy array if type(vmax).__module__ == 'numpy': vmax = vmax.tolist() # json does not deal with numpy array if output_json is not None: params = { 'nbSlice': { 'X': data.shape[0], 'Y': data.shape[1], 'Z': data.shape[2] }, 'min': vmin, 'max': vmax, 'affine': img.affine.tolist() } if isinstance(output_json, str): f = open(output_json, 'w') f.write(json.dumps(params)) f.close() else: output_json.write(json.dumps(params)) # save the colormap if output_cmap is not None: data = np.arange(0, n_colors) / (n_colors - 1) data = data.reshape([1, n_colors]) imsave(output_cmap, data, cmap=cmap, format=format) return sprite
def find_cut_coords(img, mask=None, activation_threshold=None): import warnings import numpy as np from scipy import ndimage from nilearn._utils import as_ndarray, new_img_like from nilearn._utils.ndimage import largest_connected_component from nilearn._utils.extmath import fast_abs_percentile """ Find the center of the largest activation connected component. Parameters ----------- img : 3D Nifti1Image The brain map. mask : 3D ndarray, boolean, optional An optional brain mask. activation_threshold : float, optional The lower threshold to the positive activation. If None, the activation threshold is computed using the 80% percentile of the absolute value of the map. Returns ------- x : float the x world coordinate. y : float the y world coordinate. z : float the z world coordinate. """ data = img.get_data() # To speed up computations, we work with partial views of the array, # and keep track of the offset offset = np.zeros(3) # Deal with masked arrays: if hasattr(data, 'mask'): not_mask = np.logical_not(data.mask) if mask is None: mask = not_mask else: mask *= not_mask data = np.asarray(data) # Get rid of potential memmapping data = as_ndarray(data) my_map = data.copy() if mask is not None: slice_x, slice_y, slice_z = ndimage.find_objects(mask)[0] my_map = my_map[slice_x, slice_y, slice_z] mask = mask[slice_x, slice_y, slice_z] my_map *= mask offset += [slice_x.start, slice_y.start, slice_z.start] # Testing min and max is faster than np.all(my_map == 0) if (my_map.max() == 0) and (my_map.min() == 0): return .5 * np.array(data.shape) if activation_threshold is None: activation_threshold = fast_abs_percentile(my_map[my_map != 0].ravel(), 80) mask = np.abs(my_map) > activation_threshold - 1.e-15 # mask may be zero everywhere in rare cases if mask.max() == 0: return .5 * np.array(data.shape) mask = largest_connected_component(mask) slice_x, slice_y, slice_z = ndimage.find_objects(mask)[0] my_map = my_map[slice_x, slice_y, slice_z] mask = mask[slice_x, slice_y, slice_z] my_map *= mask offset += [slice_x.start, slice_y.start, slice_z.start] # For the second threshold, we use a mean, as it is much faster, # althought it is less robust second_threshold = np.abs(np.mean(my_map[mask])) second_mask = (np.abs(my_map) > second_threshold) if second_mask.sum() > 50: my_map *= largest_connected_component(second_mask) cut_coords = ndimage.center_of_mass(np.abs(my_map)) x_map, y_map, z_map = cut_coords + offset coords = [] coords.append(x_map) coords.append(y_map) coords.append(z_map) # Return as a list of scalars return coords
def test_fast_abs_percentile_no_index_error(): # check the offending low-level function fast_abs_percentile(np.arange(4))
def test_fast_abs_percentile(): data = np.arange(1, 100) for p in range(10, 100, 10): yield nose.tools.assert_equal, fast_abs_percentile(data, p - 1), p
def test_fast_abs_percentile(): rng = check_random_state(1) data = np.arange(100) rng.shuffle(data) for p in data: yield nose.tools.assert_equal, fast_abs_percentile(data, p), p
def plot_full_surf_stat_map(stat, title=None, ts=None, mask=None, inflate=False, outfile=None, add_colorbar=True, vmax=None, **kwargs): """Use nilearn's plot_surf_stat_map to plot volume data in the surface. Plots a collage of both hemispheres and both medial and lateral views of the brain. The surface mesh used for plotting is freesurfer's fsaverage. :param stat: A 3D statistical map (Nilearn's niimg object) :param title: A title for the image :param ts: Optional timeseries to be plotted in the background. :param mask: Optional mask passed to nilearn's vol_to_surf. :param inflate: If True, plot on inflated, if False, on pial. :param outfile: Optional output filename to save figure. :param vmax: Colormap vmax limit Other kwargs are passed to Nilearn's plot_surf_stat_map. """ fig, axes = plt.subplots(dpi=100, nrows=2, ncols=2, subplot_kw={'projection': '3d'}) ((ax_ll, ax_rl), (ax_lm, ax_rm)) = axes # Compute the surface textures from the statistical map. texture_r = surface.vol_to_surf(stat, FSAVERAGE.pial_right, mask_img=mask) texture_l = surface.vol_to_surf(stat, FSAVERAGE.pial_left, mask_img=mask) # Vmax scaled for optimal dynamic range. if vmax is None: vmax = fast_abs_percentile(stat.dataobj, 99.8) # Plot on inflated brain or on pial surface? if inflate: leftsurf = FSAVERAGE.infl_left rightsurf = FSAVERAGE.infl_right else: leftsurf = FSAVERAGE.pial_left rightsurf = FSAVERAGE.pial_right plotting.plot_surf_stat_map(rightsurf, texture_r, colorbar=False, hemi='right', axes=ax_rl, bg_map=FSAVERAGE.sulc_right, vmax=vmax, **kwargs) plotting.plot_surf_stat_map(rightsurf, texture_r, colorbar=False, hemi='right', view='medial', axes=ax_rm, bg_map=FSAVERAGE.sulc_right, vmax=vmax, **kwargs) plotting.plot_surf_stat_map(leftsurf, texture_l, colorbar=False, hemi='left', axes=ax_ll, bg_map=FSAVERAGE.sulc_left, vmax=vmax, **kwargs) plotting.plot_surf_stat_map(leftsurf, texture_l, colorbar=False, hemi='left', view='medial', axes=ax_lm, bg_map=FSAVERAGE.sulc_left, vmax=vmax, **kwargs) # The default camera distance in the 3D plot makes the surfaces look small. # The fix is simple, bring the camera closer to the object. for ax in axes.flatten(): ax.dist = 6 # Alpha set to 0 so that the timeseries is visible. ax.patch.set_alpha(0.) # Remove whitespace between subplots. fig.subplots_adjust(wspace=-0.02, hspace=0.0) if ts is not None: # x0, y0, width, height = 0.34, 0.465, 0.38, 0.06 # x0 left -> right, y0 top -> down. x0, y0 = 0.15, 0.445 width, height = (1.05 - 2 * x0), 0.12 ax5 = fig.add_axes([x0, y0, width, height], zorder=-1) ax5.plot(ts[::2], 'dimgray', linewidth=0.8) ax5.axis('off') # Only necessary if ax5 is on top. (zorder larger than other axes) # ax5.patch.set_alpha(0.) # Add colorbar if add_colorbar: if 'threshold' in kwargs: if kwargs['threshold'] is not None: threshold = kwargs['threshold'] else: threshold = 0 vmin = -vmax # when also displaying negative values, change to -vmax if 'cmap' in kwargs: cmap = nilearn_cmaps[kwargs['cmap']] else: cmap = nilearn_cmaps['cold_hot'] norm = Normalize(vmin=vmin, vmax=vmax) cmaplist = [cmap(i) for i in range(cmap.N)] # set colors to grey for absolute values < threshold istart = int(norm(-threshold, clip=True) * (cmap.N - 1)) istop = int(norm(threshold, clip=True) * (cmap.N - 1)) for i in range(istart, istop): cmaplist[i] = (0.5, 0.5, 0.5, 1.) our_cmap = LinearSegmentedColormap.from_list('Custom cmap', cmaplist, cmap.N) sm = plt.cm.ScalarMappable(cmap=our_cmap, norm=plt.Normalize(vmin=vmin, vmax=vmax)) # fake up the array of the scalar mappable. sm._A = [] # fig.subplots_adjust(bottom=0.05) cbar_ax = fig.add_subplot(32, 1, 32) # fig.add_axes([0.3, 0.9, 0.4, 0.03]) fig.colorbar(sm, cax=cbar_ax, orientation='horizontal') if title is not None: # y defaults to 0.98. The value of 0.93 lowers it a bit. fig.suptitle(title, fontsize=12, y=0.93) if outfile is not None: plt.savefig(outfile, dpi=100, bbox_inches='tight')