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))
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))
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)
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)
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()))
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()))
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
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
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
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)