Exemple #1
0
def load_vols(niimgs):
    """Loads a nifti image (or a bail of) into a list qof 3D volumes.

    Parameters
    ----------
    niimgs: 3 or 4D Niimg-like object
        If niimgs is an iterable, checks if data is really 4D. Then,
        considering that it is a list of niimg and load them one by one.
        If niimg is a string, consider it as a path to Nifti image and
        call nibabel.load on it. If it is an object, check if get_data
        and get_affine methods are present, raise an Exception otherwise.

    Returns
    -------
    niimgs_: list of nifti image objects
        The loaded volumes.
    """
    # try loading 4d
    try:
        niimgs = list(check_niimg_4d(niimgs, return_iterator=True))
    except TypeError:
        # probably not 4d
        niimgs = [check_niimg(niimgs)]
    except ValueError:
        # probably inconsisten affines
        pass
    try:
        # try loading volumes one-by-one
        if isinstance(niimgs, _basestring):
            niimgs = [niimgs]
        return [check_niimg(niimg, ensure_ndim=3) for niimg in niimgs]
    except TypeError:
        pass

    # collect the loaded volumes into a list
    if is_niimg(niimgs):
        # should be 3d, squash 4th dimension otherwise
        if niimgs.shape[-1] == 1:
            return [
                nibabel.Nifti1Image(niimgs.get_data()[:, :, :, 0],
                                    niimgs.get_affine())
            ]
        else:
            return list(iter_img(niimgs))
    else:
        niimgs = list(niimgs)
        if len(niimgs) == 1:
            niimgs = niimgs[0]
        return list(iter_img(niimgs))
Exemple #2
0
def load_vols(niimgs):
    """Loads a nifti image (or a bail of) into a list qof 3D volumes.

    Parameters
    ----------
    niimgs: 3 or 4D Niimg-like object
        If niimgs is an iterable, checks if data is really 4D. Then,
        considering that it is a list of niimg and load them one by one.
        If niimg is a string, consider it as a path to Nifti image and
        call nibabel.load on it. If it is an object, check if get_data
        and get_affine methods are present, raise an Exception otherwise.

    Returns
    -------
    niimgs_: list of nifti image objects
        The loaded volumes.
    """
    # try loading 4d
    try:
        niimgs = list(check_niimg_4d(niimgs, return_iterator=True))
    except TypeError:
        # probably not 4d
        niimgs = [check_niimg(niimgs)]
    except ValueError:
        # probably inconsisten affines
        pass
    try:
        # try loading volumes one-by-one
        if isinstance(niimgs, _basestring): niimgs = [niimgs]
        return [check_niimg(niimg, ensure_ndim=3) for niimg in niimgs]
    except TypeError:
        pass

    # collect the loaded volumes into a list
    if is_niimg(niimgs):
        # should be 3d, squash 4th dimension otherwise
        if niimgs.shape[-1] == 1:
            return [nibabel.Nifti1Image(niimgs.get_data()[:, :, :, 0],
                                        niimgs.get_affine())]
        else:
            return list(iter_img(niimgs))
    else:
        niimgs = list(niimgs)
        if len(niimgs) == 1: niimgs = niimgs[0]
        return list(iter_img(niimgs))
Exemple #3
0
    def _sanitize_raw_data(self, raw_data, fitting=False):
        """
        Re-implementation of parent method to sanitize fMRI data.

        """

        if not hasattr(self, 'basenames_'):
            self.basenames_ = None

        if isinstance(raw_data, _basestring):
            # _basestring
            if isinstance(raw_data, _basestring):
                self.basenames_ = os.path.basename(raw_data)
            img = nibabel.load(raw_data)
            raw_data, self.affine_ = img.get_data(), img.get_affine()
        elif is_niimg(raw_data):
            raw_data, self.affine_ = raw_data.get_data(), raw_data.get_affine()
        elif isinstance(raw_data, list) and (isinstance(
                raw_data[0], _basestring) or is_niimg(raw_data[0])):
            # list of strings or niimgs
            if isinstance(raw_data[0], _basestring):
                self.basenames_ = [os.path.basename(x) for x in raw_data]
            n_scans = len(raw_data)
            _first = check_niimg(raw_data[0])
            _raw_data = np.ndarray(list(_first.shape) + [n_scans])
            _raw_data[..., 0] = _first.get_data()
            self.affine_ = [_first.get_affine()]

            for t in range(1, n_scans):
                vol = check_niimg(raw_data[t])
                _raw_data[..., t] = vol.get_data()
                self.affine_.append(vol.get_affine())
            raw_data = _raw_data
        else:
            raw_data = np.array(raw_data)

        if raw_data.ndim == 5:
            assert raw_data.shape[-2] == 1, raw_data.shape
            raw_data = raw_data[..., 0, ...]

        # our business is over: deligate to super method
        return STC._sanitize_raw_data(self, raw_data, fitting=fitting)
    def _sanitize_raw_data(self, raw_data, fitting=False):
        """
        Re-implementation of parent method to sanitize fMRI data.

        """

        if not hasattr(self, 'basenames_'):
            self.basenames_ = None

        if isinstance(raw_data, basestring):
            # basestring
            if isinstance(raw_data, basestring):
                self.basenames_ = os.path.basename(raw_data)
            img = nibabel.load(raw_data)
            raw_data, self.affine_ = img.get_data(), img.get_affine()
        elif is_niimg(raw_data):
            raw_data, self.affine_ = raw_data.get_data(), raw_data.get_affine()
        elif isinstance(raw_data, list) and (isinstance(
                raw_data[0], basestring) or is_niimg(raw_data[0])):
            # list of strings or niimgs
            if isinstance(raw_data[0], basestring):
                self.basenames_ = [os.path.basename(x) for x in raw_data]
            n_scans = len(raw_data)
            _first = check_niimg(raw_data[0])
            _raw_data = np.ndarray(list(_first.shape) + [n_scans])
            _raw_data[..., 0] = _first.get_data()
            self.affine_ = [_first.get_affine()]

            for t in range(1, n_scans):
                vol = check_niimg(raw_data[t])
                _raw_data[..., t] = vol.get_data()
                self.affine_.append(vol.get_affine())
            raw_data = _raw_data
        else:
            raw_data = np.array(raw_data)

        if raw_data.ndim == 5:
            assert raw_data.shape[-2] == 1, raw_data.shape
            raw_data = raw_data[..., 0, ...]

        # our business is over: deligate to super method
        return STC._sanitize_raw_data(self, raw_data, fitting=fitting)
Exemple #5
0
def crop_img(img, rtol=1e-8, copy=True, return_slices=False):
    """Crops img as much as possible
    Will crop img, removing as many zero entries as possible
    without touching non-zero entries. Will leave one voxel of
    zero padding around the obtained non-zero area in order to
    avoid sampling issues later on.
    Parameters
    ----------
    img: Niimg-like object
        See http://nilearn.github.io/manipulating_images/input_output.html
        img to be cropped.
    rtol: float
        relative tolerance (with respect to maximal absolute
        value of the image), under which values are considered
        negligeable and thus croppable.
    copy: boolean
        Specifies whether cropped data is copied or not.
    return_slices: boolean
        If True, the slices that define the cropped image will be returned.
    Returns
    -------
    cropped_img: image
        Cropped version of the input image
    """

    img = check_niimg(img)
    data = img.get_data()
    infinity_norm = max(-data.min(), data.max())
    # Discard voxels that are approximately zero or zero
    passes_threshold = np.logical_or(data < -rtol * infinity_norm,
                                     data > rtol * infinity_norm)

    if data.ndim == 4:
        passes_threshold = np.any(passes_threshold, axis=-1)
    # Return a numpy array with shape (3, num_column), each column is coordinates of voxel other than 0
    coords = np.array(np.where(passes_threshold))
    # Find min and max of rows => Determine min and max of coordinates => range for x, y, z for coordinates.
    start = coords.min(axis=1)
    end = coords.max(axis=1) + 1

    # pad with one voxel to avoid resampling problems
    start = np.maximum(start - 1, 0)
    end = np.minimum(end + 1, data.shape[:3])

    # Range for three dimension to crop MRI image, ex: (10, 100), (40, 120), (30, 130)
    slices = [slice(s, e) for s, e in zip(start, end)]
    # return_slices = True
    if return_slices:
        return slices

    return crop_img_to(img, slices, copy=copy)
def crop_img(img, rtol=1e-8, copy=True, return_slices=False):
    """Crops img as much as possible
    Will crop img, removing as many zero entries as possible
    without touching non-zero entries. Will leave one voxel of
    zero padding around the obtained non-zero area in order to
    avoid sampling issues later on.
    Parameters
    ----------
    img: Niimg-like object
        See http://nilearn.github.io/manipulating_images/input_output.html
        img to be cropped.
    rtol: float
        relative tolerance (with respect to maximal absolute
        value of the image), under which values are considered
        negligeable and thus croppable.
    copy: boolean
        Specifies whether cropped data is copied or not.
    return_slices: boolean
        If True, the slices that define the cropped image will be returned.
    Returns
    -------
    cropped_img: image
        Cropped version of the input image
    """

    img = check_niimg(img)
    data = img.get_data()
    infinity_norm = max(-data.min(), data.max())
    # Bo di cac voxel xap xi bang 0, hoac bang 0.
    passes_threshold = np.logical_or(data < -rtol * infinity_norm,
                                     data > rtol * infinity_norm)

    if data.ndim == 4:
        passes_threshold = np.any(passes_threshold, axis=-1)
    # Tra ve mang gom 3 dong, moi cot la cac thong so toa do cua voxel khac 0.
    coords = np.array(np.where(passes_threshold))
    # Tim min va max cua cac dong => xac dinh min va max cua toa do => pham vi toa do.
    start = coords.min(axis=1)
    end = coords.max(axis=1) + 1

    # pad with one voxel to avoid resampling problems
    start = np.maximum(start - 1, 0)
    end = np.minimum(end + 1, data.shape[:3])

    # slices nay neu la 3D thi se la pham vi ung voi 3 dimension de crop hinh, vi du: (10, 100), (40, 120), (30, 130)
    slices = [slice(s, e) for s, e in zip(start, end)]

    if return_slices:
        return slices

    return crop_img_to(img, slices, copy=copy)
Exemple #7
0
def get_index_of_slice(foreground):
    img = check_niimg(foreground)
    image = img.get_data()
    #
    all_index = np.array(np.where(image == 1))
    #
    min_index = all_index.min(axis=1)
    max_index = all_index.max(axis=1) + 1
    #
    min_index = np.maximum(min_index - 1, 0)
    max_index = np.minimum(max_index + 1, image.shape[:3])
    #
    slices = [slice(s, e) for s, e in zip(min_index, max_index)]
    return slices
def apply_realignment_to_vol(vol, q, inverse=True):
    """
    Modifies the affine headers of the given volume according to
    the realignment parameters (q).

    Parameters
    ----------
    vol: `nibabel.Nifti1Image`
        image to be transformed
    q: 1D array of length <= 12
        realignment parameters representing the rigid transformation
    inverse: boolean, optional (default False)
        indicates the direction in which the transformation is to be performed;
        if set then, it is assumed q actually represents the inverse of the
        transformation to be applied

    Returns
    -------
    `nibabel.Nifti1Image` object
        the realigned volume

    Notes
    -----
    Input is not modified.

    """
    # misc
    vol = check_niimg(vol)
    # assert len(vol.shape) == 3, vol.shape

    # convert realigment params to affine transformation matrix
    M_q = spm_matrix(q)
    if inverse:
        M_q = scipy.linalg.inv(M_q)

    # apply affine transformation
    return nibabel.Nifti1Image(vol.get_data(), np.dot(
        M_q, vol.get_affine()))
Exemple #9
0
def crop_img(img, rtol=1e-8, copy=True, return_slices=False):
  img = check_niimg(img)
  data = img.get_data()
  infinity_norm = max(-data.min(), data.max())
  passes_threshold = np.logical_or(data < -rtol * infinity_norm,
                                    data > rtol * infinity_norm)

  if data.ndim == 4:
      passes_threshold = np.any(passes_threshold, axis=-1)
  coords = np.array(np.where(passes_threshold))
  start = coords.min(axis=1)
  end = coords.max(axis=1) + 1

  # pad with one voxel to avoid resampling problems
  start = np.maximum(start - 1, 0)
  end = np.minimum(end + 1, data.shape[:3])

  slices = [slice(s, e) for s, e in zip(start, end)]

  if return_slices:
      return slices

  return crop_img_to(img, slices, copy=copy)
def apply_realignment_to_vol(vol, q, inverse=True):
    """
    Modifies the affine headers of the given volume according to
    the realignment parameters (q).

    Parameters
    ----------
    vol: `nibabel.Nifti1Image`
        image to be transformed
    q: 1D array of length <= 12
        realignment parameters representing the rigid transformation
    inverse: boolean, optional (default False)
        indicates the direction in which the transformation is to be performed;
        if set then, it is assumed q actually represents the inverse of the
        transformation to be applied

    Returns
    -------
    `nibabel.Nifti1Image` object
        the realigned volume

    Notes
    -----
    Input is not modified.

    """
    # misc
    vol = check_niimg(vol)
    # assert len(vol.shape) == 3, vol.shape

    # convert realigment params to affine transformation matrix
    M_q = spm_matrix(q)
    if inverse:
        M_q = scipy.linalg.inv(M_q)

    # apply affine transformation
    return nibabel.Nifti1Image(vol.get_data(), np.dot(M_q, vol.get_affine()))
Exemple #11
0
def save_vols(vols,
              output_dir,
              basenames=None,
              affine=None,
              concat=False,
              prefix='',
              ext=None):
    """
    Saves a single 4D image or a couple of 3D vols unto disk.

    vols: single 4D nibabel image object, or list of 3D nibabel image objects
        volumes, of ndarray
        volumes to be saved

    output_dir: string
        existing filename, destination directory

    basenames: string or list of string, optional (default None)
        basename(s) for output image(s)

    affine: 2D array of shape (4, 4)
        affine matrix for the output images

    concat: bool, optional (default False)
        concatenate all vols into a single film

    prefix: string, optional (default '')
       prefix to be prepended to output file basenames

    ext: string, optional (default ".nii.gz")
        file extension for output images

    Returns
    -------
    string of list of strings, dependending on whether vols is list or
    not, and on whether concat is set or not
        the output image filename(s)

    """
    if ext is None:
        ext = ".nii.gz"

    def _nifti_or_ndarray_to_nifti(x):
        if is_niimg(x):
            if affine is not None:
                raise ValueError(
                    ("vol is of type %s; not expecting `affine` parameter.") %
                    type(x))
            else:
                return x

        if affine is None:
            raise ValueError(
                "vol is of type ndarray; you need to specifiy `affine`")
        else:
            return nibabel.Nifti1Image(x, affine)

    if basenames is not None:
        basenames = get_basenames(basenames, ext=ext)

    # sanitize output_dir
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    # vols are ndarray ?
    if isinstance(vols, np.ndarray):
        vols = _nifti_or_ndarray_to_nifti(vols)

    # concat vols to single 4D film ?
    if concat:
        if isinstance(vols, list):
            vols = nibabel.concat_images(
                [_nifti_or_ndarray_to_nifti(vol) for vol in vols],
                check_affines=False)
            if basenames is not None:
                if not isinstance(basenames, _basestring):
                    basenames = basenames[0]
        else:
            if basenames is not None:
                if not isinstance(basenames, _basestring):
                    raise RuntimeError(
                        ("concat=True specified but basenames is of type %s "
                         "instead of string") % type(basenames))

    if not isinstance(vols, list):
        if basenames is None:
            basenames = get_basenames("vols", ext=ext)

        if not isinstance(basenames, _basestring):
            vols = nibabel.four_to_three(vols)
            filenames = []
            for vol, basename in zip(vols, basenames):
                if not isinstance(basename, _basestring):
                    raise RuntimeError
                filename = os.path.join(output_dir,
                                        "%s%s" % (prefix, basename))
                nibabel.save(vol, filename)
                filenames.append(filename)
        else:
            filenames = os.path.join(output_dir, "%s%s" % (prefix, basenames))
            nibabel.save(vols, filenames)

        return filenames
    else:
        n_vols = len(vols)
        filenames = []
        if basenames is None:
            if prefix:
                prefix = prefix + "_"
        else:
            if isinstance(basenames, _basestring):
                basenames = [
                    "vol%i_%s" % (t, basenames) for t in range(len(vols))
                ]
            else:
                if len(set(basenames)) != len(vols):
                    raise RuntimeError
        for t, vol in zip(range(n_vols), vols):
            if isinstance(vol, np.ndarray):
                if affine is None:
                    raise ValueError(
                        ("vols is of type ndarray; you need to specifiy"
                         " `affine`"))
                else:
                    vol = nibabel.Nifti1Image(vol, affine)

            # save realigned vol unto disk
            if basenames is None:
                output_filename = os.path.join(
                    output_dir, get_basename("%svol_%i" % (prefix, t),
                                             ext=ext))
            else:
                basename = basenames if isinstance(
                    basenames, _basestring) else basenames[t]
                output_filename = os.path.join(
                    output_dir,
                    get_basenames("%s%s" % (prefix, basename), ext=ext))

            vol = check_niimg(vol)
            nibabel.save(vol, output_filename)

            # update rvols and filenames
            filenames.append(output_filename)

    return filenames
Exemple #12
0
def save_vols(vols, output_dir, basenames=None, affine=None,
              concat=False, prefix='', ext=None):
    """
    Saves a single 4D image or a couple of 3D vols unto disk.

    vols: single 4D nibabel image object, or list of 3D nibabel image objects
        volumes, of ndarray
        volumes to be saved

    output_dir: string
        existing filename, destination directory

    basenames: string or list of string, optional (default None)
        basename(s) for output image(s)

    affine: 2D array of shape (4, 4)
        affine matrix for the output images

    concat: bool, optional (default False)
        concatenate all vols into a single film

    prefix: string, optional (default '')
       prefix to be prepended to output file basenames

    ext: string, optional (default ".nii.gz")
        file extension for output images

    Returns
    -------
    string of list of strings, dependending on whether vols is list or
    not, and on whether concat is set or not
        the output image filename(s)

    """

    def _nifti_or_ndarray_to_nifti(x):
        if is_niimg(x):
            if not affine is None:
                raise ValueError(
                    ("vol is of type %s; not expecting `affine` parameter."
                     ) % type(x))
            else:
                return x

        if affine is None:
            raise ValueError(
                "vol is of type ndarray; you need to specifiy `affine`")
        else:
            return nibabel.Nifti1Image(x, affine)

    if not basenames is None:
        basenames = get_basenames(basenames, ext=ext)

    # sanitize output_dir
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    # vols are ndarray ?
    if isinstance(vols, np.ndarray):
        vols = _nifti_or_ndarray_to_nifti(vols)

    # concat vols to single 4D film ?
    if concat:
        if isinstance(vols, list):
            vols = nibabel.concat_images([_nifti_or_ndarray_to_nifti(vol)
                                  for vol in vols],
                                         check_affines=False
                                         )
            if not basenames is None:
                if not isinstance(basenames, _basestring):
                    basenames = basenames[0]
        else:
            if not basenames is None:
                if not isinstance(basenames, _basestring):
                    raise RuntimeError(
                        ("concat=True specified but basenames is of type %s "
                         "instead of string") % type(basenames))

    if not isinstance(vols, list):
        if basenames is None:
            basenames = get_basenames("vols", ext=ext)

        if not isinstance(basenames, _basestring):
            vols = nibabel.four_to_three(vols)
            filenames = []
            for vol, basename in zip(vols, basenames):
                if not isinstance(basename, _basestring):
                    raise RuntimeError
                filename = os.path.join(output_dir, "%s%s" % (
                        prefix, basename))
                nibabel.save(vol, filename)
                filenames.append(filename)
        else:
            filenames = os.path.join(output_dir, "%s%s" % (
                    prefix, basenames))
            nibabel.save(vols, filenames)

        return filenames
    else:
        n_vols = len(vols)
        filenames = []
        if basenames is None:
            if prefix:
                prefix = prefix + "_"
        else:
            if isinstance(basenames, _basestring):
                basenames = ["vol%i_%s" % (t, basenames)
                             for t in range(len(vols))]
            else:
                if len(set(basenames)) != len(vols):
                    raise RuntimeError
        for t, vol in zip(range(n_vols), vols):
            if isinstance(vol, np.ndarray):
                if affine is None:
                    raise ValueError(
                        ("vols is of type ndarray; you need to specifiy"
                         " `affine`"))
                else:
                    vol = nibabel.Nifti1Image(vol, affine)

            # save realigned vol unto disk
            if basenames is None:
                if ext is None:
                    ext = ".nii.gz"
                output_filename = os.path.join(output_dir,
                                               get_basename("%svol_%i" % (
                                                   prefix, t), ext=ext))
            else:
                basename = basenames if isinstance(
                    basenames, _basestring) else basenames[t]
                output_filename = os.path.join(output_dir,
                                               get_basenames("%s%s" % (
                                                   prefix, basename), ext=ext))

            vol = check_niimg(vol)
            nibabel.save(vol, output_filename)

            # update rvols and filenames
            filenames.append(output_filename)

    return filenames
Exemple #13
0
def reslice_vols(vols,
                 target_affine=None,
                 interp_order=3,
                 interp_mode='constant',
                 mask=True,
                 wrp=None,
                 log=None):
    """
    Uses B-spline interpolation to reslice (i.e resample) all other
    volumes to have thesame affine header matrix as the first (0th) volume.

    Parameters
    ----------
    vols: list of `nibabel.Nifti1Image` objects
        vols[0] is the reference volume. All other volumes will be resliced
        so that the end up with the same header affine matrix as vol[0].

    target_affine: 2D array of shape (4, 4), optional (default None)
        Target affine matrix to which the vols will be resliced. If not
        specified, vols will be resliced to match the first vol's affine.

    interp_order: int, optional (default 3)
        Degree of B-spline interpolation used for resampling the volumes.

    interp_mode: string, optional (default "wrap")
        Mode param to be passed to `scipy.ndimage.map_coordinates`.

    mask: boolean, optional (default True)
        If set, vols will be masked before reslicing. This masking will
        help eliminate artefactual motion across volumes due to on-off
        voxels.

    wrp: list_like of 3 booleans, optional (default None)
        Option passed to _get_mask function. For each axis, it specifies
        if or not wrapping is to be done along that axis.

    log: function(basestring), optional (default None)
        function for logging messages.

    Returns
    -------
    vols: generator object on `nibabel.Nifti1Image` objects
        resliced volumes.

    Raises
    ------
    RuntimeError in case dimensions are inconsistent across volumes.

    """

    wrp = [1, 1, 0] if wrp is None else wrp
    vols = list(vols)

    def _log(msg):
        if log:
            log(msg)
        else:
            print(msg)

    # load first vol
    vol_0 = check_niimg(vols[0])

    # sanitize target_affine
    reslice_first_vol = True
    if target_affine is None:
        reslice_first_vol = False
        target_affine = vol_0.get_affine()

    # build working grid
    dim = vol_0.shape
    n_scans = len(vols)
    grid = np.mgrid[0:dim[0], 0:dim[1], 0:dim[2]].reshape((3, -1))

    # compute global mask for all vols, to mask out voxels that show
    # artefactual movement across volumes
    msk = np.ones(grid.shape[1]).astype('bool')
    if mask:
        for t in range(len(vols)):
            # load vol
            vol = check_niimg(vols[t])

            # saniiy check on dimensions
            if vol.shape != dim:
                raise RuntimeError(
                    ("All source volumes must have the same dimensions as the "
                     "reference. Volume %i has dim %s instead of %s.") %
                    (t, vol.shape, dim))

            # affine matrix for passing from vol's space to the ref vol's
            M = scipy.linalg.inv(
                scipy.linalg.lstsq(target_affine, vol.get_affine())[0])
            fov_msk, _ = _get_mask(M, grid, dim, wrp=wrp)
            msk = msk & fov_msk

    # loop on all vols, reslicing them one-by-one
    rvols = []
    for t in range(n_scans):
        _log('\tReslicing volume %i/%i...' % (t + 1, len(vols)))
        vol = check_niimg(vols[t])

        # reslice vol
        if t > 0 or reslice_first_vol:
            # affine matrix for passing from vol's space to the ref vol's
            M = scipy.linalg.inv(
                scipy.linalg.lstsq(target_affine, vol.get_affine())[0])

            # transform vol's grid according to M
            _, new_grid = _get_mask(M, grid, dim, wrp=wrp)

            # resample vol on new grid
            rdata = scipy.ndimage.map_coordinates(vol.get_data(),
                                                  new_grid,
                                                  order=interp_order,
                                                  mode=interp_mode)
        else:  # don't reslice first vol
            rdata = vol.get_data().ravel()
        rdata[~msk] = 0

        # replace vols's affine with ref vol's (this has been the ultimate
        # goal all along)
        rvols.append(nibabel.Nifti1Image(rdata.reshape(dim), target_affine))

    return rvols
def plot_registration(reference_img,
                      coregistered_img,
                      title="untitled coregistration!",
                      cut_coords=None,
                      display_mode='ortho',
                      cmap=None,
                      close=False,
                      output_filename=None):
    """Plots a coregistered source as bg/contrast for the reference image

    Parameters
    ----------
    reference_img: string
        path to reference (background) image

    coregistered_img: string
        path to other image (to be compared with reference)

    display_mode: string (optional, defaults to 'ortho')
        display_mode param

    cmap: matplotlib colormap object (optional, defaults to spectral)
        colormap to user for plots

    output_filename: string (optional)
        path where plot will be stored

    """
    # sanity
    if cmap is None:
        cmap = plt.cm.gray  # registration QA always gray cmap!

    if not isinstance(coregistered_img, _basestring) and hasattr(
            coregistered_img, "__iter__"):
        coregistered_img = coregistered_img[0][0]
    if not isinstance(reference_img, _basestring) and hasattr(
            reference_img, "__iter__"):
        reference_img = reference_img[0][0]

    print(reference_img)
    print(coregistered_img)
    reference_img = check_niimg(reference_img)
    coregistered_img = check_niimg(coregistered_img)

    if cut_coords is None:
        cut_coords = (-10, -28, 17)

    if display_mode in ['x', 'y', 'z']:
        cut_coords = (cut_coords['xyz'.index(display_mode)], )

    # XXX nilearn complains about rotations in affine, etc.
    coregistered_img = reorder_img(coregistered_img, resample="continuous")

    _slicer = plot_img(coregistered_img,
                       cmap=cmap,
                       cut_coords=cut_coords,
                       display_mode=display_mode,
                       black_bg=True)

    # XXX nilearn complains about rotations in affine, etc.
    # reference_img = reorder_img(reference_img, resample="continuous")
    _slicer.add_edges(reference_img)
    # misc
    _slicer.title(title, size=12, color='w', alpha=0)

    if output_filename is not None:
        try:
            plt.savefig(output_filename,
                        dpi=200,
                        bbox_inches='tight',
                        facecolor="k",
                        edgecolor="k")
            if close:
                plt.close()
        except AttributeError:
            # XXX TODO: handle this case!!
            pass
def reslice_vols(vols, target_affine=None, interp_order=3,
                 interp_mode='constant', mask=True, wrp=None, log=None):
    """
    Uses B-spline interpolation to reslice (i.e resample) all other
    volumes to have thesame affine header matrix as the first (0th) volume.

    Parameters
    ----------
    vols: list of `nibabel.Nifti1Image` objects
        vols[0] is the reference volume. All other volumes will be resliced
        so that the end up with the same header affine matrix as vol[0].

    target_affine: 2D array of shape (4, 4), optional (default None)
        Target affine matrix to which the vols will be resliced. If not
        specified, vols will be resliced to match the first vol's affine.

    interp_order: int, optional (default 3)
        Degree of B-spline interpolation used for resampling the volumes.

    interp_mode: string, optional (default "wrap")
        Mode param to be passed to `scipy.ndimage.map_coordinates`.

    mask: boolean, optional (default True)
        If set, vols will be masked before reslicing. This masking will
        help eliminate artefactual motion across volumes due to on-off
        voxels.

    wrp: list_like of 3 booleans, optional (default None)
        Option passed to _get_mask function. For each axis, it specifies
        if or not wrapping is to be done along that axis.

    log: function(basestring), optional (default None)
        function for logging messages.

    Returns
    -------
    vols: generator object on `nibabel.Nifti1Image` objects
        resliced volumes.

    Raises
    ------
    RuntimeError in case dimensions are inconsistent across volumes.

    """

    wrp = [1, 1, 0] if wrp is None else wrp
    vols = list(vols)

    def _log(msg):
        if log:
            log(msg)
        else:
            print(msg)

    # load first vol
    vol_0 = check_niimg(vols[0])

    # sanitize target_affine
    reslice_first_vol = True
    if target_affine is None:
        reslice_first_vol = False
        target_affine = vol_0.get_affine()

    # build working grid
    dim = vol_0.shape
    n_scans = len(vols)
    grid = np.mgrid[0:dim[0], 0:dim[1], 0:dim[2]].reshape((3, -1))

    # compute global mask for all vols, to mask out voxels that show
    # artefactual movement across volumes
    msk = np.ones(grid.shape[1]).astype('bool')
    if mask:
        for t in range(len(vols)):
            # load vol
            vol = check_niimg(vols[t])

            # saniiy check on dimensions
            if vol.shape != dim:
                raise RuntimeError(
                    ("All source volumes must have the same dimensions as the "
                     "reference. Volume %i has dim %s instead of %s.") % (
                        t, vol.shape, dim))

            # affine matrix for passing from vol's space to the ref vol's
            M = scipy.linalg.inv(scipy.linalg.lstsq(
                    target_affine, vol.get_affine())[0])
            fov_msk, _ = _get_mask(M, grid, dim, wrp=wrp)
            msk = msk & fov_msk

    # loop on all vols, reslicing them one-by-one
    rvols = []
    for t in range(n_scans):
        _log('\tReslicing volume %i/%i...' % (t + 1, len(vols)))
        vol = check_niimg(vols[t])

        # reslice vol
        if t > 0 or reslice_first_vol:
            # affine matrix for passing from vol's space to the ref vol's
            M = scipy.linalg.inv(scipy.linalg.lstsq(target_affine,
                                                    vol.get_affine())[0])

            # transform vol's grid according to M
            _, new_grid = _get_mask(M, grid, dim, wrp=wrp)

            # resample vol on new grid
            rdata = scipy.ndimage.map_coordinates(
                vol.get_data(), new_grid, order=interp_order, mode=interp_mode)
        else:  # don't reslice first vol
            rdata = vol.get_data().ravel()
        rdata[~msk]  = 0

        # replace vols's affine with ref vol's (this has been the ultimate
        # goal all along)
        rvols.append(nibabel.Nifti1Image(rdata.reshape(dim), target_affine))

    return rvols
Exemple #16
0
def crop_img(img,
             rtol=1e-8,
             copy=True,
             return_slices=False,
             pad=True,
             percentile=None,
             return_affine=False):
    """Crops img as much as possible
    Will crop img, removing as many zero entries as possible
    without touching non-zero entries. Will leave one voxel of
    zero padding around the obtained non-zero area in order to
    avoid sampling issues later on.
    Parameters
    ----------
    img: Niimg-like object
        See http://nilearn.github.io/manipulating_images/input_output.html
        img to be cropped.
    rtol: float
        relative tolerance (with respect to maximal absolute
        value of the image), under which values are considered
        negligeable and thus croppable.
    copy: boolean
        Specifies whether cropped data is copied or not.
    return_slices: boolean
        If True, the slices that define the cropped image will be returned.
    pad: boolean or integer
        If True, an extra slice in each direction will be added to the image. If integer > 0 then the pad width will
        be set to that integer.
    percentile: integer or None
        If not None, then the image will be crop out slices below the given percentile
    Returns
    -------
    cropped_img: image
        Cropped version of the input image
    """

    img = check_niimg(img)
    data = img.get_data()
    if percentile is not None:
        passes_threshold = data > np.percentile(data, percentile)
    else:
        infinity_norm = max(-data.min(), data.max())
        passes_threshold = np.logical_or(data < -rtol * infinity_norm,
                                         data > rtol * infinity_norm)

    if data.ndim == 4:
        passes_threshold = np.any(passes_threshold, axis=-1)
    coords = np.array(np.where(passes_threshold))
    start = coords.min(axis=1)
    end = coords.max(axis=1) + 1

    if int(pad) > 0:
        pad_width = int(pad)
        # pad with one voxel to avoid resampling problems
        start = np.maximum(start - pad_width, 0)
        end = np.minimum(end + pad_width, data.shape[:3])

    slices = [slice(s, e) for s, e in zip(start, end)]

    if return_slices:
        return slices

    if return_affine:
        return image_slices_to_affine(img, slices), end - start

    return crop_img_to(img, slices, copy=copy)