コード例 #1
0
def contour_slices(
    bg_image,
    file_template,
    auto_figsize=False,
    invert=False,
    alpha=[0.9],
    colors=['r', 'g', 'b'],
    dimming=0.,
    figure_title='',
    force_reverse_slice_order=True,
    legend_template='',
    levels_percentile=[80],
    linewidths=(),
    ratio='portrait',
    save_as='',
    scale=0.4,
    slice_spacing=0.5,
    substitutions=[
        {},
    ],
    style='light',
    title_color='#BBBBBB',
):
    """
	Plot coronal `bg_image` slices at a given spacing, and overlay contours from a list of NIfTI files.

	Parameters
	----------

	bg_image : str
		Path to the NIfTI image to draw in grayscale as th eplot background.
		This would commonly be some sort of brain template.
	file_template : str
		String template giving the path to the overlay stack.
		To create multiple overlays, this template will iteratively be substituted with each of the substitution dictionaries in the `substitutions` parameter.
	auto_figsize : boolean, optional
		Whether to automatically determine the size of the figure.
	invert : boolean, optional
		Whether to automatically invert data matrix values (useful if the image consists of negative values, e.g. when dealing with negative contrast agent CBV scans).
	alpha : list, optional
		List of floats, specifying with how much alpha to draw each contour.
	colors : list, optional
		List of colors in which to plot the overlays.
	dimming : float, optional
		Dimming factor, generally between -2 and 2 (-2 increases contrast, 2 decreases it).
		This parameter is passed directly to `nilearn.plotting.plot_anat()`
		Set to 'auto', to use nilearn automagick dimming.
	figure_title : str, optional
		Title for the figure.
	force_reverse_slice_order : bool, optional
		Whether to force the reversal of the slice order.
		This can be done to enforce a visual presentation without having to modify the underlying data (i.e. visualize neurological-order slices in radiological order).
		This option should generally be avoided, ideally one would not obfuscate the data orientation when plotting.
	legend_template : string, optional
		String template which can be formatted with the dictionaries contained in the `substitutions` parameter.
		The resulting strings will give the legend text.
	levels_percentile : list, optional
		List of integers, specifying at which percentiles of each overlay to draw contours.
	line_widths : tuple, optional
		Tuple of desired contour line widths (one per substitution).
	ratio : list or {'landscape', 'portrait'}, optional
		Either a list of 2 integers giving the desired number of rows and columns (in this order), or a string, which is either 'landscape' or 'portrait', and which prompts the function to auto-determine the best number of rows and columns given the number of slices and the `scale` attribute.
	save_as : str, optional
		Path under which to save the output figure.
		The string may contain formatting fields from the first dictionary in the `substitutions` variable.
	scale : float, optional
		The expected ratio of the slice height divided by the sum of the slice height and width.
		This somewhat complex metric controls the row and column distribution of slices in the 'landscape' and 'portrait' plotting shapes.
	slice_spacing : float
		Slice spacing in mm.
	substitutions : list of dicts, optional
		A list of dictionaries, with keys including all substitution keys found in the `file_template` parameter, and values giving desired substitution values which point the `file_template` string templated to existing filed which are to be included in the overlay stack.
		Such a dictionary is best obtained via `samri.utilities.bids_substitution_iterator()`.
	style : {'light', 'dark', ''}, optional
		Default SAMRI styling which to apply, set to an empty string to apply no styling and leave it to the environment matplotlibrc.
	title_color : string, optional
		String specifying the desired color for the title.
		This needs to be specified in-function, because the matplotlibrc styling standard does not provide for title color specification [matplotlibrc_title]

	References
	----------

	.. [matplotlibrc_title] https://stackoverflow.com/questions/30109465/matplotlib-set-title-color-in-stylesheet
	"""

    if len(substitutions) == 0:
        print(
            'ERROR: You have specified a substitution dictionary of length 0. There needs to be at least one set of substitutions. If your string contains no formatting fields, please pass a list containing an empty dictionary to the `sbstitution parameter` (this is also its default value).'
        )

    plotting_module_path = path.dirname(path.realpath(__file__))
    if style == 'light':
        black_bg = False
        anatomical_cmap = 'binary'
        style_path = path.join(plotting_module_path, 'contour_slices.conf')
        plt.style.use([style_path])
    elif style == 'dark':
        black_bg = True
        anatomical_cmap = 'binary_r'
        style_path = path.join(plotting_module_path,
                               'contour_slices_dark.conf')
        plt.style.use([style_path])
    else:
        anatomical_cmap = 'binary'
        black_bg = False

    bg_image = path.abspath(path.expanduser(bg_image))
    bg_img = nib.load(bg_image)
    if bg_img.header['dim'][0] > 3:
        bg_data = bg_img.get_data()
        ndim = 0
        for i in range(len(bg_img.header['dim']) - 1):
            current_dim = bg_img.header['dim'][i + 1]
            if current_dim == 1:
                break
            ndim += 1
        bg_img.header['dim'][0] = ndim
        bg_img.header['pixdim'][ndim + 1:] = 0
        bg_data = bg_data.T[0].T
        bg_img = nib.nifti1.Nifti1Image(bg_data, bg_img.affine, bg_img.header)

    imgs = []
    bounds = []
    levels = []
    slice_order_is_reversed = 0
    for substitution in substitutions:
        filename = file_template.format(**substitution)
        filename = path.abspath(path.expanduser(filename))
        img = nib.load(filename)
        data = img.get_data()
        if img.header['dim'][0] > 3:
            img = collapse(img)
        if invert:
            data = -data
            img = nib.nifti1.Nifti1Image(data, img.affine, img.header)
        #we should only be looking at the percentile of the entire data matrix, rather than just the active slice
        for level_percentile in levels_percentile:
            level = np.percentile(data, level_percentile)
            levels.append(level)
        slice_row = img.affine[1]
        subthreshold_start_slices = 0
        while True:
            for i in np.arange(data.shape[1]):
                my_slice = data[:, i, :]
                if my_slice.max() < min(levels):
                    subthreshold_start_slices += 1
                else:
                    break
            break
        subthreshold_end_slices = 0
        while True:
            for i in np.arange(data.shape[1])[::-1]:
                my_slice = data[:, i, :]
                if my_slice.max() < min(levels):
                    subthreshold_end_slices += 1
                else:
                    break
            break
        slice_thickness = (slice_row[0]**2 + slice_row[1]**2 +
                           slice_row[2]**2)**(1 / 2)
        best_guess_negative = abs(min(slice_row[0:3])) > abs(
            max(slice_row[0:3]))
        slices_number = data.shape[list(slice_row).index(max(slice_row))]
        img_min_slice = slice_row[
            3] + subthreshold_start_slices * slice_thickness
        img_max_slice = slice_row[3] + (
            slices_number - subthreshold_end_slices) * slice_thickness
        bounds.extend([img_min_slice, img_max_slice])
        if best_guess_negative:
            slice_order_is_reversed += 1
        else:
            slice_order_is_reversed -= 1
        imgs.append(img)

    if len(alpha) == 1:
        alpha = alpha * len(imgs)
    min_slice = min(bounds)
    max_slice = max(bounds)
    cut_coords = np.arange(min_slice, max_slice, slice_spacing)
    if slice_order_is_reversed > 0:
        cut_coords = cut_coords[::-1]
    if force_reverse_slice_order:
        cut_coords = cut_coords[::-1]

    if not linewidths:
        linewidths = (rcParams['axes.linewidth'], ) * len(imgs)

    if len(cut_coords) > 3:
        cut_coord_length = len(cut_coords)
        if legend_template:
            cut_coord_length += 1
        try:
            nrows, ncols = ratio
        except ValueError:
            if ratio == "portrait":
                ncols = np.floor(cut_coord_length**scale)
                nrows = np.ceil(cut_coord_length / float(ncols))
            elif ratio == "landscape":
                nrows = np.floor(cut_coord_length**(scale))
                ncols = np.ceil(cut_coord_length / float(nrows))
        # we adjust the respective rc.Param here, because it needs to be set before drawing to take effect
        if legend_template and cut_coord_length == ncols * (nrows - 1) + 1:
            rcParams['figure.subplot.bottom'] = np.max(
                [rcParams['figure.subplot.bottom'] - 0.05, 0.])

        if auto_figsize:
            figsize = np.array(rcParams['figure.figsize'])
            figsize_scales = figsize / np.array([float(ncols), float(nrows)])
            figsize_scale = figsize_scales.min()
            fig, ax = plt.subplots(
                figsize=(ncols * figsize_scale, nrows * figsize_scale),
                nrows=int(nrows),
                ncols=int(ncols),
            )
        else:
            fig, ax = plt.subplots(
                nrows=int(nrows),
                ncols=int(ncols),
            )
        flat_axes = list(ax.flatten())
        for ix, ax_i in enumerate(flat_axes):
            try:
                display = nilearn.plotting.plot_anat(
                    bg_img,
                    axes=ax_i,
                    display_mode='y',
                    cut_coords=[cut_coords[ix]],
                    annotate=False,
                    black_bg=black_bg,
                    dim=dimming,
                    cmap=anatomical_cmap,
                )
            except IndexError:
                ax_i.axis('off')
            else:
                for img_ix, img in enumerate(imgs):
                    color = colors[img_ix]
                    display.add_contours(
                        img,
                        alpha=alpha[img_ix],
                        colors=[color],
                        levels=levels[img_ix],
                        linewidths=(linewidths[img_ix], ),
                    )

        if legend_template:
            for ix, img in enumerate(imgs):
                insertion_legend, = plt.plot(
                    [], [],
                    color=colors[ix],
                    label=legend_template.format(**substitutions[ix]))
            if cut_coord_length == ncols * (nrows - 1) + 1:
                plt.legend(loc='upper left', bbox_to_anchor=(-0.1, -0.3))
            else:
                plt.legend(loc='lower left', bbox_to_anchor=(1.1, 0.))
    else:
        display = nilearn.plotting.plot_anat(
            bg_img,
            display_mode='y',
            cut_coords=cut_coords,
            black_bg=black_bg,
        )
        for ix, img in enumerate(imgs):
            color = colors[ix]
            display.add_contours(img, levels=levels, colors=[color])

    if figure_title:
        fig.suptitle(figure_title, color=title_color)

    if save_as:
        save_as = save_as.format(**substitutions[0])
        save_as = path.abspath(path.expanduser(save_as))
        save_dir, _ = os.path.split(save_as)
        if not os.path.exists(save_dir):
            os.makedirs(save_dir)
        plt.savefig(save_as,
                    #facecolor=fig.get_facecolor(),
                    )
        plt.close()
コード例 #2
0
ファイル: snr.py プロジェクト: rezaie99/SAMRI
def threshold_volume(
    in_file,
    substitution={},
    masker='',
    threshold=45,
    threshold_is_percentile=False,
    inverted_data=False,
):
    """Return the volume which lies above a given threshold in a NIfTI (implicitly, in the volume units of the respective NIfTI).

	Parameters
	----------
	in_file : str
		Path to a NIfTI file.
		4D files will be collapsed to 3D using the mean function.
	masker : str or nilearn.NiftiMasker, optional
		Path to a NIfTI file containing a mask (1 and 0 values) or a `nilearn.NiftiMasker` object.
		NOT YET SUPPORTED!
	threshold : float, optional
		A float giving the voxel value threshold.
	threshold_is_percentile : bool, optional
		Whether `threshold` is to be interpreted not literally, but as a percentile of the data matrix.
		This is useful for making sure that the volume estimation is not susceptible to the absolute value range, but only the value distribution.
	inverted_data : bool, optional
		Whether data is inverted (flipped with respect to 0).
		If `True`, the inverse of the threshold is automatically computed.

	Returns
	-------
	float
		The volume (in volume units of the respective NIfTI) containing values above the given threshold.
	"""

    if substitution:
        in_file = in_file.format(**substitution)
    in_file = path.abspath(path.expanduser(in_file))
    try:
        img = nib.load(in_file)
    except FileNotFoundError:
        return float('NaN')

    if img.header['dim'][0] > 4:
        raise ValueError(
            "Files with more than 4 dimensions are not currently supported.")
    elif img.header['dim'][0] > 3:
        img = collapse(img)
    data = img.get_data()
    x_len = (img.affine[0][0]**2 + img.affine[0][1]**2 +
             img.affine[0][2]**2)**(1 / 2.)
    y_len = (img.affine[1][0]**2 + img.affine[1][1]**2 +
             img.affine[1][2]**2)**(1 / 2.)
    z_len = (img.affine[2][0]**2 + img.affine[2][1]**2 +
             img.affine[2][2]**2)**(1 / 2.)
    voxel_volume = x_len * y_len * z_len

    if inverted_data and not threshold_is_percentile:
        threshold_voxels = (data < threshold).sum()
    else:
        if inverted_data:
            threshold = 100 - threshold
        if threshold_is_percentile:
            threshold = np.percentile(data, threshold)
        threshold_voxels = (data > threshold).sum()

    threshold_volume = voxel_volume * threshold_voxels

    return threshold_volume
コード例 #3
0
ファイル: maps.py プロジェクト: aswinnarayanan/SAMRI
def slices(heatmap_image,
	bg_image='/usr/share/mouse-brain-atlases/dsurqec_40micron_masked.nii',
	contour_image='',
	heatmap_threshold=3,
	heatmap_alpha=1.0,
	contour_threshold=3,
	auto_figsize=False,
	invert=False,
	contour_alpha=0.9,
	contour_color='g',
	cmap='autumn_r',
	dimming=0.,
	figure_title='',
	force_reverse_slice_order=True,
	legend=False,
	aspect='portrait',
	save_as='',
	ratio=3/4.,
	slice_spacing=0.4,
	style='light',
	title_color='#BBBBBB',
	position_vspace=0.0,
	positive_only=False,
	negative_only=False,
	skip_start=0,
	skip_end=0,
	):
	"""
	Plot coronal `bg_image` slices at a given spacing, and overlay contours from a list of NIfTI files.

	Parameters
	----------

	heatmap_image : str
		Path to an overlay image to be printed as a heatmap.
	bg_image : str, optional
		Path to the NIfTI image to draw in grayscale as th eplot background.
		This would commonly be some sort of brain template.
	contour_image : str, optional
		Path to an overlay image to be printed as a contour.
	heatmap_threshold : float, optional
		Value at which to threshold the heatmap_image.
	heatmap_alpha : float, optional
		Alpha (opacity, from 0.0 to 1.0) with which to draw the contour image.
	contour_threshold : float, optional
		Value at which to threshold the contour_image.
	auto_figsize : boolean, optional
		Whether to automatically determine the size of the figure.
	invert : boolean, optional
		Whether to automatically invert data matrix values (useful if the image consists of negative values, e.g. when dealing with negative contrast agent CBV scans).
	contour_alpha : float, optional
		Alpha (opacity, from 0.0 to 1.0) with which to draw the contour image.
	contour_color : str, optional
		Color with which to draw the contour image.
	cmap : str, optional
		Colormap with which to draw the heatmap image.
	dimming : float, optional
		Dimming factor, generally between -2 and 2 (-2 increases contrast, 2 decreases it).
		This parameter is passed directly to `nilearn.plotting.plot_anat()`
		Set to 'auto', to use nilearn automagick dimming.
	figure_title : str, optional
		Title for the figure.
	force_reverse_slice_order : bool, optional
		Whether to force the reversal of the slice order.
		This can be done to enforce a visual presentation without having to modify the underlying data (i.e. visualize neurological-order slices in radiological order).
		This option should generally be avoided, ideally one would not obfuscate the data orientation when plotting.
	legend : string, optional
		The legend text.
	aspect : list or {'landscape', 'portrait'}, optional
		Either a list of 2 integers giving the desired number of rows and columns (in this order), or a string, which is either 'landscape' or 'portrait', and which prompts the function to auto-determine the best number of rows and columns given the number of slices and the `scale` attribute.
	save_as : str, optional
		Path under which to save the output figure.
	ratio : float, optional
		The desired ratio between the number of columns and the number of rows in the desired figure layout.
	slice_spacing : float
		Slice spacing in mm.
	style : {'light', 'dark', ''}, optional
		Default SAMRI styling which to apply, set to an empty string to apply no styling and leave it to the environment matplotlibrc.
	title_color : string, optional
		String specifying the desired color for the title.
		This needs to be specified in-function, because the matplotlibrc styling standard does not provide for title color specification [matplotlibrc_title]
	position_vspace : float, optional
		Vertical distance adjustment between slice and coordinate text annotation.
	skip_start : int, optional
		Number of slices (at the slice spacing given by `slice_spacing`) to skip at the start of the listing.
	skip_end : int, optional
		Number of slices (at the slice spacing given by `slice_spacing`) to skip at the end of the listing.

	References
	----------

	.. [matplotlibrc_title] https://stackoverflow.com/questions/30109465/matplotlib-set-title-color-in-stylesheet
	"""

	plotting_module_path = path.dirname(path.realpath(__file__))
	if style=='light':
		black_bg=False
		anatomical_cmap = 'binary'
		style_path = path.join(plotting_module_path,'contour_slices.conf')
		plt.style.use([style_path])
	elif style=='dark':
		black_bg=True
		anatomical_cmap = 'binary_r'
		style_path = path.join(plotting_module_path,'contour_slices_dark.conf')
		plt.style.use([style_path])
	else:
		anatomical_cmap = 'binary'
		black_bg=False

	bg_image = path.abspath(path.expanduser(bg_image))
	bg_img = nib.load(bg_image)
	if bg_img.header['dim'][0] > 3:
		bg_img = collapse(bg_img)

	slice_order_is_reversed = 0
	heatmap_image = path.abspath(path.expanduser(heatmap_image))
	heatmap_img = nib.load(heatmap_image)
	heatmap_data = heatmap_img.get_data()
	if heatmap_img.header['dim'][0] > 3:
		img = collapse(heatmap_img)
	if contour_image:
		contour_image = path.abspath(path.expanduser(contour_image))
		contour_img = nib.load(contour_image)
		if contour_img.header['dim'][0] > 3:
			contour_img = collapse(contour_img)
		# We apply thresholding here, rather than when drawing the contours, to ensure the same contour color in all slices.
		# This is possibly a bug in nilearn.
		contour_img = from_img_threshold(contour_img, contour_threshold)

	#we should only be looking at the percentile of the entire data matrix, rather than just the active slice
	slice_row = heatmap_img.affine[1]
	subthreshold_start_slices = 0
	while True:
		for i in np.arange(heatmap_data.shape[1]):
			my_slice = heatmap_data[:,i,:]
			if math.isnan(my_slice.max()) or my_slice.max() < heatmap_threshold:
				subthreshold_start_slices += 1
			else:
				break
		break
	subthreshold_end_slices = 0
	while True:
		for i in np.arange(heatmap_data.shape[1])[::-1]:
			my_slice = heatmap_data[:,i,:]
			if math.isnan(my_slice.max()) or my_slice.max() < heatmap_threshold:
				subthreshold_end_slices += 1
			else:
				break
		break
	slice_thickness = (slice_row[0]**2+slice_row[1]**2+slice_row[2]**2)**(1/2)
	best_guess_negative = abs(min(slice_row[0:3])) > abs(max(slice_row[0:3]))
	slices_number = heatmap_data.shape[list(slice_row).index(max(slice_row))]
	skip_start = skip_start*slice_spacing/slice_thickness
	skip_end = skip_end*slice_spacing/slice_thickness
	img_min_slice = slice_row[3] + (subthreshold_start_slices+skip_start)*slice_thickness
	img_max_slice = slice_row[3] + (slices_number-subthreshold_end_slices-skip_end)*slice_thickness
	bounds = [img_min_slice,img_max_slice]
	if best_guess_negative:
		slice_order_is_reversed += 1
	else:
		slice_order_is_reversed -= 1

	min_slice = min(bounds)
	max_slice = max(bounds)
	cut_coords = np.arange(min_slice, max_slice, slice_spacing)
	if slice_order_is_reversed > 0:
		cut_coords = cut_coords[::-1]
	if force_reverse_slice_order:
		cut_coords = cut_coords[::-1]

	linewidth = rcParams['lines.linewidth']

	cut_coord_length = len(cut_coords)
	if legend:
		cut_coord_length += 1
	try:
		nrows, ncols = aspect
	except ValueError:
		if aspect == "portrait":
			ncols = np.ceil((cut_coord_length*ratio)**(1/2))
			nrows = np.ceil(cut_coord_length/ncols)
		elif aspect == "landscape":
			nrows = np.ceil((cut_coord_length*ratio)**(1/2))
			ncols = np.ceil(cut_coord_length/nrows)
	# we adjust the respective rc.Param here, because it needs to be set before drawing to take effect
	if legend and cut_coord_length == ncols*(nrows-1)+1:
		rcParams['figure.subplot.bottom'] = np.max([rcParams['figure.subplot.bottom']-0.05,0.])

	if auto_figsize:
		figsize = np.array(rcParams['figure.figsize'])
		figsize_scales = figsize/np.array([float(ncols),float(nrows)])
		figsize_scale = figsize_scales.min()
		fig, ax = plt.subplots(figsize=(ncols*figsize_scale,nrows*figsize_scale),
				nrows=int(nrows), ncols=int(ncols),
				)
	else:
		figsize = np.array(rcParams['figure.figsize'])
		fig, ax = plt.subplots(
				nrows=int(nrows), ncols=int(ncols),
				)
	flat_axes = list(ax.flatten())

	if cmap and heatmap_image:
		cax, kw,vmin,vmax,cmap = _draw_colorbar(heatmap_image,ax,
			threshold=heatmap_threshold,
			aspect=40,
			fraction=0.05,
			anchor=(0,-0.5),
			pad=0.05,
			panchor=(10.0, 0.5),
			shrink=0.99,
			cut_coords = cut_coords,
			positive_only = positive_only,
			negative_only = negative_only,
			cmap=cmap,
			really_draw=True,
			)
	if positive_only:
		vmin = 0
	elif negative_only:
		vmax = 0
	for ix, ax_i in enumerate(flat_axes):
		try:
			display = nilearn.plotting.plot_anat(bg_img,
				axes=ax_i,
				display_mode='y',
				cut_coords=[cut_coords[ix]],
				annotate=False,
				black_bg=black_bg,
				dim=dimming,
				cmap=anatomical_cmap,
				)
		except IndexError:
			ax_i.axis('off')
		else:
			display.add_overlay(heatmap_img,
				threshold=heatmap_threshold,
				alpha=heatmap_alpha,
				cmap=cmap,
				vmin = vmin,vmax = vmax,
				)
			if contour_image:
				display.add_contours(contour_img,
					alpha=contour_alpha,
					levels=[0.8],
					linewidths=linewidth,
					)
			ax_i.set_xlabel('{} label'.format(ix))
			slice_title = '{0:.2f}mm'.format(cut_coords[ix])
			text = ax_i.text(0.5,-position_vspace,
				slice_title,
				horizontalalignment='center',
				fontsize=rcParams['font.size'],
				)
	if legend:
		for ix, img in enumerate(imgs):
			insertion_legend, = plt.plot([],[], color=colors[ix], label=legend)
		if cut_coord_length == ncols*(nrows-1)+1:
			plt.legend(loc='upper left',bbox_to_anchor=(-0.1, -0.3))
		else:
			plt.legend(loc='lower left',bbox_to_anchor=(1.1, 0.))

	if figure_title:
		fig.suptitle(figure_title, color=title_color)

	if save_as:
		save_as = path.abspath(path.expanduser(save_as))
		save_dir,_ = os.path.split(save_as)
		try:
			os.makedirs(save_dir)
		except FileExistsError:
			pass
		plt.savefig(save_as)
		plt.close()
コード例 #4
0
ファイル: maps.py プロジェクト: TheChymera/chyMRI
def contour_slices(bg_image, file_template,
	auto_figsize=False,
	invert=False,
	alpha=[0.9],
	colors=['r','g','b'],
	dimming=0.,
	figure_title='',
	force_reverse_slice_order=True,
	legend_template='',
	levels_percentile=[80],
	linewidths=(),
	ratio='portrait',
	save_as='',
	scale=0.4,
	slice_spacing=0.5,
	substitutions=[{},],
	style='light',
	title_color='#BBBBBB',
	):
	"""
	Plot coronal `bg_image` slices at a given spacing, and overlay contours from a list of NIfTI files.

	Parameters
	----------

	bg_image : str
		Path to the NIfTI image to draw in grayscale as th eplot background.
		This would commonly be some sort of brain template.
	file_template : str
		String template giving the path to the overlay stack.
		To create multiple overlays, this template will iteratively be substituted with each of the substitution dictionaries in the `substitutions` parameter.
	auto_figsize : boolean, optional
		Whether to automatically determine the size of the figure.
	invert : boolean, optional
		Whether to automatically invert data matrix values (useful if the image consists of negative values, e.g. when dealing with negative contrast agent CBV scans).
	alpha : list, optional
		List of floats, specifying with how much alpha to draw each contour.
	colors : list, optional
		List of colors in which to plot the overlays.
	dimming : float, optional
		Dimming factor, generally between -2 and 2 (-2 increases contrast, 2 decreases it).
		This parameter is passed directly to `nilearn.plotting.plot_anat()`
		Set to 'auto', to use nilearn automagick dimming.
	figure_title : str, optional
		Title for the figure.
	force_reverse_slice_order : bool, optional
		Whether to force the reversal of the slice order.
		This can be done to enforce a visual presentation without having to modify the underlying data (i.e. visualize neurological-order slices in radiological order).
		This option should generally be avoided, ideally one would not obfuscate the data orientation when plotting.
	legend_template : string, optional
		String template which can be formatted with the dictionaries contained in the `substitutions` parameter.
		The resulting strings will give the legend text.
	levels_percentile : list, optional
		List of integers, specifying at which percentiles of each overlay to draw contours.
	line_widths : tuple, optional
		Tuple of desired contour line widths (one per substitution).
	ratio : list or {'landscape', 'portrait'}, optional
		Either a list of 2 integers giving the desired number of rows and columns (in this order), or a string, which is either 'landscape' or 'portrait', and which prompts the function to auto-determine the best number of rows and columns given the number of slices and the `scale` attribute.
	save_as : str, optional
		Path under which to save the output figure.
		The string may contain formatting fields from the first dictionary in the `substitutions` variable.
	scale : float, optional
		The expected ratio of the slice height divided by the sum of the slice height and width.
		This somewhat complex metric controls the row and column distribution of slices in the 'landscape' and 'portrait' plotting shapes.
	slice_spacing : float
		Slice spacing in mm.
	substitutions : list of dicts, optional
		A list of dictionaries, with keys including all substitution keys found in the `file_template` parameter, and values giving desired substitution values which point the `file_template` string templated to existing filed which are to be included in the overlay stack.
		Such a dictionary is best obtained via `samri.utilities.bids_substitution_iterator()`.
	style : {'light', 'dark', ''}, optional
		Default SAMRI styling which to apply, set to an empty string to apply no styling and leave it to the environment matplotlibrc.
	title_color : string, optional
		String specifying the desired color for the title.
		This needs to be specified in-function, because the matplotlibrc styling standard does not provide for title color specification [matplotlibrc_title]

	References
	----------

	.. [matplotlibrc_title] https://stackoverflow.com/questions/30109465/matplotlib-set-title-color-in-stylesheet
	"""

	if len(substitutions) == 0:
		print('ERROR: You have specified a substitution dictionary of length 0. There needs to be at least one set of substitutions. If your string contains no formatting fields, please pass a list containing an empty dictionary to the `sbstitution parameter` (this is also its default value).')

	plotting_module_path = path.dirname(path.realpath(__file__))
	if style=='light':
		black_bg=False
		anatomical_cmap = 'binary'
		style_path = path.join(plotting_module_path,'contour_slices.conf')
		plt.style.use([style_path])
	elif style=='dark':
		black_bg=True
		anatomical_cmap = 'binary_r'
		style_path = path.join(plotting_module_path,'contour_slices_dark.conf')
		plt.style.use([style_path])
	else:
		anatomical_cmap = 'binary'
		black_bg=False

	bg_image = path.abspath(path.expanduser(bg_image))
	bg_img = nib.load(bg_image)
	if bg_img.header['dim'][0] > 3:
		bg_data = bg_img.get_data()
		ndim = 0
		for i in range(len(bg_img.header['dim'])-1):
			current_dim = bg_img.header['dim'][i+1]
			if current_dim == 1:
				break
			ndim += 1
		bg_img.header['dim'][0] = ndim
		bg_img.header['pixdim'][ndim+1:] = 0
		bg_data = bg_data.T[0].T
		bg_img = nib.nifti1.Nifti1Image(bg_data, bg_img.affine, bg_img.header)

	imgs = []
	bounds = []
	levels = []
	slice_order_is_reversed = 0
	for substitution in substitutions:
		filename = file_template.format(**substitution)
		filename = path.abspath(path.expanduser(filename))
		img = nib.load(filename)
		data = img.get_data()
		if img.header['dim'][0] > 3:
			img = collapse(img)
		if invert:
			data = -data
			img = nib.nifti1.Nifti1Image(data, img.affine, img.header)
		#we should only be looking at the percentile of the entire data matrix, rather than just the active slice
		for level_percentile in levels_percentile:
			level = np.percentile(data,level_percentile)
			levels.append(level)
		slice_row = img.affine[1]
		subthreshold_start_slices = 0
		while True:
			for i in np.arange(data.shape[1]):
				my_slice = data[:,i,:]
				if my_slice.max() < min(levels):
					subthreshold_start_slices += 1
				else:
					break
			break
		subthreshold_end_slices = 0
		while True:
			for i in np.arange(data.shape[1])[::-1]:
				my_slice = data[:,i,:]
				if my_slice.max() < min(levels):
					subthreshold_end_slices += 1
				else:
					break
			break
		slice_thickness = (slice_row[0]**2+slice_row[1]**2+slice_row[2]**2)**(1/2)
		best_guess_negative = abs(min(slice_row[0:3])) > abs(max(slice_row[0:3]))
		slices_number = data.shape[list(slice_row).index(max(slice_row))]
		img_min_slice = slice_row[3] + subthreshold_start_slices*slice_thickness
		img_max_slice = slice_row[3] + (slices_number-subthreshold_end_slices)*slice_thickness
		bounds.extend([img_min_slice,img_max_slice])
		if best_guess_negative:
			slice_order_is_reversed += 1
		else:
			slice_order_is_reversed -= 1
		imgs.append(img)

	if len(alpha) == 1:
		alpha = alpha * len(imgs)
	min_slice = min(bounds)
	max_slice = max(bounds)
	cut_coords = np.arange(min_slice, max_slice, slice_spacing)
	if slice_order_is_reversed > 0:
		cut_coords = cut_coords[::-1]
	if force_reverse_slice_order:
		cut_coords = cut_coords[::-1]

	if not linewidths:
		linewidths = (rcParams['axes.linewidth'],)*len(imgs)

	if len(cut_coords) > 3:
		cut_coord_length = len(cut_coords)
		if legend_template:
			cut_coord_length += 1
		try:
			nrows, ncols = ratio
		except ValueError:
			if ratio == "portrait":
				ncols = np.floor(cut_coord_length**scale)
				nrows = np.ceil(cut_coord_length/float(ncols))
			elif ratio == "landscape":
				nrows = np.floor(cut_coord_length**(scale))
				ncols = np.ceil(cut_coord_length/float(nrows))
		# we adjust the respective rc.Param here, because it needs to be set before drawing to take effect
		if legend_template and cut_coord_length == ncols*(nrows-1)+1:
			rcParams['figure.subplot.bottom'] = np.max([rcParams['figure.subplot.bottom']-0.05,0.])

		if auto_figsize:
			figsize = np.array(rcParams['figure.figsize'])
			figsize_scales = figsize/np.array([float(ncols),float(nrows)])
			figsize_scale = figsize_scales.min()
			fig, ax = plt.subplots(figsize=(ncols*figsize_scale,nrows*figsize_scale),
					nrows=int(nrows), ncols=int(ncols),
					)
		else:
			fig, ax = plt.subplots(
					nrows=int(nrows), ncols=int(ncols),
					)
		flat_axes = list(ax.flatten())
		for ix, ax_i in enumerate(flat_axes):
			try:
				display = nilearn.plotting.plot_anat(bg_img,
					axes=ax_i,
					display_mode='y',
					cut_coords=[cut_coords[ix]],
					annotate=False,
					black_bg=black_bg,
					dim=dimming,
					cmap=anatomical_cmap,
					)
			except IndexError:
				ax_i.axis('off')
			else:
				for img_ix, img in enumerate(imgs):
					color = colors[img_ix]
					display.add_contours(img,
							alpha=alpha[img_ix],
							colors=[color],
							levels=levels[img_ix],
							linewidths=(linewidths[img_ix],),
							)

		if legend_template:
			for ix, img in enumerate(imgs):
				insertion_legend, = plt.plot([],[], color=colors[ix], label=legend_template.format(**substitutions[ix]))
			if cut_coord_length == ncols*(nrows-1)+1:
				plt.legend(loc='upper left',bbox_to_anchor=(-0.1, -0.3))
			else:
				plt.legend(loc='lower left',bbox_to_anchor=(1.1, 0.))
	else:
		display = nilearn.plotting.plot_anat(bg_img,
			display_mode='y',
			cut_coords=cut_coords,
			black_bg=black_bg,
			)
		for ix, img in enumerate(imgs):
			color = colors[ix]
			display.add_contours(img, levels=levels, colors=[color])

	if figure_title:
		fig.suptitle(figure_title, color=title_color)

	if save_as:
		save_as = save_as.format(**substitutions[0])
		save_as = path.abspath(path.expanduser(save_as))
		save_dir,_ = os.path.split(save_as)
		try:
			os.makedirs(save_dir)
		except FileExistsError:
			pass
		plt.savefig(save_as,
			#facecolor=fig.get_facecolor(),
			)
		plt.close()
コード例 #5
0
ファイル: snr.py プロジェクト: TheChymera/chyMRI
def threshold_volume(in_file,
	substitution={},
	masker='',
	threshold=45,
	threshold_is_percentile=False,
	inverted_data=False,
	):
	"""Return the volume which lies above a given threshold in a NIfTI (implicitly, in the volume units of the respective NIfTI).

	Parameters
	----------
	in_file : str
		Path to a NIfTI file.
		4D files will be collapsed to 3D using the mean function.
	masker : str or nilearn.NiftiMasker, optional
		Path to a NIfTI file containing a mask (1 and 0 values) or a `nilearn.NiftiMasker` object.
		NOT YET SUPPORTED!
	threshold : float, optional
		A float giving the voxel value threshold.
	threshold_is_percentile : bool, optional
		Whether `threshold` is to be interpreted not literally, but as a percentile of the data matrix.
		This is useful for making sure that the volume estimation is not susceptible to the absolute value range, but only the value distribution.
	inverted_data : bool, optional
		Whether data is inverted (flipped with respect to 0).
		If `True`, the inverse of the threshold is automatically computed.

	Returns
	-------
	float
		The volume (in volume units of the respective NIfTI) containing values above the given threshold.
	"""

	if substitution:
		in_file = in_file.format(**substitution)
	in_file = path.abspath(path.expanduser(in_file))
	try:
		img = nib.load(in_file)
	except FileNotFoundError:
		return float('NaN')

	if img.header['dim'][0] > 4:
		raise ValueError("Files with more than 4 dimensions are not currently supported.")
	elif img.header['dim'][0] > 3:
		img = collapse(img)
	data = img.get_data()
	x_len = (img.affine[0][0]**2+img.affine[0][1]**2+img.affine[0][2]**2)**(1/2.)
	y_len = (img.affine[1][0]**2+img.affine[1][1]**2+img.affine[1][2]**2)**(1/2.)
	z_len = (img.affine[2][0]**2+img.affine[2][1]**2+img.affine[2][2]**2)**(1/2.)
	voxel_volume = x_len*y_len*z_len

	if inverted_data and not threshold_is_percentile:
		threshold_voxels = (data < threshold).sum()
	else:
		if inverted_data:
			threshold = 100-threshold
		if threshold_is_percentile:
			threshold = np.percentile(data,threshold)
		threshold_voxels = (data > threshold).sum()

	threshold_volume = voxel_volume * threshold_voxels

	return threshold_volume