"""
Functions to do automatic visualization of Niimg-like objects
See http://nilearn.github.io/manipulating_images/input_output.html

Only matplotlib is required.
"""

# Author: Gael Varoquaux, Chris Filo Gorgolewski
# License: BSD

# Standard library imports
import os
import collections.abc
import functools
import numbers
import warnings
from distutils.version import LooseVersion

# Standard scientific libraries imports (more specific imports are
# delayed, so that the part module can be used without them).
import numpy as np
from scipy import ndimage
from scipy import sparse
from scipy import stats
from nibabel.spatialimages import SpatialImage

from ..signal import clean
from .._utils.numpy_conversions import as_ndarray
from .._utils.niimg import _safe_get_data

import matplotlib
import matplotlib.pyplot as plt
from matplotlib import gridspec as mgs

from .. import _utils
from .._utils.extmath import fast_abs_percentile
from .._utils.param_validation import check_threshold
from .._utils.ndimage import get_border_data
from ..datasets import load_mni152_template
from ..image import new_img_like, iter_img, get_data, math_img, resample_to_img
from ..input_data import NiftiMasker
from nilearn.image.resampling import reorder_img
from ..masking import compute_epi_mask, apply_mask
from .displays import get_slicer, get_projector
from . import cm


def show():
    """Show all the figures generated by nilearn and/or matplotlib.

    This function is equivalent to :func:`matplotlib.pyplot.show`,
    but is skipped on the 'Agg' backend where it has no effect other
    than to emit a warning.

    """
    if matplotlib.get_backend().lower() != 'agg':  # avoid warnings
        plt.show()


###############################################################################
# Core, usage-agnostic functions


def _get_colorbar_and_data_ranges(stat_map_data, vmax, symmetric_cbar, kwargs,
                                  force_min_stat_map_value=None):
    """Internal function for setting colormap and colorbar limits.

    Used by for plot_stat_map and plot_glass_brain.

    The limits for the colormap will always be set to range from -vmax to vmax.
    The limits for the colorbar depend on the symmetric_cbar argument, please
    refer to docstring of plot_stat_map.

    """
    if 'vmin' in kwargs:
        raise ValueError('this function does not accept a "vmin" '
                         'argument, as it uses a symmetrical range '
                         'defined via the vmax argument. To threshold '
                         'the plotted map, use the "threshold" argument')

    # make sure that the color range is symmetrical
    if vmax is None or symmetric_cbar in ['auto', False]:
        # Avoid dealing with masked_array:
        if hasattr(stat_map_data, '_mask'):
            stat_map_data = np.asarray(
                stat_map_data[np.logical_not(stat_map_data._mask)])
        stat_map_max = np.nanmax(stat_map_data)
        if force_min_stat_map_value is None:
            stat_map_min = np.nanmin(stat_map_data)
        else:
            stat_map_min = force_min_stat_map_value

    if symmetric_cbar == 'auto':
        symmetric_cbar = stat_map_min < 0 and stat_map_max > 0

    if vmax is None:
        vmax = max(-stat_map_min, stat_map_max)
    vmin = -vmax

    if not symmetric_cbar:
        negative_range = stat_map_max <= 0
        positive_range = stat_map_min >= 0
        if positive_range:
            cbar_vmin = 0
            cbar_vmax = None
        elif negative_range:
            cbar_vmax = 0
            cbar_vmin = None
        else:
            cbar_vmin = stat_map_min
            cbar_vmax = stat_map_max
    else:
        cbar_vmin, cbar_vmax = None, None
    return cbar_vmin, cbar_vmax, vmin, vmax


def _plot_img_with_bg(img, bg_img=None, cut_coords=None,
                      output_file=None, display_mode='ortho',
                      colorbar=False, figure=None, axes=None, title=None,
                      threshold=None, annotate=True,
                      draw_cross=True, black_bg=False,
                      vmin=None, vmax=None,
                      bg_vmin=None, bg_vmax=None, interpolation="nearest",
                      display_factory=get_slicer,
                      cbar_vmin=None, cbar_vmax=None,
                      brain_color=(0.5, 0.5, 0.5),
                      decimals=False,
                      **kwargs):
    """Internal function, please refer to the docstring of plot_img for
    parameters not listed below.

    Parameters
    ----------
    img : Niimg like object
        Image to plot.

    bg_vmin : float, optional
        vmin for bg_img.

    bg_vmax : float, optional
        vmax for bg_img.

    interpolation : string, optional
        Passed to the add_overlay calls.
        Default='nearest'.

    display_factory : function, optional
        Takes a display_mode argument and return a display class.
        Default=get_slicer.

    """
    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 (isinstance(cut_coords, numbers.Number) and
            (display_mode == 'ortho' or display_mode == 'tiled')):
        raise ValueError(
            "The input given for display_mode='{0}' needs to be "
            "a list of 3d world coordinates in (x, y, z). "
            "You provided single cut, "
            "cut_coords={1}".format(display_mode, cut_coords))

    if img is not False and img is not None:
        img = _utils.check_niimg_3d(img, dtype='auto')
        data = _safe_get_data(img, ensure_finite=True)
        affine = img.affine

        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

        img = new_img_like(img, as_ndarray(data), affine)

    display = display_factory(display_mode)(
        img,
        threshold=threshold,
        cut_coords=cut_coords,
        figure=figure, axes=axes,
        black_bg=black_bg,
        colorbar=colorbar,
        brain_color=brain_color,
        )

    if bg_img is not None:
        bg_img = _utils.check_niimg_3d(bg_img)
        display.add_overlay(bg_img,
                            vmin=bg_vmin, vmax=bg_vmax,
                            cmap=plt.cm.gray, interpolation=interpolation)

    if img is not None and img is not False:
        display.add_overlay(new_img_like(img, data, affine),
                            threshold=threshold, interpolation=interpolation,
                            colorbar=colorbar, vmin=vmin, vmax=vmax,
                            **kwargs)

    if annotate:
        display.annotate(decimals=decimals)
    if draw_cross:
        display.draw_cross()
    if title is not None and not title == '':
        display.title(title)
    if hasattr(display, '_cbar'):
        cbar = display._cbar
        _crop_colorbar(cbar, cbar_vmin, cbar_vmax)
    if output_file is not None:
        display.savefig(output_file)
        display.close()
        display = None
    return display


def _crop_colorbar(cbar, cbar_vmin, cbar_vmax):
    """Crop a colorbar to show from cbar_vmin to cbar_vmax.
    Used when symmetric_cbar=False is used.

    """
    if (cbar_vmin is None) and (cbar_vmax is None):
        return
    cbar_tick_locs = cbar.locator.locs
    if cbar_vmax is None:
        cbar_vmax = cbar_tick_locs.max()
    if cbar_vmin is None:
        cbar_vmin = cbar_tick_locs.min()
    new_tick_locs = np.linspace(cbar_vmin, cbar_vmax,
                                len(cbar_tick_locs))

    # matplotlib >= 3.2.0 no longer normalizes axes between 0 and 1
    # See https://matplotlib.org/3.2.1/api/prev_api_changes/api_changes_3.2.0.html
    # _outline was removed in
    # https://github.com/matplotlib/matplotlib/commit/03a542e875eba091a027046d5ec652daa8be6863
    # so we use the code from there
    if LooseVersion(matplotlib.__version__) >= LooseVersion("3.2.0"):
        cbar.ax.set_ylim(cbar_vmin, cbar_vmax)
        X, _ = cbar._mesh()
        X = np.array([X[0], X[-1]])
        Y = np.array([[cbar_vmin, cbar_vmin], [cbar_vmax, cbar_vmax]])
        N = X.shape[0]
        ii = [0, 1, N - 2, N - 1, 2 * N - 1, 2 * N - 2, N + 1, N, 0]
        x = X.T.reshape(-1)[ii]
        y = Y.T.reshape(-1)[ii]
        xy = (np.column_stack([y, x])
              if cbar.orientation == 'horizontal' else
              np.column_stack([x, y]))
        cbar.outline.set_xy(xy)
    else:
        cbar.ax.set_ylim(cbar.norm(cbar_vmin), cbar.norm(cbar_vmax))
        outline = cbar.outline.get_xy()
        outline[:2, 1] += cbar.norm(cbar_vmin)
        outline[2:6, 1] -= (1. - cbar.norm(cbar_vmax))
        outline[6:, 1] += cbar.norm(cbar_vmin)
        cbar.outline.set_xy(outline)

    cbar.set_ticks(new_tick_locs, update_ticks=True)


def plot_img(img, cut_coords=None, output_file=None, display_mode='ortho',
             figure=None, axes=None, title=None, threshold=None,
             annotate=True, draw_cross=True, black_bg=False, colorbar=False,
             resampling_interpolation='continuous',
             bg_img=None, vmin=None, vmax=None, **kwargs):
    """ Plot cuts of a given image (by default Frontal, Axial, and Lateral)

    Parameters
    ----------
    img : Niimg-like object
        See http://nilearn.github.io/manipulating_images/input_output.html

    cut_coords : None, a tuple of floats, or an integer, optional
        The MNI coordinates of the point where the cut is performed
        If display_mode is 'ortho' or 'tiled',
        this should be a 3-tuple: (x, y, z)
        For display_mode == 'x', 'y', or 'z', then these are the
        coordinates of each cut in the corresponding direction.
        If None is given, the cuts is calculated automaticaly.
        If display_mode is 'x', 'y' or 'z', cut_coords can be an integer,
        in which case it specifies the number of cuts to perform

    output_file : string, or None, optional
        The name of an image file to export the plot to. Valid extensions
        are .png, .pdf, .svg. If output_file is not None, the plot
        is saved to a file, and the display is closed.

    display_mode : {'ortho', 'tiled', 'x', 'y', 'z', 'yx', 'xz', 'yz'}
        Choose the direction of the cuts: 'x' - sagittal, 'y' - coronal,
        'z' - axial, 'ortho' - three cuts are performed in orthogonal
        directions, 'tiled' - three cuts are performed
        and arranged in a 2x2 grid.
        Default='ortho'.

    figure : integer or matplotlib figure, optional
        Matplotlib figure used or its number. If None is given, a
        new figure is created.

    axes : matplotlib axes or 4 tuple of float: (xmin, ymin, width, height), optional
        The axes, or the coordinates, in matplotlib figure space,
        of the axes used to display the plot. If None, the complete
        figure is used.

    title : string, optional
        The title displayed on the figure.

    threshold : a number, None, or 'auto', optional
        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.

    annotate : boolean, optional
        If annotate is True, positions and left/right annotation
        are added to the plot. Default=True.

    decimals : integer, optional
        Number of decimal places on slice position annotation. If False (default),
        the slice position is integer without decimal point.

    draw_cross : boolean, optional
        If draw_cross is True, a cross is drawn on the plot to
        indicate the cut plosition. Default=True.

    black_bg : boolean, optional
        If True, the background of the image is set to be black. If
        you wish to save figures with a black background, you
        will need to pass "facecolor='k', edgecolor='k'"
        to matplotlib.pyplot.savefig. Default=False.

    colorbar : boolean, optional
        If True, display a colorbar on the right of the plots.
        Default=False.

    resampling_interpolation : str, optional
        Interpolation to use when resampling the image to the destination
        space. Can be "continuous" to use 3rd-order spline interpolation,
        or "nearest" to use nearest-neighbor mapping. "nearest" is faster
        but can be noisier in some cases. Default='continuous'.

    bg_img : Niimg-like object, optional
        See http://nilearn.github.io/manipulating_images/input_output.html
        The background image that the ROI/mask will be plotted on top of.
        If nothing is specified, no background image is plotted.

    vmin : float, optional
        Lower bound of the colormap. If `None`, the min of the image is used.

    vmax : float, optional
        Upper bound of the colormap. If `None`, the max of the image is used.

    kwargs : extra keyword arguments, optional
        Extra keyword arguments passed to matplotlib.pyplot.imshow.

    """  # noqa: E501
    display = _plot_img_with_bg(
        img, cut_coords=cut_coords,
        output_file=output_file, display_mode=display_mode,
        figure=figure, axes=axes, title=title,
        threshold=threshold, annotate=annotate,
        draw_cross=draw_cross,
        resampling_interpolation=resampling_interpolation,
        black_bg=black_bg, colorbar=colorbar,
        bg_img=bg_img, vmin=vmin, vmax=vmax, **kwargs)

    return display


###############################################################################
# Anatomy image for background

# A constant class to serve as a sentinel for the default MNI template
class _MNI152Template(SpatialImage):
    """This class is a constant pointing to the MNI152 Template
    provided by nilearn.

    """
    data = None
    affine = None
    vmax = None
    _shape = None
    # Having a header is required by the load_niimg function
    header = None

    def __init__(self, data=None, affine=None, header=None):
        # Comply with spatial image requirements while allowing empty init
        pass

    def load(self):
        if self.data is None:
            anat_img = load_mni152_template()
            anat_img = reorder_img(anat_img)
            data = get_data(anat_img)
            data = data.astype(np.float64)
            anat_mask = ndimage.morphology.binary_fill_holes(data > 0)
            data = np.ma.masked_array(data, np.logical_not(anat_mask))
            self._affine = anat_img.affine
            self.data = data
            self.vmax = data.max()
            self._shape = anat_img.shape

    @property
    def _data_cache(self):
        self.load()
        return self.data

    @property
    def _dataobj(self):
        self.load()
        return self.data

    def get_data(self):
        self.load()
        return self.data

    @property
    def affine(self):
        self.load()
        return self._affine

    def get_affine(self):
        self.load()
        return self._affine

    @property
    def shape(self):
        self.load()
        return self._shape

    def get_shape(self):
        self.load()
        return self._shape

    def __str__(self):
        return "<MNI152Template>"

    def __repr__(self):
        return "<MNI152Template>"


# The constant that we use as a default in functions
MNI152TEMPLATE = _MNI152Template()


def _load_anat(anat_img=MNI152TEMPLATE, dim='auto', black_bg='auto'):
    """ Internal function used to load anatomy, for optional diming
    """
    vmin = None
    vmax = None
    if anat_img is False or anat_img is None:
        if black_bg == 'auto':
            # No anatomy given: no need to turn black_bg on
            black_bg = False
        return anat_img, black_bg, vmin, vmax

    if anat_img is MNI152TEMPLATE:
        anat_img.load()
        # We special-case the 'canonical anat', as we don't need
        # to do a few transforms to it.
        vmin = 0
        vmax = anat_img.vmax
        if black_bg == 'auto':
            black_bg = False
    else:
        anat_img = _utils.check_niimg_3d(anat_img)
        # Clean anat_img for non-finite values to avoid computing unnecessary
        # border data values.
        data = _safe_get_data(anat_img, ensure_finite=True)
        anat_img = new_img_like(anat_img, data, affine=anat_img.affine)
        if dim or black_bg == 'auto':
            # We need to inspect the values of the image
            vmin = np.nanmin(data)
            vmax = np.nanmax(data)
        if black_bg == 'auto':
            # Guess if the background is rather black or light based on
            # the values of voxels near the border
            background = np.median(get_border_data(data, 2))
            if background > .5 * (vmin + vmax):
                black_bg = False
            else:
                black_bg = True
    if dim:
        if dim != 'auto' and not isinstance(dim, numbers.Number):
            raise ValueError(
                "The input given for 'dim' needs to be a float. "
                "You provided dim=%s in %s" % (str(dim), type(dim)))
        vmean = .5 * (vmin + vmax)
        ptp = .5 * (vmax - vmin)
        if black_bg:
            if not isinstance(dim, numbers.Number):
                dim = .8
            vmax = vmean + (1 + dim) * ptp
        else:
            if not isinstance(dim, numbers.Number):
                dim = .6
            vmin = .5 * (2 - dim) * vmean - (1 + dim) * ptp
    return anat_img, black_bg, vmin, vmax


###############################################################################
# Usage-specific functions


def plot_anat(anat_img=MNI152TEMPLATE, cut_coords=None,
              output_file=None, display_mode='ortho', figure=None,
              axes=None, title=None, annotate=True, threshold=None,
              draw_cross=True, black_bg='auto', dim='auto', cmap=plt.cm.gray,
              vmin=None, vmax=None, **kwargs):
    """Plot cuts of an anatomical image (by default 3 cuts:
    Frontal, Axial, and Lateral)

    Parameters
    ----------
    anat_img : Niimg-like object, optional
        See http://nilearn.github.io/manipulating_images/input_output.html
        The anatomical image to be used as a background. If None is
        given, nilearn tries to find a T1 template.
        Default=MNI152TEMPLATE.

    cut_coords : None, a tuple of floats, or an integer, optional
        The MNI coordinates of the point where the cut is performed
        If display_mode is 'ortho' or 'tiled', this should be a 3-tuple: (x, y, z)
        For display_mode == 'x', 'y', or 'z', then these are the
        coordinates of each cut in the corresponding direction.
        If None is given, the cuts is calculated automaticaly.
        If display_mode is 'x', 'y' or 'z', cut_coords can be an integer,
        in which case it specifies the number of cuts to perform.
        If display_mode is 'mosaic', and the number of cuts is the same
        for all directions, cut_coords can be specified as an integer.
        It can also be a length 3 tuple specifying the number of cuts for
        every direction if these are different.

    output_file : string, or None, optional
        The name of an image file to export the plot to. Valid extensions
        are .png, .pdf, .svg. If output_file is not None, the plot
        is saved to a file, and the display is closed.

    display_mode : {'ortho', 'tiled', 'mosaic', 'x', 'y', 'z', 'yx', 'xz', 'yz'}, optional
        Choose the direction of the cuts: 'x' - sagittal, 'y' - coronal,
        'z' - axial, 'ortho' - three cuts are performed in orthogonal
        directions, 'tiled' - three cuts are performed
        and arranged in a 2x2 grid, 'mosaic' - three cuts
        are performed along multiple rows and columns, Default='ortho'.

    figure : integer or matplotlib figure, optional
        Matplotlib figure used or its number. If None is given, a
        new figure is created.

    axes : matplotlib axes or 4 tuple of float: (xmin, ymin, width, height), optional
        The axes, or the coordinates, in matplotlib figure space,
        of the axes used to display the plot. If None, the complete figure is used.

    title : string, optional
        The title displayed on the figure.

    annotate : boolean, optional
        If annotate is True, positions and left/right annotation
        are added to the plot. Default=True.

    threshold : a number, None, or 'auto', optional
        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.

    draw_cross : boolean, optional
        If draw_cross is True, a cross is drawn on the plot to
        indicate the cut plosition. Default=True.

    black_bg : boolean or 'auto', optional
        If True, the background of the image is set to be black. If
        you wish to save figures with a black background, you
        will need to pass "facecolor='k', edgecolor='k'"
        to matplotlib.pyplot.savefig.
        Default='auto'.

    dim : float or 'auto', optional
        Dimming factor applied to background image. By default, automatic
        heuristics are applied based upon the image intensity.
        Accepted float values, where a typical span is between -2 and 2
        (-2 = increase contrast; 2 = decrease contrast), but larger
        values can be used for a more pronounced effect. 0 means no
        dimming. Default='auto'.

    cmap : matplotlib colormap, optional
        The colormap for the anat. Default=plt.cm.gray.

    vmin : float, optional
        Lower bound for plotting, passed to matplotlib.pyplot.imshow.

    vmax : float, optional
        Upper bound for plotting, passed to matplotlib.pyplot.imshow.

    Notes
    -----
    Arrays should be passed in numpy convention: (x, y, z) ordered.

    For visualization, non-finite values found in passed 'anat_img'
    are set to zero.

    """
    anat_img, black_bg, anat_vmin, anat_vmax = _load_anat(
        anat_img,
        dim=dim, black_bg=black_bg)

    if vmin is None:
        vmin = anat_vmin
    if vmax is None:
        vmax = anat_vmax

    display = plot_img(anat_img, cut_coords=cut_coords,
                       output_file=output_file, display_mode=display_mode,
                       figure=figure, axes=axes, title=title,
                       threshold=threshold, annotate=annotate,
                       draw_cross=draw_cross, black_bg=black_bg,
                       vmin=vmin, vmax=vmax, cmap=cmap, **kwargs)
    return display


def plot_epi(epi_img=None, cut_coords=None, output_file=None,
             display_mode='ortho', figure=None, axes=None, title=None,
             annotate=True, draw_cross=True, black_bg=True,
             cmap=plt.cm.nipy_spectral, vmin=None, vmax=None, **kwargs):
    """Plot cuts of an EPI image (by default 3 cuts:
    Frontal, Axial, and Lateral)

    Parameters
    ----------
    epi_img : a nifti-image like object or a filename, optional
        The EPI (T2*) image.

    cut_coords : None, a tuple of floats, or an integer, optional
        The MNI coordinates of the point where the cut is performed
        If display_mode is 'ortho' or 'tiled', this should be a 3-tuple: (x, y, z)
        For display_mode == 'x', 'y', or 'z', then these are the
        coordinates of each cut in the corresponding direction.
        If None is given, the cuts is calculated automaticaly.
        If display_mode is 'x', 'y' or 'z', cut_coords can be an integer,
        in which case it specifies the number of cuts to perform.
        If display_mode is 'mosaic', and the number of cuts is the same
        for all directions, cut_coords can be specified as an integer.
        It can also be a length 3 tuple specifying the number of cuts for
        every direction if these are different.

    output_file : string, or None, optional
        The name of an image file to export the plot to. Valid extensions
        are .png, .pdf, .svg. If output_file is not None, the plot
        is saved to a file, and the display is closed.

    display_mode : {'ortho', 'tiled', 'mosaic', 'x', 'y', 'z', 'yx', 'xz', 'yz'}, optional
        Choose the direction of the cuts: 'x' - sagittal, 'y' - coronal,
        'z' - axial, 'ortho' - three cuts are performed in orthogonal
        directions, 'tiled' - three cuts are performed
        and arranged in a 2x2 grid. 'mosaic' - three cuts are
        performed along multiple rows and columns. Default='ortho'.

    figure : integer or matplotlib figure, optional
        Matplotlib figure used or its number. If None is given, a
        new figure is created.

    axes : matplotlib axes or 4 tuple of float: (xmin, ymin, width, height), optional
        The axes, or the coordinates, in matplotlib figure space,
        of the axes used to display the plot. If None, the complete figure is used.

    title : string, optional
        The title displayed on the figure.

    annotate : boolean, optional
        If annotate is True, positions and left/right annotation
        are added to the plot. Default=True.

    draw_cross : boolean, optional
        If draw_cross is True, a cross is drawn on the plot to
        indicate the cut plosition. Default=True.

    black_bg : boolean, optional
        If True, the background of the image is set to be black. If
        you wish to save figures with a black background, you
        will need to pass "facecolor='k', edgecolor='k'"
        to matplotlib.pyplot.savefig.
        Default=True.

    cmap : matplotlib colormap, optional
        The colormap for specified image.
        Default=plt.cm.nipy_spectral.

    threshold : a number, None, or 'auto', optional
        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.

    vmin : float, optional
        Lower bound for plotting, passed to matplotlib.pyplot.imshow.

    vmax : float, optional
        Upper bound for plotting, passed to matplotlib.pyplot.imshow.

    Notes
    -----
    Arrays should be passed in numpy convention: (x, y, z) ordered.

    """
    display = plot_img(epi_img, cut_coords=cut_coords,
                       output_file=output_file, display_mode=display_mode,
                       figure=figure, axes=axes, title=title,
                       threshold=None, annotate=annotate,
                       draw_cross=draw_cross, black_bg=black_bg,
                       cmap=cmap, vmin=vmin, vmax=vmax, **kwargs)
    return display


def _plot_roi_contours(display, roi_img, cmap, alpha, linewidths):
    """Helper function for plotting regions of interest ROIs in contours.

    Parameters
    ----------
    display : nilearn.plotting.displays.OrthoSlicer, object
        An object with background image on which contours are shown.

    roi_img : Niimg-like object
        See http://nilearn.github.io/manipulating_images/input_output.html
        The ROI/mask image, it could be binary mask or an atlas or ROIs
        with integer values.

    cmap : matplotlib colormap
        The colormap for the atlas maps.

    alpha : float between 0 and 1
        Alpha sets the transparency of the color inside the filled
        contours.

    linewidths : float
        This option can be used to set the boundary thickness of the
        contours.

    Returns
    -------
    display : nilearn.plotting.displays.OrthoSlicer, object
        Contours displayed on the background image.

    """
    roi_img = _utils.check_niimg_3d(roi_img)
    roi_data = get_data(roi_img)
    labels = np.unique(roi_data)
    cmap = plt.cm.get_cmap(cmap)
    color_list = cmap(np.linspace(0, 1, len(labels)))
    for idx, label in enumerate(labels):
        if label == 0:
            continue
        data = (roi_data == label)
        data = data.astype(int)
        img = new_img_like(roi_img, data, affine=roi_img.affine)
        display.add_contours(img, levels=[0.5], colors=[color_list[idx - 1]],
                             alpha=alpha, linewidths=linewidths,
                             linestyles='solid')
    return display


def plot_roi(roi_img, bg_img=MNI152TEMPLATE, cut_coords=None,
             output_file=None, display_mode='ortho', figure=None, axes=None,
             title=None, annotate=True, draw_cross=True, black_bg='auto',
             threshold=0.5, alpha=0.7, cmap=plt.cm.gist_ncar, dim='auto',
             vmin=None, vmax=None, resampling_interpolation='nearest',
             view_type='continuous', linewidths=2.5, **kwargs):
    """Plot cuts of an ROI/mask image (by default 3 cuts: Frontal, Axial, and
    Lateral)

    Parameters
    ----------
    roi_img : Niimg-like object
        See http://nilearn.github.io/manipulating_images/input_output.html
        The ROI/mask image, it could be binary mask or an atlas or ROIs
        with integer values.

    bg_img : Niimg-like object, optional
        See http://nilearn.github.io/manipulating_images/input_output.html
        The background image that the ROI/mask will be plotted on top of.
        If nothing is specified, the MNI152 template will be used.
        To turn off background image, just pass "bg_img=None".
        Default=MNI152TEMPLATE.

    cut_coords : None, or a tuple of floats, optional
        The MNI coordinates of the point where the cut is performed, in
        MNI coordinates and order.
        If display_mode is 'ortho' or 'tiled', this should be a 3-tuple: (x, y, z)
        For display_mode == 'x', 'y', or 'z', then these are the
        coordinates of each cut in the corresponding direction.
        If None is given, the cuts is calculated automaticaly.
        If display_mode is 'mosaic', and the number of cuts is the same
        for all directions, cut_coords can be specified as an integer.
        It can also be a length 3 tuple specifying the number of cuts for
        every direction if these are different.

    output_file : string, or None, optional
        The name of an image file to export the plot to. Valid extensions
        are .png, .pdf, .svg. If output_file is not None, the plot
        is saved to a file, and the display is closed.

    display_mode : {'ortho', 'mosaic', 'tiled', 'x', 'y', 'z', 'yx', 'xz', 'yz'}, optional
        Choose the direction of the cuts: 'x' - sagittal, 'y' - coronal,
        'z' - axial, 'ortho' - three cuts are performed in orthogonal
        directions, 'tiled' - three cuts are performed
        and arranged in a 2x2 grid, 'mosaic' - three cuts
        are performed along multiple rows and columns, Default='ortho'.

    figure : integer or matplotlib figure, optional
        Matplotlib figure used or its number. If None is given, a
        new figure is created.

    axes : matplotlib axes or 4 tuple of float: (xmin, ymin, width, height), optional
        The axes, or the coordinates, in matplotlib figure space,
        of the axes used to display the plot. If None, the complete
        figure is used.

    title : string, optional
        The title displayed on the figure.

    annotate : boolean, optional
        If annotate is True, positions and left/right annotation
        are added to the plot. Default=True.

    draw_cross : boolean, optional
        If draw_cross is True, a cross is drawn on the plot to
        indicate the cut plosition.

    black_bg : boolean or 'auto', optional
        If True, the background of the image is set to be black. If
        you wish to save figures with a black background, you
        will need to pass "facecolor='k', edgecolor='k'"
        to matplotlib.pyplot.savefig.
        Default='auto'.

    threshold : None, 'auto', or a number, optional
        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.
        Default=0.5.

    alpha : float between 0 and 1, optional
        Alpha sets the transparency of the color inside the filled
        contours. Default=0.7.

    cmap : matplotlib colormap, optional
        The colormap for the atlas maps. Default=plt.cm.gist_ncar.

    dim : float or 'auto', optional
        Dimming factor applied to background image. By default, automatic
        heuristics are applied based upon the background image intensity.
        Accepted float values, where a typical span is between -2 and 2
        (-2 = increase contrast; 2 = decrease contrast), but larger values
        can be used for a more pronounced effect. 0 means no dimming.
        Default='auto'.

    vmin : float, optional
        Lower bound for plotting, passed to matplotlib.pyplot.imshow.

    vmax : float, optional
        Upper bound for plotting, passed to matplotlib.pyplot.imshow.

    resampling_interpolation : str, optional
        Interpolation to use when resampling the image to the destination
        space. Can be "continuous" to use 3rd-order spline interpolation,
        or "nearest" to use nearest-neighbor mapping.
        "nearest" is faster but can be noisier in some cases.
        Default='nearest'.

    view_type : {'continuous', 'contours'}, optional
        By default view_type == 'continuous', rois are shown as continuous colors.
        If view_type == 'contours', maps are shown as contours. For this type, label
        denoted as 0 is considered as background and not shown.
        Default='continuous'.

    linewidths : float, optional
        This option can be used to set the boundary thickness of the
        contours. Only reflects when view_type='contours'.
        Default=2.5.

    Notes
    -----
    A small threshold is applied by default to eliminate numerical
    background noise.

    For visualization, non-finite values found in passed 'roi_img' or
    'bg_img' are set to zero.

    See Also
    --------
    nilearn.plotting.plot_prob_atlas : To simply plot probabilistic atlases
        (4D images)

    """  # noqa: E501
    valid_view_types = ['continuous', 'contours']
    if view_type not in valid_view_types:
        raise ValueError(
            'Unknown view type: %s. Valid view types are %s' %
            (str(view_type), str(valid_view_types))
        )
    elif view_type == 'contours':
        img = roi_img
        roi_img = None

    bg_img, black_bg, bg_vmin, bg_vmax = _load_anat(bg_img, dim=dim,
                                                    black_bg=black_bg)

    display = _plot_img_with_bg(
        img=roi_img, bg_img=bg_img, cut_coords=cut_coords,
        output_file=output_file, display_mode=display_mode,
        figure=figure, axes=axes, title=title, annotate=annotate,
        draw_cross=draw_cross, black_bg=black_bg,
        threshold=threshold, bg_vmin=bg_vmin, bg_vmax=bg_vmax,
        resampling_interpolation=resampling_interpolation,
        alpha=alpha, cmap=cmap, vmin=vmin, vmax=vmax, **kwargs)

    if view_type == 'contours':
        display = _plot_roi_contours(display, img, cmap=cmap, alpha=alpha,
                                     linewidths=linewidths)

    return display


def plot_prob_atlas(maps_img, bg_img=MNI152TEMPLATE, view_type='auto',
                    threshold='auto', linewidths=2.5, cut_coords=None,
                    output_file=None, display_mode='ortho',
                    figure=None, axes=None, title=None, annotate=True,
                    draw_cross=True, black_bg='auto', dim='auto',
                    colorbar=False,
                    cmap=plt.cm.gist_rainbow, vmin=None, vmax=None,
                    alpha=0.7, **kwargs):
    """Plot the probabilistic atlases onto the anatomical image
    by default MNI template

    Parameters
    ----------
    maps_img : Niimg-like object or the filename
        4D image of the probabilistic atlas maps.

    bg_img : Niimg-like object, optional
        See http://nilearn.github.io/manipulating_images/input_output.html
        The anatomical image to be used as a background.
        If nothing is specified, the MNI152 template will be used.
        To turn off background image, just pass "bg_img=False".
        Default=MNI152TEMPLATE.

        .. versionadded:: 0.4.0

    view_type : {'auto', 'contours', 'filled_contours', 'continuous'}, optional
        By default view_type == 'auto', means maps will be displayed
        automatically using any one of the three view types. The automatic
        selection of view type depends on the total number of maps.
        If view_type == 'contours', maps are overlayed as contours
        If view_type == 'filled_contours', maps are overlayed as contours
        along with color fillings inside the contours.
        If view_type == 'continuous', maps are overlayed as continous
        colors irrespective of the number maps.
        Default='auto'.

    threshold : a str or a number, list of str or numbers, optional
        This parameter is optional and is used to threshold the maps image
        using the given value or automatically selected value. The values
        in the image above the threshold level will be visualized.
        The default strategy, computes a threshold level that seeks to
        minimize (yet not eliminate completely) the overlap between several
        maps for a better visualization.
        The threshold can also be expressed as a percentile over the values
        of the whole atlas. In that case, the value must be specified as
        string finishing with a percent sign, e.g., "25.3%".
        If a single string is provided, the same percentile will be applied
        over the whole atlas. Otherwise, if a list of percentiles is
        provided, each 3D map is thresholded with certain percentile
        sequentially. Length of percentiles given should match the number
        of 3D map in time (4th) dimension.
        If a number or a list of numbers, the given value will be used
        directly to threshold the maps without any percentile calculation.
        If None, a very small threshold is applied to remove numerical
        noise from the maps background.

    linewidths : float, optional
        This option can be used to set the boundary thickness of the contours.
        Default=2.5.

    cut_coords : None, a tuple of floats, or an integer, optional
        The MNI coordinates of the point where the cut is performed
        If display_mode is 'ortho' or 'tiled', this should be a 3-tuple: (x, y, z)
        For display_mode == 'x', 'y', or 'z', then these are the
        coordinates of each cut in the corresponding direction.
        If None is given, the cuts is calculated automaticaly.
        If display_mode is 'x', 'y' or 'z', cut_coords can be an integer,
        in which case it specifies the number of cuts to perform.
        If display_mode is 'mosaic', and the number of cuts is the same
        for all directions, cut_coords can be specified as an integer.
        It can also be a length 3 tuple specifying the number of cuts for
        every direction if these are different.

    output_file : string, or None, optional
        The name of an image file to export the plot to. Valid extensions
        are .png, .pdf, .svg. If output_file is not None, the plot
        is saved to a file, and the display is closed.

    display_mode : {'ortho', 'tiled', 'mosaic', 'x', 'y', 'z', 'yx', 'xz', 'yz'}, optional
        Choose the direction of the cuts: 'x' - sagittal, 'y' - coronal,
        'z' - axial, 'ortho' - three cuts are performed in orthogonal
        directions, 'tiled' - three cuts are performed
        and arranged in a 2x2 grid, 'mosaic' - three cuts are performed along
        multiple rows and columns, Default='ortho'.

    figure : integer or matplotlib figure, optional
        Matplotlib figure used or its number. If None is given, a
        new figure is created.

    axes : matplotlib axes or 4 tuple of float: (xmin, ymin, width, height), optional
        The axes, or the coordinates, in matplotlib figure space,
        of the axes used to display the plot. If None, the complete figure is used.

    title : string, optional
        The title displayed on the figure.

    annotate : boolean, optional
        If annotate is True, positions and left/right annotation
        are added to the plot. Default=True.

    draw_cross : boolean, optional
        If draw_cross is True, a cross is drawn on the plot to
        indicate the cut plosition. Default=True.

    black_bg : boolean or 'auto', optional
        If True, the background of the image is set to be black. If
        you wish to save figures with a black background, you
        will need to pass "facecolor='k', edgecolor='k'" to pylab's
        savefig. Default='auto'.

    dim : float or 'auto', optional
        Dimming factor applied to background image. By default, automatic
        heuristics are applied based upon the background image intensity.
        Accepted float values, where a typical span is between -2 and 2
        (-2 = increase contrast; 2 = decrease contrast), but larger values
        can be used for a more pronounced effect. 0 means no dimming.
        Default='auto'.

    cmap : matplotlib colormap, optional
        The colormap for the atlas maps. Default=plt.cm.gist_rainbow.

    colorbar : boolean, optional
        If True, display a colorbar on the right of the plots.
        Default=False.

    vmin : float, optional
        Lower bound for plotting, passed to matplotlib.pyplot.imshow.

    vmax : float, optional
        Upper bound for plotting, passed to matplotlib.pyplot.imshow.

    alpha : float between 0 and 1, optional
        Alpha sets the transparency of the color inside the filled contours.
        Default=0.7.

    See Also
    --------
    nilearn.plotting.plot_roi : To simply plot max-prob atlases (3D images)

    """
    display = plot_anat(bg_img, cut_coords=cut_coords,
                        display_mode=display_mode,
                        figure=figure, axes=axes, title=title,
                        annotate=annotate, draw_cross=draw_cross,
                        black_bg=black_bg, dim=dim, **kwargs)

    maps_img = _utils.check_niimg_4d(maps_img)
    n_maps = maps_img.shape[3]

    valid_view_types = ['auto', 'contours', 'filled_contours', 'continuous']
    if view_type not in valid_view_types:
        raise ValueError(
            'Unknown view type: %s. Valid view types are %s' %
            (str(view_type), str(valid_view_types))
        )

    cmap = plt.cm.get_cmap(cmap)
    color_list = cmap(np.linspace(0, 1, n_maps))

    if view_type == 'auto':
        if n_maps > 20:
            view_type = 'contours'
        elif n_maps > 10:
            view_type = 'filled_contours'
        else:
            view_type = 'continuous'

    if threshold is None:
        threshold = 1e-6
    elif threshold == 'auto':
        # it will use default percentage,
        # strategy is to avoid maximum overlaps as possible
        if view_type == 'contours':
            correction_factor = 1
        elif view_type == 'filled_contours':
            correction_factor = .8
        else:
            correction_factor = .5
        threshold = "%f%%" % (100 * (1 - .2 * correction_factor / n_maps))

    if (isinstance(threshold, collections.abc.Iterable) and
            not isinstance(threshold, str)):
        threshold = [thr for thr in threshold]
        if len(threshold) != n_maps:
            raise TypeError('The list of values to threshold '
                            'should be equal to number of maps')
    else:
        threshold = [threshold] * n_maps

    filled = view_type.startswith('filled')
    for (map_img, color, thr) in zip(iter_img(maps_img), color_list,
                                     threshold):
        data = get_data(map_img)
        # To threshold or choose the level of the contours
        thr = check_threshold(thr, data,
                              percentile_func=fast_abs_percentile,
                              name='threshold')
        # Get rid of background values in all cases
        thr = max(thr, 1e-6)

        if view_type == 'continuous':
            display.add_overlay(map_img, threshold=thr,
                                cmap=cm.alpha_cmap(color), alpha=alpha)
        else:
            display.add_contours(map_img, levels=[thr],
                                 linewidths=linewidths,
                                 colors=[color], filled=filled,
                                 alpha=alpha, linestyles='solid', )
    if colorbar:
        display._colorbar = True
        # Create a colormap from color list to feed display
        cmap = matplotlib.colors.LinearSegmentedColormap.from_list(
            'segmented colors', color_list, n_maps + 1)
        display._show_colorbar(cmap, matplotlib.colors.Normalize(1,
                                                                 n_maps + 1))
        tick_locator = matplotlib.ticker.MaxNLocator(nbins=10)
        display.locator = tick_locator
        display._cbar.update_ticks()
        tick_location = np.round(np.linspace(1,
                                             n_maps,
                                             min(n_maps, 10))).astype('int')
        display._cbar.set_ticks(tick_location + .5)
        display._cbar.set_ticklabels(tick_location)
        left, bottom, width, height = display._colorbar_ax.\
            get_position().bounds
        display._colorbar_ax.set_position([left, bottom, width, height * 0.95])
        display._colorbar_ax.annotate('Map #', xy=(1, 1.03), ha='right',
                                      va='bottom',
                                      xycoords='axes fraction')
    if output_file is not None:
        display.savefig(output_file)
        display.close()
        display = None

    return display


def plot_stat_map(stat_map_img, bg_img=MNI152TEMPLATE, cut_coords=None,
                  output_file=None, display_mode='ortho', colorbar=True,
                  figure=None, axes=None, title=None, threshold=1e-6,
                  annotate=True, draw_cross=True, black_bg='auto',
                  cmap=cm.cold_hot, symmetric_cbar="auto",
                  dim='auto', vmax=None, resampling_interpolation='continuous',
                  **kwargs):
    """Plot cuts of an ROI/mask image (by default 3 cuts: Frontal, Axial, and
    Lateral)

    Parameters
    ----------
    stat_map_img : Niimg-like object
        See http://nilearn.github.io/manipulating_images/input_output.html
        The statistical map image

    bg_img : Niimg-like object, optional
        See http://nilearn.github.io/manipulating_images/input_output.html
        The background image that the ROI/mask will be plotted on top of.
        If nothing is specified, the MNI152 template will be used.
        To turn off background image, just pass "bg_img=None".
        Default=MNI152TEMPLATE.

    cut_coords : None, a tuple of floats, or an integer, optional
        The MNI coordinates of the point where the cut is performed
        If display_mode is 'ortho' or 'tiled', this should be a 3-tuple: (x, y, z)
        For display_mode == 'x', 'y', or 'z', then these are the
        coordinates of each cut in the corresponding direction.
        If None is given, the cuts is calculated automaticaly.
        If display_mode is 'x', 'y' or 'z', cut_coords can be an integer,
        in which case it specifies the number of cuts to perform.
        If display_mode is 'mosaic', and the number of cuts is the same
        for all directions, cut_coords can be specified as an integer.
        It can also be a length 3 tuple specifying the number of cuts for
        every direction if these are different.

    output_file : string, or None, optional
        The name of an image file to export the plot to. Valid extensions
        are .png, .pdf, .svg. If output_file is not None, the plot
        is saved to a file, and the display is closed.

    display_mode : {'ortho', 'tiled', 'mosaic', 'x', 'y', 'z', 'yx', 'xz', 'yz'}, optional
        Choose the direction of the cuts: 'x' - sagittal, 'y' - coronal,
        'z' - axial, 'ortho' - three cuts are performed in orthogonal
        directions, 'tiled' - three cuts are performed
        and arranged in a 2x2 grid, 'mosaic' - three cuts
        are performed along multiple rows and columns, Default='ortho'.

    colorbar : boolean, optional
        If True, display a colorbar on the right of the plots. Default=True.

    figure : integer or matplotlib figure, optional
        Matplotlib figure used or its number. If None is given, a
        new figure is created.

    axes : matplotlib axes or 4 tuple of float: (xmin, ymin, width, height), optional
        The axes, or the coordinates, in matplotlib figure space,
        of the axes used to display the plot. If None, the complete figure is used.

    title : string, optional
        The title displayed on the figure.

    threshold : a number, None, or 'auto', optional
        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.
        Default=1e-6.

    annotate : boolean, optional
        If annotate is True, positions and left/right annotation
        are added to the plot. Default=True.

    draw_cross : boolean, optional
        If draw_cross is True, a cross is drawn on the plot to
        indicate the cut plosition. Default=True.

    black_bg : boolean or 'auto', optional
        If True, the background of the image is set to be black. If
        you wish to save figures with a black background, you
        will need to pass "facecolor='k', edgecolor='k'"
        to matplotlib.pyplot.savefig.
        Default='auto'.

    cmap : matplotlib colormap, optional
        The colormap for specified image. The ccolormap *must* be symmetrical.
        Default=plt.cm.cold_hot.

    symmetric_cbar : boolean or 'auto', optional
        Specifies whether the colorbar should range from -vmax to vmax
        or from vmin to vmax. Setting to 'auto' will select the latter if
        the range of the whole image is either positive or negative.
        Note: The colormap will always be set to range from -vmax to vmax.
        Default='auto'.

    dim : float or 'auto', optional
        Dimming factor applied to background image. By default, automatic
        heuristics are applied based upon the background image intensity.
        Accepted float values, where a typical scan is between -2 and 2
        (-2 = increase constrast; 2 = decrease contrast), but larger values
        can be used for a more pronounced effect. 0 means no dimming.
        Default='auto'.

    vmax : float, optional
        Upper bound for plotting, passed to matplotlib.pyplot.imshow.

    resampling_interpolation : str, optional
        Interpolation to use when resampling the image to the destination
        space. Can be "continuous" to use 3rd-order spline interpolation,
        or "nearest" to use nearest-neighbor mapping.
        "nearest" is faster but can be noisier in some cases.
        Default='continuous'.

    Notes
    -----
    Arrays should be passed in numpy convention: (x, y, z) ordered.

    For visualization, non-finite values found in passed 'stat_map_img' or
    'bg_img' are set to zero.

    See Also
    --------
    nilearn.plotting.plot_anat : To simply plot anatomical images
    nilearn.plotting.plot_epi : To simply plot raw EPI images
    nilearn.plotting.plot_glass_brain : To plot maps in a glass brain

    """  # noqa: E501
    # dim the background
    bg_img, black_bg, bg_vmin, bg_vmax = _load_anat(bg_img, dim=dim,
                                                    black_bg=black_bg)

    stat_map_img = _utils.check_niimg_3d(stat_map_img, dtype='auto')

    cbar_vmin, cbar_vmax, vmin, vmax = _get_colorbar_and_data_ranges(
        _safe_get_data(stat_map_img, ensure_finite=True),
        vmax,
        symmetric_cbar,
        kwargs)

    display = _plot_img_with_bg(
        img=stat_map_img, bg_img=bg_img, cut_coords=cut_coords,
        output_file=output_file, display_mode=display_mode,
        figure=figure, axes=axes, title=title, annotate=annotate,
        draw_cross=draw_cross, black_bg=black_bg, threshold=threshold,
        bg_vmin=bg_vmin, bg_vmax=bg_vmax, cmap=cmap, vmin=vmin, vmax=vmax,
        colorbar=colorbar, cbar_vmin=cbar_vmin, cbar_vmax=cbar_vmax,
        resampling_interpolation=resampling_interpolation, **kwargs)

    return display


def plot_glass_brain(stat_map_img,
                     output_file=None, display_mode='ortho', colorbar=False,
                     figure=None, axes=None, title=None, threshold='auto',
                     annotate=True,
                     black_bg=False,
                     cmap=None,
                     alpha=0.7,
                     vmin=None, vmax=None,
                     plot_abs=True,
                     symmetric_cbar="auto",
                     resampling_interpolation='continuous',
                     **kwargs):
    """Plot 2d projections of an ROI/mask image (by default 3 projections:
    Frontal, Axial, and Lateral). The brain glass schematics
    are added on top of the image.

    The plotted image should be in MNI space for this function to work
    properly.

    Only glass brain can be plotted by switching stat_map_img to None.

    Parameters
    ----------
    stat_map_img : Niimg-like object
        See http://nilearn.github.io/manipulating_images/input_output.html
        The statistical map image. It needs to be in MNI space
        in order to align with the brain schematics.

    output_file : string, or None, optional
        The name of an image file to export the plot to. Valid extensions
        are .png, .pdf, .svg. If output_file is not None, the plot
        is saved to a file, and the display is closed.

    display_mode : string, optional
        Choose the direction of the cuts: 'x' - sagittal, 'y' - coronal,
        'z' - axial, 'l' - sagittal left hemisphere only,
        'r' - sagittal right hemisphere only, 'ortho' - three cuts are
        performed in orthogonal directions. Possible values are: 'ortho',
        'x', 'y', 'z', 'xz', 'yx', 'yz', 'l', 'r', 'lr', 'lzr', 'lyr',
        'lzry', 'lyrz'. Default='ortho'.

    colorbar : boolean, optional
        If True, display a colorbar on the right of the plots.
        Default=False.

    figure : integer or matplotlib figure, optional
        Matplotlib figure used or its number. If None is given, a
        new figure is created.

    axes : matplotlib axes or 4 tuple of float: (xmin, ymin, width, height), optional
        The axes, or the coordinates, in matplotlib figure space,
        of the axes used to display the plot. If None, the complete figure is used.

    title : string, optional
        The title displayed on the figure.

    threshold : a number or 'auto', optional
            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.
            Default='auto'.

    annotate : boolean, optional
        If annotate is True, positions and left/right annotation
        are added to the plot. Default=True.

    black_bg : boolean, optional
        If True, the background of the image is set to be black. If
        you wish to save figures with a black background, you
        will need to pass "facecolor='k', edgecolor='k'"
        to matplotlib.pyplot.savefig. Default=False.

    cmap : matplotlib colormap, optional
        The colormap for specified image

    alpha : float between 0 and 1, optional
        Alpha transparency for the brain schematics. Default=0.7.

    vmin : float, optional
        Lower bound for plotting, passed to matplotlib.pyplot.imshow.

    vmax : float, optional
        Upper bound for plotting, passed to matplotlib.pyplot.imshow.

    plot_abs : boolean, optional
        If set to True (default) maximum intensity projection of the
        absolute value will be used (rendering positive and negative
        values in the same manner). If set to false the sign of the
        maximum intensity will be represented with different colors.
        See http://nilearn.github.io/auto_examples/01_plotting/plot_demo_glass_brain_extensive.html
        for examples. Default=True.

    symmetric_cbar : boolean or 'auto', optional
        Specifies whether the colorbar should range from -vmax to vmax
        or from vmin to vmax. Setting to 'auto' will select the latter if
        the range of the whole image is either positive or negative.
        Note: The colormap will always be set to range from -vmax to vmax.
        Default='auto'.

    resampling_interpolation : str, optional
        Interpolation to use when resampling the image to the destination
        space. Can be "continuous" (default) to use 3rd-order spline
        interpolation, or "nearest" to use nearest-neighbor mapping.
        "nearest" is faster but can be noisier in some cases.
        Default='continuous'.

    Notes
    -----
    Arrays should be passed in numpy convention: (x, y, z) ordered.

    """
    if cmap is None:
        cmap = cm.cold_hot if black_bg else cm.cold_white_hot

    if stat_map_img:
        stat_map_img = _utils.check_niimg_3d(stat_map_img, dtype='auto')
        if plot_abs:
            cbar_vmin, cbar_vmax, vmin, vmax = _get_colorbar_and_data_ranges(
                _safe_get_data(stat_map_img, ensure_finite=True),
                vmax,
                symmetric_cbar,
                kwargs,
                0)
        else:
            cbar_vmin, cbar_vmax, vmin, vmax = _get_colorbar_and_data_ranges(
                _safe_get_data(stat_map_img, ensure_finite=True),
                vmax,
                symmetric_cbar,
                kwargs)
    else:
        cbar_vmin, cbar_vmax = None, None

    def display_factory(display_mode):
        return functools.partial(get_projector(display_mode),
                                 alpha=alpha, plot_abs=plot_abs)

    display = _plot_img_with_bg(
        img=stat_map_img, output_file=output_file, display_mode=display_mode,
        figure=figure, axes=axes, title=title, annotate=annotate,
        black_bg=black_bg, threshold=threshold, cmap=cmap, colorbar=colorbar,
        display_factory=display_factory, vmin=vmin, vmax=vmax,
        cbar_vmin=cbar_vmin, cbar_vmax=cbar_vmax,
        resampling_interpolation=resampling_interpolation, **kwargs)

    if stat_map_img is None and 'l' in display.axes:
        display.axes['l'].ax.invert_xaxis()

    return display


def plot_connectome(adjacency_matrix, node_coords,
                    node_color='auto', node_size=50,
                    edge_cmap=cm.bwr,
                    edge_vmin=None, edge_vmax=None,
                    edge_threshold=None,
                    output_file=None, display_mode='ortho',
                    figure=None, axes=None, title=None,
                    annotate=True, black_bg=False,
                    alpha=0.7,
                    edge_kwargs=None, node_kwargs=None,
                    colorbar=False):
    """Plot connectome on top of the brain glass schematics.

    The plotted image should be in MNI space for this function to work
    properly.

    In the case of 'l' and 'r' directions (for hemispheric projections),
    markers in the coordinate x == 0 are included in both hemispheres.

    Parameters
    ----------
    adjacency_matrix : numpy array of shape (n, n)
        Represents the link strengths of the graph. The matrix can be
        symmetric which will result in an undirected graph, or not
        symmetric which will result in a directed graph.

    node_coords : numpy array_like of shape (n, 3)
        3d coordinates of the graph nodes in world space.

    node_color : color or sequence of colors or 'auto', optional
        Color(s) of the nodes. If string is given, all nodes
        are plotted with same color given in string.

    node_size : scalar or array_like, optional
        Size(s) of the nodes in points^2. Default=50.

    edge_cmap : colormap, optional
        Colormap used for representing the strength of the edges.
        Default=cm.bwr.

    edge_vmin, edge_vmax : float, optional
        If not None, either or both of these values will be used to
        as the minimum and maximum values to color edges. If None are
        supplied the maximum absolute value within the given threshold
        will be used as minimum (multiplied by -1) and maximum
        coloring levels.

    edge_threshold : str or number, optional
        If it is a number only the edges with a value greater than
        edge_threshold will be shown.
        If it is a string it must finish with a percent sign,
        e.g. "25.3%", and only the edges with a abs(value) above
        the given percentile will be shown.

    output_file : string, or None, optional
        The name of an image file to export the plot to. Valid extensions
        are .png, .pdf, .svg. If output_file is not None, the plot
        is saved to a file, and the display is closed.

    display_mode : string, optional
        Choose the direction of the cuts: 'x' - sagittal, 'y' - coronal,
        'z' - axial, 'l' - sagittal left hemisphere only,
        'r' - sagittal right hemisphere only, 'ortho' - three cuts are
        performed in orthogonal directions. Possible values are: 'ortho',
        'x', 'y', 'z', 'xz', 'yx', 'yz', 'l', 'r', 'lr', 'lzr', 'lyr',
        'lzry', 'lyrz'. Default='ortho'.

    figure : integer or matplotlib figure, optional
        Matplotlib figure used or its number.
        If None is given, a new figure is created.

    axes : matplotlib axes or 4 tuple of float: (xmin, ymin, width, height), optional
        The axes, or the coordinates, in matplotlib figure space,
        of the axes used to display the plot. If None, the complete figure is used.

    title : string, optional
        The title displayed on the figure.

    annotate : boolean, optional
        If annotate is True, positions and left/right annotation are
        added to the plot. Default=True.

    black_bg : boolean, optional
        If True, the background of the image is set to be black. If
        you wish to save figures with a black background, you
        will need to pass "facecolor='k', edgecolor='k'"
        to matplotlib.pyplot.savefig. Default=False.

    alpha : float between 0 and 1, optional
        Alpha transparency for the brain schematics. Default=0.7.

    edge_kwargs : dict, optional
        Will be passed as kwargs for each edge matlotlib Line2D.

    node_kwargs : dict, optional
        Will be passed as kwargs to the plt.scatter call that plots all
        the nodes in one go.

    colorbar : bool, optional
        If True, display a colorbar on the right of the plots.
        Default=False.

    See Also
    ---------
    nilearn.plotting.find_parcellation_cut_coords : Extraction of node
        coords on brain parcellations.
    nilearn.plotting.find_probabilistic_atlas_cut_coords : Extraction of
        node coords on brain probabilisitic atlases.

    """
    display = plot_glass_brain(None,
                               display_mode=display_mode,
                               figure=figure, axes=axes, title=title,
                               annotate=annotate,
                               black_bg=black_bg)

    display.add_graph(adjacency_matrix, node_coords,
                      node_color=node_color, node_size=node_size,
                      edge_cmap=edge_cmap,
                      edge_vmin=edge_vmin, edge_vmax=edge_vmax,
                      edge_threshold=edge_threshold,
                      edge_kwargs=edge_kwargs, node_kwargs=node_kwargs,
                      colorbar=colorbar)

    if output_file is not None:
        display.savefig(output_file)
        display.close()
        display = None

    return display


def plot_connectome_strength(adjacency_matrix, node_coords, node_size="auto",
                             cmap=None, output_file=None, display_mode="ortho",
                             figure=None, axes=None, title=None):
    """Plot connectome strength on top of the brain glass schematics.

    The strength of a connection is define as the sum of absolute values of
    the edges arriving to a node.

    Parameters
    ----------
    adjacency_matrix : numpy array of shape (n, n)
        Represents the link strengths of the graph. Assumed to be
        a symmetric matrix.

    node_coords : numpy array_like of shape (n, 3)
        3d coordinates of the graph nodes in world space.

    node_size : 'auto' or scalar, optional
        Size(s) of the nodes in points^2. By default the size of the node is
        inversely propertionnal to the number of nodes.
        Default='auto'.

    cmap : str or colormap, optional
        Colormap used to represent the strength of a node.

    output_file : string, or None, optional
        The name of an image file to export the plot to. Valid extensions
        are .png, .pdf, .svg. If output_file is not None, the plot
        is saved to a file, and the display is closed.

    display_mode : string, optional
        Choose the direction of the cuts: 'x' - sagittal, 'y' - coronal,
        'z' - axial, 'l' - sagittal left hemisphere only,
        'r' - sagittal right hemisphere only, 'ortho' - three cuts are
        performed in orthogonal directions. Possible values are: 'ortho',
        'x', 'y', 'z', 'xz', 'yx', 'yz', 'l', 'r', 'lr', 'lzr', 'lyr',
        'lzry', 'lyrz'. Default='ortho'.

    figure : integer or matplotlib figure, optional
        Matplotlib figure used or its number. If None is given, a
        new figure is created.

    axes : matplotlib axes or 4 tuple of float: (xmin, ymin, width, height), \
optional
        The axes, or the coordinates, in matplotlib figure space,
        of the axes used to display the plot. If None, the complete
        figure is used.

    title : string, optional
        The title displayed on the figure.

    Notes
    -----
    The plotted image should in MNI space for this function to work properly.

    This function is deprecated and will be removed in the 0.9.0 release. Use
    plot_markers instead.

    """
    dep_msg = ("This function is deprecated and will be "
               "removed in the 0.9.0 release. Use plot_markers instead.")
    warnings.warn(dep_msg, DeprecationWarning)

    # input validation
    if cmap is None:
        cmap = plt.cm.viridis_r
    elif isinstance(cmap, str):
        cmap = plt.get_cmap(cmap)
    else:
        cmap = cmap

    node_size = (1 / len(node_coords) * 1e4
                 if node_size == 'auto' else node_size)

    node_coords = np.asarray(node_coords)

    if sparse.issparse(adjacency_matrix):
        adjacency_matrix = adjacency_matrix.toarray()

    adjacency_matrix = np.nan_to_num(adjacency_matrix)

    adjacency_matrix_shape = adjacency_matrix.shape
    if (len(adjacency_matrix_shape) != 2 or  # noqa: W504
            adjacency_matrix_shape[0] != adjacency_matrix_shape[1]):
        raise ValueError(
            "'adjacency_matrix' is supposed to have shape (n, n)."
            ' Its shape was {0}'.format(adjacency_matrix_shape))

    node_coords_shape = node_coords.shape
    if len(node_coords_shape) != 2 or node_coords_shape[1] != 3:
        message = (
            "Invalid shape for 'node_coords'. You passed an "
            "'adjacency_matrix' of shape {0} therefore "
            "'node_coords' should be a array with shape ({0[0]}, 3) "
            "while its shape was {1}").format(adjacency_matrix_shape,
                                              node_coords_shape)

        raise ValueError(message)

    if node_coords_shape[0] != adjacency_matrix_shape[0]:
        raise ValueError(
            "Shape mismatch between 'adjacency_matrix' "
            "and 'node_coords'"
            "'adjacency_matrix' shape is {0}, 'node_coords' shape is {1}"
            .format(adjacency_matrix_shape, node_coords_shape))

    if not np.allclose(adjacency_matrix, adjacency_matrix.T, rtol=1e-3):
        raise ValueError("'adjacency_matrix' should be symmetric")

    # For a masked array, masked values are replaced with zeros
    if hasattr(adjacency_matrix, 'mask'):
        if not (adjacency_matrix.mask == adjacency_matrix.mask.T).all():
            raise ValueError(
                "'adjacency_matrix' was masked with a non symmetric mask")
        adjacency_matrix = adjacency_matrix.filled(0)

    # plotting
    region_strength = np.sum(np.abs(adjacency_matrix), axis=0)
    region_strength /= np.sum(region_strength)

    region_idx_sorted = np.argsort(region_strength)[::-1]
    strength_sorted = region_strength[region_idx_sorted]
    coords_sorted = node_coords[region_idx_sorted]

    display = plot_glass_brain(
        None, display_mode=display_mode, figure=figure, axes=axes, title=title
    )

    for coord, region in zip(coords_sorted, strength_sorted):
        color = list(
            cmap((region - strength_sorted.min()) / strength_sorted.max())
        )
        # reduce alpha for the least strong regions
        color[-1] = (
            (region - strength_sorted.min()) *  # noqa: W504
            (1 / (strength_sorted.max() - strength_sorted.min()))
        )
        # make color to be a 2D array
        color = [color]
        display.add_markers(
            [coord], marker_color=color, marker_size=node_size
        )

    if output_file is not None:
        display.savefig(output_file)
        display.close()
        display = None

    return display


def plot_markers(node_values, node_coords, node_size='auto',
                 node_cmap=plt.cm.viridis_r, node_vmin=None, node_vmax=None,
                 node_threshold=None, alpha=0.7, output_file=None,
                 display_mode="ortho", figure=None, axes=None, title=None,
                 annotate=True, black_bg=False, node_kwargs=None,
                 colorbar=True):
    """Plot network nodes (markers) on top of the brain glass schematics.

    Nodes are color coded according to provided nodal measure. Nodal measure
    usually represents some notion of node importance.

    Parameters
    ----------
    node_values : array_like of length n
        Vector containing nodal importance measure. Each node will be colored
        acording to corresponding node value.

    node_coords : numpy array_like of shape (n, 3)
        3d coordinates of the graph nodes in world space.

    node_size : 'auto' or scalar or array-like, optional
        Size(s) of the nodes in points^2. By default the size of the node is
        inversely proportional to the number of nodes.

    node_cmap : str or colormap, optional
        Colormap used to represent the node measure. Default=plt.cm.viridis_r.

    node_vmin : float, optional
        Lower bound of the colormap. If `None`, the min of the node_values is
        used.

    node_vmax : float, optional
        Upper bound of the colormap. If `None`, the min of the node_values is
        used.

    node_threshold : float
        If provided only the nodes with a value greater than node_threshold
        will be shown.

    alpha : float between 0 and 1, optional
        Alpha transparency for markers. Default=0.7.

    output_file : string, or None, optional
        The name of an image file to export the plot to. Valid extensions
        are .png, .pdf, .svg. If output_file is not None, the plot
        is saved to a file, and the display is closed.

    display_mode : string, optional
        Choose the direction of the cuts: 'x' - sagittal, 'y' - coronal,
        'z' - axial, 'l' - sagittal left hemisphere only,
        'r' - sagittal right hemisphere only, 'ortho' - three cuts are
        performed in orthogonal directions. Possible values are: 'ortho',
        'x', 'y', 'z', 'xz', 'yx', 'yz', 'l', 'r', 'lr', 'lzr', 'lyr',
        'lzry', 'lyrz'. Default='ortho'.

    figure : integer or matplotlib figure, optional
        Matplotlib figure used or its number. If None is given, a
        new figure is created.

    axes : matplotlib axes or 4 tuple of float: (xmin, ymin, width, height), optional
        The axes, or the coordinates, in matplotlib figure space,
        of the axes used to display the plot. If None, the complete
        figure is used.

    title : string, optional
        The title displayed on the figure.

    annotate : boolean, optional
        If annotate is True, positions and left/right annotation
        are added to the plot. Default=True.

    black_bg : boolean, optional
        If True, the background of the image is set to be black. If
        you wish to save figures with a black background, you
        will need to pass "facecolor='k', edgecolor='k'"
        to matplotlib.pyplot.savefig. Default=False.

    node_kwargs : dict, optional
        will be passed as kwargs to the plt.scatter call that plots all
        the nodes in one go

    colorbar : boolean, optional
        If True, display a colorbar on the right of the plots.
        Default=True.

    """
    node_values = np.squeeze(np.array(node_values))
    node_coords = np.array(node_coords)

    # Validate node_values
    if node_values.shape != (node_coords.shape[0], ):
        msg = ("Dimension mismatch: 'node_values' should be vector of length "
               "{0}, but current shape is {1} instead of {2}").format(
                   len(node_coords),
                   node_values.shape,
                   (node_coords.shape[0], ))
        raise ValueError(msg)

    display = plot_glass_brain(None, display_mode=display_mode,
                               figure=figure, axes=axes, title=title,
                               annotate=annotate, black_bg=black_bg)

    if isinstance(node_size, str) and node_size == 'auto':
        node_size = min(1e4 / len(node_coords), 100)

    # Filter out nodes with node values below threshold
    if node_threshold is not None:
        if node_threshold > np.max(node_values):
            msg = ("Provided 'node_threshold' value: {0} should not exceed "
                   "highest node value: {1}").format(node_threshold,
                                                     np.max(node_values))
            raise ValueError(msg)

        retained_nodes = node_values > node_threshold
        node_values = node_values[retained_nodes]
        node_coords = node_coords[retained_nodes]
        if isinstance(node_size, collections.abc.Iterable):
            node_size = [size for ok_retain, size in
                         zip(retained_nodes, node_size) if ok_retain]

    # Calculate node colors based on value
    node_vmin = np.min(node_values) if node_vmin is None else node_vmin
    node_vmax = np.max(node_values) if node_vmax is None else node_vmax
    if node_vmin == node_vmax:
        node_vmin = 0.9 * node_vmin
        node_vmax = 1.1 * node_vmax
    norm = matplotlib.colors.Normalize(vmin=node_vmin, vmax=node_vmax)
    node_cmap = (plt.get_cmap(node_cmap) if isinstance(node_cmap, str)
                 else node_cmap)
    node_color = [node_cmap(norm(node_value)) for node_value in node_values]

    # Prepare additional parameters for plt.scatter
    node_kwargs = {} if node_kwargs is None else node_kwargs
    node_kwargs.update([('alpha', alpha)])

    display.add_markers(
        marker_coords=node_coords,
        marker_color=node_color,
        marker_size=node_size,
        **node_kwargs
    )

    if colorbar:
        display._colorbar = True
        display._show_colorbar(cmap=node_cmap, norm=norm)

    if output_file is not None:
        display.savefig(output_file)
        display.close()
        display = None

    return display


def plot_carpet(img, mask_img=None, mask_labels=None,
                detrend=True, output_file=None,
                figure=None, axes=None, vmin=None, vmax=None, title=None,
                cmap=plt.cm.gist_ncar):
    """Plot an image representation of voxel intensities across time.

    This figure is also known as a "grayplot" or "Power plot".

    Parameters
    ----------
    img : Niimg-like object
        4D image.
        See http://nilearn.github.io/manipulating_images/input_output.html.

    mask_img : Niimg-like object or None, optional
        Limit plotted voxels to those inside the provided mask (default is
        None). If a 3D atlas is provided, voxels will be grouped by atlas
        value and a colorbar will be added to the left side of the figure
        with atlas labels.
        If not specified, a new mask will be derived from data.
        See http://nilearn.github.io/manipulating_images/input_output.html.

    mask_labels : :obj:`dict`, optional
        If ``mask_img`` corresponds to an atlas, then this dictionary maps
        values from the ``mask_img`` to labels. Dictionary keys are labels
        and values are values within the atlas.

    detrend : :obj:`bool`, optional
        Detrend and z-score the data prior to plotting. Default=True.

    output_file : :obj:`str` or None, optional
        The name of an image file to which to export the plot (default is
        None). Valid extensions are .png, .pdf, and .svg.
        If `output_file` is not None, the plot is saved to a file, and the
        display is closed.

    figure : :class:`matplotlib.figure.Figure` or None, optional
        Matplotlib figure used (default is None).
        If None is given, a new figure is created.

    axes : matplotlib axes or None, optional
        The axes used to display the plot (default is None).
        If None, the complete figure is used.

    vmin : float or None, optional
        Lower bound for plotting, passed to matplotlib.pyplot.imshow.
        If None, vmin will be automatically determined based on the data.
        Default=None.

    vmax : float or None, optional
        Upper bound for plotting, passed to matplotlib.pyplot.imshow.
        If None, vmax will be automatically determined based on the data.
        Default=None.

    title : :obj:`str` or None, optional
        The title displayed on the figure (default is None).

    cmap : matplotlib colormap, optional
        The colormap for the sidebar, if an atlas is used.
        Default=plt.cm.gist_ncar.

    Returns
    -------
    figure : :class:`matplotlib.figure.Figure`
        Figure object with carpet plot.

    Notes
    -----
    This figure was originally developed in :footcite:`POWER2017150`.

    In cases of long acquisitions (>800 volumes), the data will be downsampled
    to have fewer than 800 volumes before being plotted.

    References
    ----------
    .. footbibliography::

    """
    img = _utils.check_niimg_4d(img, dtype='auto')

    # Define TR and number of frames
    tr = img.header.get_zooms()[-1]
    n_tsteps = img.shape[-1]

    if mask_img is None:
        mask_img = compute_epi_mask(img)
    else:
        mask_img = _utils.check_niimg_3d(mask_img, dtype='auto')

    is_atlas = len(np.unique(mask_img.get_fdata())) > 2
    if is_atlas:
        background_label = 0

        atlas_img_res = resample_to_img(
            mask_img,
            img,
            interpolation='nearest',
        )
        atlas_bin = math_img(
            'img != {}'.format(background_label),
            img=atlas_img_res,
        )
        masker = NiftiMasker(atlas_bin, target_affine=img.affine)

        data = masker.fit_transform(img)
        atlas_values = masker.transform(atlas_img_res)
        atlas_values = np.squeeze(atlas_values)

        if mask_labels:
            label_dtype = type(list(mask_labels.values())[0])
            if label_dtype != atlas_values.dtype:
                print('Coercing atlas_values to {}'.format(label_dtype))
                atlas_values = atlas_values.astype(label_dtype)

        # Sort data and atlas by atlas values
        order = np.argsort(atlas_values)
        order = np.squeeze(order)
        atlas_values = atlas_values[order]
        data = data[:, order]
    else:
        data = apply_mask(img, mask_img)

    # Detrend and standardize data
    if detrend:
        data = clean(data, t_r=tr, detrend=True, standardize='zscore')

    if figure is None:
        if not axes:
            figsize = (10, 5)
            figure = plt.figure(figsize=figsize)
        else:
            figure = axes.figure

    if axes is None:
        axes = figure.add_subplot(1, 1, 1)
    else:
        assert axes.figure is figure, ('The axes passed are not in the figure')

    # Determine vmin and vmax based on the full data
    std = np.mean(data.std(axis=0))
    default_vmin = data.mean() - (2 * std)
    default_vmax = data.mean() + (2 * std)

    # Avoid segmentation faults for long acquisitions by decimating the data
    LONG_CUTOFF = 800
    # Get smallest power of 2 greater than the number of volumes divided by the
    # cutoff, to determine how much to decimate (downsample) the data.
    n_decimations = int(np.ceil(np.log2(np.ceil(n_tsteps / LONG_CUTOFF))))
    data = data[::2 ** n_decimations, :]

    if is_atlas:
        # Define nested GridSpec
        legend = False
        wratios = [2, 100, 20]
        gs = mgs.GridSpecFromSubplotSpec(
            1,
            2 + int(legend),
            subplot_spec=axes,
            width_ratios=wratios[: 2 + int(legend)],
            wspace=0.0,
        )

        ax0 = plt.subplot(gs[0])
        ax0.set_xticks([])
        ax0.imshow(
            atlas_values[:, np.newaxis],
            interpolation='none',
            aspect='auto',
            cmap=cmap
        )
        if mask_labels:
            # Add labels to middle of each associated band
            mask_labels_inv = {v: k for k, v in mask_labels.items()}
            ytick_locs = [
                np.mean(np.where(atlas_values == i)[0])
                for i in np.unique(atlas_values)
            ]
            ax0.set_yticks(ytick_locs)
            ax0.set_yticklabels([
                mask_labels_inv[i] for i in np.unique(atlas_values)
            ])
        else:
            ax0.set_yticks([])

        # Carpet plot
        axes = plt.subplot(gs[1])  # overwrite axes
        axes.imshow(
            data.T,
            interpolation='nearest',
            aspect='auto',
            cmap='gray',
            vmin=vmin or default_vmin,
            vmax=vmax or default_vmax,
        )
        ax0.tick_params(axis='both', which='both', length=0)
    else:
        axes.imshow(
            data.T,
            interpolation='nearest',
            aspect='auto',
            cmap='gray',
            vmin=vmin or default_vmin,
            vmax=vmax or default_vmax,
        )

    axes.grid(False)
    axes.set_yticks([])
    axes.set_yticklabels([])

    # Set 10 frame markers in X axis
    interval = max(
        (int(data.shape[0] + 1) // 10, int(data.shape[0] + 1) // 5, 1))
    xticks = list(range(0, data.shape[0])[::interval])
    axes.set_xticks(xticks)
    axes.set_xlabel('time (s)')

    if title:
        axes.set_title(title)

    labels = tr * (np.array(xticks))
    labels *= (2 ** n_decimations)
    axes.set_xticklabels(['%.02f' % t for t in labels.tolist()])

    # Remove and redefine spines
    for side in ['top', 'right']:
        # Toggle the spine objects
        axes.spines[side].set_color('none')
        axes.spines[side].set_visible(False)

    axes.xaxis.set_ticks_position('bottom')
    axes.spines['bottom'].set_position(('outward', 10))

    if not mask_labels:
        axes.yaxis.set_ticks_position('left')
        buffer = 20 if is_atlas else 10
        axes.spines['left'].set_position(('outward', buffer))
        axes.set_ylabel('voxels')

    if output_file is not None:
        figure.savefig(output_file)
        plt.close(figure)
        figure = None

    return figure


def plot_img_comparison(ref_imgs, src_imgs, masker, plot_hist=True, log=True,
                        ref_label="image set 1", src_label="image set 2",
                        output_dir=None, axes=None):
    """Creates plots to compare two lists of images and measure correlation.

    The first plot displays linear correlation between voxel values.
    The second plot superimposes histograms to compare values distribution.

    Parameters
    ----------
    ref_imgs : nifti_like
        Reference images.

    src_imgs : nifti_like
        Source images.

    masker : NiftiMasker object
        Mask to be used on data.

    plot_hist : Boolean, optional
        If True then histograms of each img in ref_imgs will be plotted
        along-side the histogram of the corresponding image in src_imgs.
        Default=True.

    log : Boolean, optional
        Passed to plt.hist. Default=True.

    ref_label : str, optional
        Name of reference images. Default='image set 1'.

    src_label : str, optional
        Name of source images. Default='image set 2'.

    output_dir : string, optional
        Directory where plotted figures will be stored.

    axes : list of two matplotlib Axes objects, optional
        Can receive a list of the form [ax1, ax2] to render the plots.
        By default new axes will be created.

    Returns
    -------
    corrs : numpy.ndarray
        Pearson correlation between the images.

    """
    # note: doesn't work with 4d images;
    # when plot_hist is False creates two empty axes and doesn't plot anything
    corrs = []
    for i, (ref_img, src_img) in enumerate(zip(ref_imgs, src_imgs)):
        if axes is None:
            _, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
        else:
            (ax1, ax2) = axes
        ref_data = masker.transform(ref_img).ravel()
        src_data = masker.transform(src_img).ravel()
        if ref_data.shape != src_data.shape:
            warnings.warn("Images are not shape-compatible")
            return

        corr = stats.pearsonr(ref_data, src_data)[0]
        corrs.append(corr)

        if plot_hist:
            ax1.scatter(
                ref_data, src_data, label="Pearsonr: %.2f" % corr, c="g",
                alpha=.6)
            x = np.linspace(*ax1.get_xlim(), num=100)
            ax1.plot(x, x, linestyle="--", c="k")
            ax1.grid("on")
            ax1.set_xlabel(ref_label)
            ax1.set_ylabel(src_label)
            ax1.legend(loc="best")

            ax2.hist(ref_data, alpha=.6, bins=128, log=log, label=ref_label)
            ax2.hist(src_data, alpha=.6, bins=128, log=log, label=src_label)
            ax2.set_title("Histogram of imgs values")
            ax2.grid("on")
            ax2.legend(loc="best")

            if output_dir is not None:
                if not os.path.exists(output_dir):
                    os.makedirs(output_dir)
                plt.savefig(os.path.join(output_dir, "%04i.png" % i))

        plt.tight_layout()

    return corrs
