def _morphed_stc_as_volume(morph, stc, mri_resolution, mri_space, output): """Return volume source space as Nifti1Image and/or save to disk.""" if isinstance(stc, VolVectorSourceEstimate): stc = stc.magnitude() if not isinstance(stc, VolSourceEstimate): raise ValueError('Only volume source estimates can be converted to ' 'volumes') _check_dep(nibabel='2.1.0', dipy=False) NiftiImage, NiftiHeader = _triage_output(output) # if MRI resolution is set manually as a single value, convert to tuple if isinstance(mri_resolution, (int, float)): # use iso voxel size new_zooms = (float(mri_resolution),) * 3 elif isinstance(mri_resolution, tuple): new_zooms = mri_resolution # if full MRI resolution, compute zooms from shape and MRI zooms if isinstance(mri_resolution, bool): new_zooms = _get_zooms_orig(morph) if mri_resolution else None # create header hdr = NiftiHeader() hdr.set_xyzt_units('mm', 'msec') hdr['pixdim'][4] = 1e3 * stc.tstep # setup empty volume if morph.src_data['to_vox_map'] is not None: shape = morph.src_data['to_vox_map'][0] affine = morph.src_data['to_vox_map'][1] else: shape = morph.shape affine = morph.affine assert stc.data.ndim == 2 n_times = stc.data.shape[1] img = np.zeros((np.prod(shape), n_times)) img[stc.vertices, :] = stc.data img = img.reshape(shape + (n_times,), order='F') # match order='F' above del shape # make nifti from data with warnings.catch_warnings(): # nibabel<->numpy warning img = NiftiImage(img, affine, header=hdr) # reslice in case of manually defined voxel size zooms = morph.zooms[:3] if new_zooms is not None: from dipy.align.reslice import reslice new_zooms = new_zooms[:3] img, affine = reslice(_get_img_fdata(img), img.affine, # MRI to world registration zooms, # old voxel size in mm new_zooms) # new voxel size in mm with warnings.catch_warnings(): # nibabel<->numpy warning img = NiftiImage(img, affine) zooms = new_zooms # set zooms in header img.header.set_zooms(tuple(zooms) + (1,)) return img
def __init__(self, param=None, hdr=None, orientation=None, absolutepath=None, dim=None, verbose=1): from nibabel import Nifti1Header # initialization of all parameters self.im_file = None self.data = None self._path = None self.ext = "" if hdr is None: hdr = self.hdr = Nifti1Header() # an empty header else: self.hdr = hdr if absolutepath is not None: self._path = os.path.abspath(absolutepath) self.verbose = verbose # load an image from file if isinstance(param, str) or (sys.hexversion < 0x03000000 and isinstance(param, unicode)): self.loadFromPath(param, verbose) # copy constructor elif isinstance(param, type(self)): self.copy(param) # create an empty image (full of zero) of dimension [dim]. dim must be [x,y,z] or (x,y,z). No header. elif isinstance(param, list): self.data = np.zeros(param) self.hdr = hdr # create a copy of im_ref elif isinstance(param, (np.ndarray, np.generic)): self.data = param self.hdr = hdr else: raise TypeError('Image constructor takes at least one argument.')
def test_output_dtypes(): shape = (4, 2, 3) rng = np.random.RandomState(19441217) # IN-S BD data = rng.normal(4, 20, size=shape) aff = np.diag([2.2, 3.3, 4.1, 1]) cmap = vox2mni(aff) img = Image(data, cmap) fname_root = 'my_file' with InTemporaryDirectory(): for ext in 'img', 'nii': out_fname = fname_root + '.' + ext # Default is for data to come from data dtype save_image(img, out_fname) img_back = load_image(out_fname) hdr = img_back.metadata['header'] assert_dt_no_end_equal(hdr.get_data_dtype(), np.float) del img_back # lets window re-use the file # All these types are OK for both output formats for out_dt in 'i2', 'i4', np.int16, '<f4', '>f8': # Specified output dtype save_image(img, out_fname, out_dt) img_back = load_image(out_fname) hdr = img_back.metadata['header'] assert_dt_no_end_equal(hdr.get_data_dtype(), out_dt) del img_back # windows file re-use # Output comes from data by default data_typed = data.astype(out_dt) img_again = Image(data_typed, cmap) save_image(img_again, out_fname) img_back = load_image(out_fname) hdr = img_back.metadata['header'] assert_dt_no_end_equal(hdr.get_data_dtype(), out_dt) del img_back # Even if header specifies otherwise in_hdr = Nifti1Header() in_hdr.set_data_dtype(np.dtype('c8')) img_more = Image(data_typed, cmap, metadata={'header': in_hdr}) save_image(img_more, out_fname) img_back = load_image(out_fname) hdr = img_back.metadata['header'] assert_dt_no_end_equal(hdr.get_data_dtype(), out_dt) del img_back # But can come from header if specified save_image(img_more, out_fname, dtype_from='header') img_back = load_image(out_fname) hdr = img_back.metadata['header'] assert_dt_no_end_equal(hdr.get_data_dtype(), 'c8') del img_back # u2 only OK for nifti save_image(img, 'my_file.nii', 'u2') img_back = load_image('my_file.nii') hdr = img_back.metadata['header'] assert_dt_no_end_equal(hdr.get_data_dtype(), 'u2') # Check analyze can't save u2 datatype assert_raises(HeaderDataError, save_image, img, 'my_file.img', 'u2') del img_back
def parse_descrip(header: nib.Nifti1Header) -> dict[str, float]: descrip_dict = dict() descrip_array = header.get("descrip") assert isinstance(descrip_array, np.ndarray) descrip = descrip_array.tolist().decode() for m in descrip_pattern.finditer(descrip): var_name = m.group("var_name") value = m.group("value") unit = m.group("unit") assert isinstance(var_name, str) var_name = var_name.lower() value = float(value) key = None if var_name == "te": if unit is None: if value < 1: # heuristic unit = "s" else: unit = "ms" key = "echo_time" elif var_name == "tr": if unit is None: if value > 100: # heuristic unit = "ms" else: unit = "s" key = "repetition_time" if key is not None: base_quantity = ureg(unit) assert isinstance(base_quantity, pint.Quantity) quantity = value * base_quantity descrip_dict[key] = quantity.m_as(ureg.seconds) return descrip_dict
def from_h5obj(cls, fileobj, check=True): """Read the struct from a file object.""" xfm_list = [] h5group = fileobj["TransformGroup"] typo_fallback = "Transform" try: h5group["1"][f"{typo_fallback}Parameters"] except KeyError: typo_fallback = "Tranform" for xfm in list(h5group.values())[1:]: if xfm["TransformType"][0].startswith(b"AffineTransform"): _params = np.asanyarray(xfm[f"{typo_fallback}Parameters"]) xfm_list.append( ITKLinearTransform( parameters=from_matvec(_params[:-3].reshape(3, 3), _params[-3:]), offset=np.asanyarray( xfm[f"{typo_fallback}FixedParameters"]), )) continue if xfm["TransformType"][0].startswith( b"DisplacementFieldTransform"): _fixed = np.asanyarray(xfm[f"{typo_fallback}FixedParameters"]) shape = _fixed[:3].astype("uint16").tolist() offset = _fixed[3:6].astype("uint16") zooms = _fixed[6:9].astype("float") directions = _fixed[9:].astype("float").reshape((3, 3)) affine = from_matvec(directions * zooms, offset) field = np.asanyarray( xfm[f"{typo_fallback}Parameters"]).reshape( tuple(shape + [1, -1])) hdr = Nifti1Header() hdr.set_intent("vector") hdr.set_data_dtype("float") xfm_list.append( ITKDisplacementsField.from_image( Nifti1Image(field.astype("float"), affine, hdr))) continue raise NotImplementedError( f"Unsupported transform type {xfm['TransformType'][0]}") return xfm_list
def _interpolate_data(stc, morph, mri_resolution=True, mri_space=True, output='nifti1'): """Interpolate source estimate data to MRI.""" _check_dep(nibabel='2.1.0', dipy=False) if output not in ('nifti', 'nifti1', 'nifti2'): raise ValueError("invalid output specifier %s. Must be 'nifti1' or" " 'nifti2'" % output) if output in ('nifti', 'nifti1'): from nibabel import (Nifti1Image as NiftiImage, Nifti1Header as NiftiHeader) else: assert output == 'nifti2' from nibabel import (Nifti2Image as NiftiImage, Nifti2Header as NiftiHeader) assert morph.kind == 'volume' voxel_size_defined = False if isinstance(mri_resolution, (int, float)) and not isinstance( mri_resolution, bool): # use iso voxel size mri_resolution = (float(mri_resolution),) * 3 if isinstance(mri_resolution, tuple): _check_dep(nibabel=False, dipy='0.10.1') # nibabel was already checked from dipy.align.reslice import reslice voxel_size = mri_resolution voxel_size_defined = True mri_resolution = True # if data wasn't morphed yet - necessary for call of # stc_unmorphed.as_volume. Since only the shape of src is known, it cannot # be resliced to a given voxel size without knowing the original. if isinstance(morph, SourceSpaces): assert morph.kind == 'volume' if voxel_size_defined: raise ValueError( "Cannot infer original voxel size for reslicing... " "set mri_resolution to boolean value or apply morph first.") from mne.io.constants import BunchConst morph = BunchConst(src_data=_get_src_data(morph)[0]) # setup volume parameters n_times = stc.data.shape[1] shape3d = morph.src_data['src_shape'] shape = (n_times,) + shape3d vols = np.zeros(shape) mask3d = morph.src_data['inuse'].reshape(shape3d).astype(np.bool) n_vertices = np.sum(mask3d) n_vertices_seen = 0 for k, vol in enumerate(vols): # loop over time instants stc_slice = slice(n_vertices_seen, n_vertices_seen + n_vertices) vol[mask3d] = stc.data[stc_slice, k] n_vertices_seen += n_vertices # use mri resolution as represented in src if mri_resolution: mri_shape3d = morph.src_data['src_shape_full'] mri_shape = (n_times,) + mri_shape3d mri_vol = np.zeros(mri_shape) interpolator = morph.src_data['interpolator'] for k, vol in enumerate(vols): mri_vol[k] = (interpolator * vol.ravel()).reshape(mri_shape3d) vols = mri_vol vols = vols.T # set correct space affine = morph.src_data['src_affine_vox'] if not mri_resolution: affine = morph.src_data['src_affine_src'] if mri_space: affine = np.dot(morph.src_data['src_affine_ras'], affine) affine[:3] *= 1e3 # pre-define header header = NiftiHeader() header.set_xyzt_units('mm', 'msec') header['pixdim'][4] = 1e3 * stc.tstep with warnings.catch_warnings(): # nibabel<->numpy warning img = NiftiImage(vols, affine, header=header) # if a specific voxel size was targeted (only possible after morphing) if voxel_size_defined: # reslice mri img, img_affine = reslice( img.get_data(), img.affine, _get_zooms_orig(morph), voxel_size) with warnings.catch_warnings(): # nibabel<->numpy warning img = NiftiImage(img, img_affine, header=header) return img
def _morphed_stc_as_volume(morph, stc, mri_resolution=False, mri_space=True, output='nifti1'): """Return volume source space as Nifti1Image and/or save to disk.""" if not isinstance(stc, VolSourceEstimate): raise ValueError('Only volume source estimates can be converted to ' 'volumes') _check_dep(nibabel='2.1.0', dipy=False) known_types = ('nifti', 'nifti1', 'nifti2') if output not in known_types: raise ValueError('output must be one of %s, got %s' % (known_types, output)) if output in ('nifti', 'nifti1'): from nibabel import (Nifti1Image as NiftiImage, Nifti1Header as NiftiHeader) else: assert output == 'nifti2' from nibabel import (Nifti2Image as NiftiImage, Nifti2Header as NiftiHeader) new_zooms = None # if full MRI resolution, compute zooms from shape and MRI zooms if isinstance(mri_resolution, bool) and mri_resolution: new_zooms = _get_zooms_orig(morph) # if MRI resolution is set manually as a single value, convert to tuple if isinstance(mri_resolution, (int, float)) and not isinstance( mri_resolution, bool): # use iso voxel size new_zooms = (float(mri_resolution),) * 3 # if MRI resolution is set manually as a tuple, use it if isinstance(mri_resolution, tuple): new_zooms = mri_resolution # create header hdr = NiftiHeader() hdr.set_xyzt_units('mm', 'msec') hdr['pixdim'][4] = 1e3 * stc.tstep # setup empty volume img = np.zeros(morph.shape + (stc.shape[1],)).reshape(-1, stc.shape[1]) img[stc.vertices, :] = stc.data img = img.reshape(morph.shape + (-1,)) # make nifti from data with warnings.catch_warnings(): # nibabel<->numpy warning img = NiftiImage(img, morph.affine, header=hdr) # reslice in case of manually defined voxel size zooms = morph.zooms[:3] if new_zooms is not None: from dipy.align.reslice import reslice new_zooms = new_zooms[:3] img, affine = reslice(img.get_data(), img.affine, # MRI to world registration zooms, # old voxel size in mm new_zooms) # new voxel size in mm with warnings.catch_warnings(): # nibabel<->numpy warning img = NiftiImage(img, affine) zooms = new_zooms # set zooms in header img.header.set_zooms(tuple(zooms) + (1,)) return img
def save_ics(icas, mask, threshold, output_dir, header, mean=None): """ Save the independant compnents to Nifti. Parameters ----------- icas: 2D ndarray The independant components, as returned by CanICA. mask: 3D ndarray The 3D boolean array used to go from index to voxel space for each IC. threshold: float The threshold value used to threshold the ICs output_dir: string The directory in which maps and nifti files will be saved. header: dictionnary or Nifti1Header object The header for the nifti file, as returned by pynifti's header attribute, or nipy.io.imageformats.load().get_header(). """ # put in order n_ic, n_voxels icas = icas.T mask = mask.astype(np.bool) try: affine = header.get_best_affine() except: affine = header['sform'] # XXX: I don't know how to convert a dictionnary to a # Nifti1Header. h = Nifti1Header() for k, v in header.iteritems(): try: h[k] = v except ValueError: pass header = h header['cal_max'] = np.nanmax(icas) header['cal_min'] = np.nanmin(icas) if not os.path.exists(output_dir): os.mkdir(output_dir) icas = icas.copy() for ic in icas: ic *= auto_sign(ic, threshold=threshold) maps3d = np.zeros(list(mask.shape) + [len(icas)]) maps3d[mask] = icas.T save(Nifti1Image(maps3d, affine, header=header), pjoin(output_dir, 'icas_no_thr.nii')) maps3d[np.abs(maps3d) < threshold] = 0 save(Nifti1Image(maps3d, affine, header=header), pjoin(output_dir, 'icas.nii')) # Save the mask mask_header = header.copy() mask_header['cal_min'] = 0 mask_header['cal_max'] = 1 save(Nifti1Image(mask.astype(np.int), affine, header=mask_header), pjoin(output_dir, 'mask.nii')) if mean is not None: # save the mean mean_img = np.zeros(maps3d.shape[:-1]) mean_img[mask] = mean mean_header = header.copy() mean_header['cal_min'] = mean.min() mean_header['cal_max'] = mean.max() save(Nifti1Image(mean_img, affine, header=mean_header), pjoin(output_dir, 'mean.nii')) return maps3d, affine, mean_img return maps3d, affine
def _interpolate_data(stc, morph, mri_resolution, mri_space, output): """Interpolate source estimate data to MRI.""" _check_dep(nibabel='2.1.0', dipy=False) NiftiImage, NiftiHeader = _triage_output(output) assert morph.kind == 'volume' voxel_size_defined = False if isinstance(mri_resolution, (int, float)) and not isinstance( mri_resolution, bool): # use iso voxel size mri_resolution = (float(mri_resolution),) * 3 if isinstance(mri_resolution, tuple): _check_dep(nibabel=False, dipy='0.10.1') # nibabel was already checked from dipy.align.reslice import reslice voxel_size = mri_resolution voxel_size_defined = True mri_resolution = True # if data wasn't morphed yet - necessary for call of # stc_unmorphed.as_volume. Since only the shape of src is known, it cannot # be resliced to a given voxel size without knowing the original. if isinstance(morph, SourceSpaces): assert morph.kind == 'volume' if voxel_size_defined: raise ValueError( "Cannot infer original voxel size for reslicing... " "set mri_resolution to boolean value or apply morph first.") # Now deal with the fact that we may have multiple sub-volumes inuse = [morph[k]['inuse'] for k in range(len(morph))] src_shape = [morph[k]['shape'] for k in range(len(morph))] assert len(set(map(tuple, src_shape))) == 1 morph = BunchConst(src_data=_get_src_data(morph)[0]) else: # Make a list as we may have many inuse when using multiple sub-volumes inuse = [morph.src_data['inuse']] n_times = stc.data.shape[1] shape = morph.src_data['src_shape'][::-1] + (n_times,) # SAR->RAST vols = np.zeros((np.prod(shape[:3]), shape[3]), order='F') # flatten n_vertices_seen = 0 for this_inuse in inuse: this_inuse = this_inuse.astype(np.bool) n_vertices = np.sum(this_inuse) stc_slice = slice(n_vertices_seen, n_vertices_seen + n_vertices) vols[this_inuse] = stc.data[stc_slice] n_vertices_seen += n_vertices # use mri resolution as represented in src if mri_resolution: shape = morph.src_data['src_shape_full'][::-1] + (n_times,) vols = _csr_dot( morph.src_data['interpolator'], vols, np.zeros((np.prod(shape[:3]), shape[3]), order='F')) # reshape back to proper shape vols = np.reshape(vols, shape, order='F') # set correct space if mri_resolution: affine = morph.src_data['src_affine_vox'] else: affine = morph.src_data['src_affine_src'] if mri_space: affine = np.dot(morph.src_data['src_affine_ras'], affine) affine[:3] *= 1e3 # pre-define header header = NiftiHeader() header.set_xyzt_units('mm', 'msec') header['pixdim'][4] = 1e3 * stc.tstep with warnings.catch_warnings(): # nibabel<->numpy warning img = NiftiImage(vols, affine, header=header) # if a specific voxel size was targeted (only possible after morphing) if voxel_size_defined: # reslice mri img, img_affine = reslice( _get_img_fdata(img), img.affine, _get_zooms_orig(morph), voxel_size) with warnings.catch_warnings(): # nibabel<->numpy warning img = NiftiImage(img, img_affine, header=header) return img
def _interpolate_data(stc, morph, mri_resolution=True, mri_space=True, output='nifti1'): """Interpolate source estimate data to MRI.""" _check_dep(nibabel='2.1.0', dipy=False) if output not in ('nifti', 'nifti1', 'nifti2'): raise ValueError("invalid output specifier %s. Must be 'nifti1' or" " 'nifti2'" % output) if output in ('nifti', 'nifti1'): from nibabel import (Nifti1Image as NiftiImage, Nifti1Header as NiftiHeader) else: assert output == 'nifti2' from nibabel import (Nifti2Image as NiftiImage, Nifti2Header as NiftiHeader) assert morph.kind == 'volume' voxel_size_defined = False if isinstance(mri_resolution, (int, float)) and not isinstance( mri_resolution, bool): # use iso voxel size mri_resolution = (float(mri_resolution),) * 3 if isinstance(mri_resolution, tuple): _check_dep(nibabel=False, dipy='0.10.1') # nibabel was already checked from dipy.align.reslice import reslice voxel_size = mri_resolution voxel_size_defined = True mri_resolution = True # if data wasn't morphed yet - necessary for call of # stc_unmorphed.as_volume. Since only the shape of src is known, it cannot # be resliced to a given voxel size without knowing the original. if isinstance(morph, SourceSpaces): assert morph.kind == 'volume' if voxel_size_defined: raise ValueError( "Cannot infer original voxel size for reslicing... " "set mri_resolution to boolean value or apply morph first.") from mne.io.constants import BunchConst # Now deal with the fact that we may have multiple sub-volumes inuse = [morph[k]['inuse'] for k in range(len(morph))] src_shape = [morph[k]['shape'] for k in range(len(morph))] assert len(set(map(tuple, src_shape))) == 1 morph = BunchConst(src_data=_get_src_data(morph)[0]) else: # Make a list as we may have many inuse when using multiple sub-volumes inuse = [morph.src_data['inuse']] shape3d = morph.src_data['src_shape'] # setup volume parameters n_times = stc.data.shape[1] shape = (n_times,) + shape3d vols = np.zeros(shape) n_vertices_seen = 0 for this_inuse in inuse: mask3d = this_inuse.reshape(shape3d).astype(np.bool) n_vertices = np.sum(mask3d) stc_slice = slice(n_vertices_seen, n_vertices_seen + n_vertices) for k, vol in enumerate(vols): # loop over time instants vol[mask3d] = stc.data[stc_slice, k] n_vertices_seen += n_vertices # use mri resolution as represented in src if mri_resolution: mri_shape3d = morph.src_data['src_shape_full'] mri_shape = (n_times,) + mri_shape3d mri_vol = np.zeros(mri_shape) interpolator = morph.src_data['interpolator'] for k, vol in enumerate(vols): mri_vol[k] = (interpolator * vol.ravel()).reshape(mri_shape3d) vols = mri_vol vols = vols.T # set correct space affine = morph.src_data['src_affine_vox'] if not mri_resolution: affine = morph.src_data['src_affine_src'] if mri_space: affine = np.dot(morph.src_data['src_affine_ras'], affine) affine[:3] *= 1e3 # pre-define header header = NiftiHeader() header.set_xyzt_units('mm', 'msec') header['pixdim'][4] = 1e3 * stc.tstep with warnings.catch_warnings(): # nibabel<->numpy warning img = NiftiImage(vols, affine, header=header) # if a specific voxel size was targeted (only possible after morphing) if voxel_size_defined: # reslice mri img, img_affine = reslice( img.get_data(), img.affine, _get_zooms_orig(morph), voxel_size) with warnings.catch_warnings(): # nibabel<->numpy warning img = NiftiImage(img, img_affine, header=header) return img