def to_image(self, path=None, data=None): """Write itself as a binary image, and returns it Parameters ---------- path: string, path of the output image, if any data: array of shape self.size, data to put in the nonzer-region of the image """ if data is None: wdata = np.zeros(self.shape, np.int8) else: wdata = np.zeros(self.shape, data.dtype) wdata[self.ijk[:, 0], self.ijk[:, 1], self.ijk[:, 2]] = 1 if data is not None: if data.size != self.size: raise ValueError('incorrect data size') wdata[wdata > 0] = data nim = Nifti1Image(wdata, self.affine) get_header(nim)['descrip'] = ('mask image representing domain %s' % self.id) if path is not None: save(nim, path) return nim
def parcellation_based_analysis(Pa, test_images, test_id='one_sample', rfx_path=None, condition_id='', swd=None): """ This function computes parcel averages and RFX at the parcel-level Parameters ---------- Pa: MultiSubjectParcellation instance the description of the parcellation test_images: (Pa.nb_subj-) list of paths paths of images used in the inference procedure test_id: string, optional, if test_id=='one_sample', the one_sample statstic is computed otherwise, the parcel-based signal averages are returned rfx_path: string optional, path of the resulting one-sample test image, if applicable swd: string, optional output directory used to compute output path if rfx_path is not given condition_id: string, optional, contrast/condition id used to compute output path Returns ------- test_data: array of shape(Pa.nb_parcel, Pa.nb_subj) the parcel-level signal average if test is not 'one_sample' prfx: array of shape(Pa.nb_parcel), the one-sample t-value if test_id is 'one_sample' """ nb_subj = Pa.nb_subj # 1. read the test data if len(test_images) != nb_subj: raise ValueError('Inconsistent number of test images') test = np.array( [Pa.domain.make_feature_from_image(ti) for ti in test_images]).T test_data = Pa.make_feature('', np.array(test)) if test_id is not 'one_sample': return test_data # 2. perform one-sample test # computation from ..utils.reproducibility_measures import ttest prfx = ttest(test_data) # Write the stuff template = SubDomains(Pa.domain, Pa.template_labels) template.set_roi_feature('prfx', prfx) wim = template.to_image('prfx', roi=True) hdr = get_header(wim) hdr['descrip'] = 'parcel-based random effects image (in t-variate)' if rfx_path is not None: save(wim, rfx_path) return prfx
def parcellation_based_analysis(Pa, test_images, test_id='one_sample', rfx_path=None, condition_id='', swd=None): """ This function computes parcel averages and RFX at the parcel-level Parameters ---------- Pa: MultiSubjectParcellation instance the description of the parcellation test_images: (Pa.nb_subj-) list of paths paths of images used in the inference procedure test_id: string, optional, if test_id=='one_sample', the one_sample statstic is computed otherwise, the parcel-based signal averages are returned rfx_path: string optional, path of the resulting one-sample test image, if applicable swd: string, optional output directory used to compute output path if rfx_path is not given condition_id: string, optional, contrast/condition id used to compute output path Returns ------- test_data: array of shape(Pa.nb_parcel, Pa.nb_subj) the parcel-level signal average if test is not 'one_sample' prfx: array of shape(Pa.nb_parcel), the one-sample t-value if test_id is 'one_sample' """ nb_subj = Pa.nb_subj # 1. read the test data if len(test_images) != nb_subj: raise ValueError('Inconsistent number of test images') test = np.array([Pa.domain.make_feature_from_image(ti) for ti in test_images]).T test_data = Pa.make_feature('', np.array(test)) if test_id is not 'one_sample': return test_data # 2. perform one-sample test # computation from ..utils.reproducibility_measures import ttest prfx = ttest(test_data) # Write the stuff template = SubDomains(Pa.domain, Pa.template_labels) template.set_roi_feature('prfx', prfx) wim = template.to_image('prfx', roi=True) hdr = get_header(wim) hdr['descrip'] = 'parcel-based random effects image (in t-variate)' if rfx_path is not None: save(wim, rfx_path) return prfx
def to_image(self, fid=None, roi=False, method="mean", descrip=None): """Generates a label image that represents self. Parameters ---------- fid: str, Feature to be represented. If None, a binary image of the MROI domain will be we created. roi: bool, Whether or not to write the desired feature as a ROI one. (i.e. a ROI feature corresponding to `fid` will be looked upon, and if not found, a representative feature will be computed from the `fid` feature). method: str, If a feature is written as a ROI feature, this keyword tweaks the way the representative feature is computed. descrip: str, Description of the image, to be written in its header. Notes ----- Requires that self.dom is an ddom.NDGridDomain Returns ------- nim : nibabel nifti image Nifti image corresponding to the ROI feature to be written. """ if not isinstance(self.domain, ddom.NDGridDomain): print('self.domain is not an NDGridDomain; nothing was written.') return None if fid is None: # write a binary representation of the domain if no fid provided nim = self.domain.to_image(data=(self.label != -1).astype(np.int32)) if descrip is None: descrip = 'binary representation of MROI' else: data = -np.ones(self.label.size, dtype=np.int32) tmp_image = self.domain.to_image() mask = tmp_image.get_data().copy().astype(bool) if not roi: # write a feature if fid not in self.features: raise ValueError("`%s` feature could not be found" % fid) for i in self.get_id(): data[self.select_id(i, roi=False)] = \ self.get_feature(fid, i) else: # write a roi feature if fid in self.roi_features: # write from existing roi feature for i in self.get_id(): data[self.select_id(i, roi=False)] = \ self.get_roi_feature( fid, i) elif fid in self.features: # write from representative feature summary_feature = self.representative_feature( fid, method=method) for i in self.get_id(): data[self.select_id(i, roi=False)] = \ summary_feature[self.select_id(i)] # MROI object was defined on a masked image: we square it back. wdata = -np.ones(mask.shape, data.dtype) wdata[mask] = data nim = Nifti1Image(wdata, get_affine(tmp_image)) # set description of the image if descrip is not None: get_header(nim)['descrip'] = descrip return nim
def contrast( self, contrasts, con_id="", contrast_type=None, output_z=True, output_stat=False, output_effects=False, output_variance=False, ): """ Estimation of a contrast as fixed effects on all sessions Parameters ---------- contrasts : array or list of arrays of shape (n_col) or (n_dim, n_col) where ``n_col`` is the number of columns of the design matrix, numerical definition of the contrast (one array per run) con_id : str, optional name of the contrast contrast_type : {'t', 'F', 'tmin-conjunction'}, optional type of the contrast output_z : bool, optional Return or not the corresponding z-stat image output_stat : bool, optional Return or not the base (t/F) stat image output_effects : bool, optional Return or not the corresponding effect image output_variance : bool, optional Return or not the corresponding variance image Returns ------- output_images : list of nibabel images The required output images, in the following order: z image, stat(t/F) image, effects image, variance image """ if self.glms == []: raise ValueError("first run fit() to estimate the model") if isinstance(contrasts, np.ndarray): contrasts = [contrasts] if len(contrasts) != len(self.glms): raise ValueError("contrasts must be a sequence of %d session contrasts" % len(self.glms)) contrast_ = None for i, (glm, con) in enumerate(zip(self.glms, contrasts)): if np.all(con == 0): warn("Contrast for session %d is null" % i) elif contrast_ is None: contrast_ = glm.contrast(con, contrast_type) else: contrast_ = contrast_ + glm.contrast(con, contrast_type) if output_z or output_stat: # compute the contrast and stat contrast_.z_score() # Prepare the returned images mask = self.mask.get_data().astype(np.bool) do_outputs = [output_z, output_stat, output_effects, output_variance] estimates = ["z_score_", "stat_", "effect", "variance"] descrips = ["z statistic", "Statistical value", "Estimated effect", "Estimated variance"] dims = [1, 1, contrast_.dim, contrast_.dim ** 2] n_vox = mask.sum() output_images = [] for (do_output, estimate, descrip, dim) in zip(do_outputs, estimates, descrips, dims): if do_output: if dim > 1: result_map = np.tile(mask.astype(np.float)[:, :, :, np.newaxis], dim) result_map[mask] = np.reshape(getattr(contrast_, estimate).T, (n_vox, dim)) else: result_map = mask.astype(np.float) result_map[mask] = np.squeeze(getattr(contrast_, estimate)) output = Nifti1Image(result_map, self.affine) get_header(output)["descrip"] = "%s associated with contrast %s" % (descrip, con_id) output_images.append(output) return output_images
def as_volume_img(obj, copy=True, squeeze=True, world_space=None): """ Convert the input to a VolumeImg. Parameters ---------- obj : filename, pynifti or brifti object, or volume dataset. Input object, in any form that can be converted to a VolumeImg. This includes Nifti filenames, pynifti or brifti objects, or other volumetric dataset objects. copy: boolean, optional If copy is True, the data and affine arrays are copied, elsewhere a view is taken. squeeze: boolean, optional If squeeze is True, the data array is squeeze on for dimensions above 3. world_space: string or None, optional An optional specification of the world space, to override that given by the image. Returns ------- volume_img: VolumeImg object A VolumeImg object containing the data. The metadata is kept as much as possible in the metadata attribute. Notes ------ The world space might not be correctly defined by the input object (in particular, when loading data from disk). In this case, you can correct it manually using the world_space keyword argument. For pynifti objects, the data is transposed. """ if hasattr(obj, 'as_volume_img'): obj = obj.as_volume_img(copy=copy) if copy: obj = obj.__copy__() return obj elif isinstance(obj, basestring): if not os.path.exists(obj): raise ValueError("The file '%s' cannot be found" % obj) obj = nib.load(obj) copy = False if isinstance(obj, SpatialImage): data = obj.get_data() affine = get_affine(obj) header = dict(get_header(obj)) fname = obj.file_map['image'].filename if fname: header['filename'] = fname elif hasattr(obj, 'data') and hasattr(obj, 'sform') and \ hasattr(obj, 'getVolumeExtent'): # Duck-types to a pynifti object data = obj.data.T affine = obj.sform header = obj.header filename = obj.getFilename() if filename != '': header['filename'] = filename else: raise ValueError('Invalid type (%s) passed in: cannot convert %s to ' 'VolumeImg' % (type(obj), obj)) if world_space is None and header.get('sform_code', 0) == 4: world_space = 'mni152' data = np.asanyarray(data) affine = np.asanyarray(affine) if copy: data = data.copy() affine = affine.copy() if squeeze: # Squeeze the dimensions above 3 shape = [ val for index, val in enumerate(data.shape) if val != 1 or index < 3 ] data = np.reshape(data, shape) return VolumeImg(data, affine, world_space, metadata=header)
def as_volume_img(obj, copy=True, squeeze=True, world_space=None): """ Convert the input to a VolumeImg. Parameters ---------- obj : filename, pynifti or brifti object, or volume dataset. Input object, in any form that can be converted to a VolumeImg. This includes Nifti filenames, pynifti or brifti objects, or other volumetric dataset objects. copy: boolean, optional If copy is True, the data and affine arrays are copied, elsewhere a view is taken. squeeze: boolean, optional If squeeze is True, the data array is squeeze on for dimensions above 3. world_space: string or None, optional An optional specification of the world space, to override that given by the image. Returns ------- volume_img: VolumeImg object A VolumeImg object containing the data. The metadata is kept as much as possible in the metadata attribute. Notes ------ The world space might not be correctly defined by the input object (in particular, when loading data from disk). In this case, you can correct it manually using the world_space keyword argument. For pynifti objects, the data is transposed. """ if hasattr(obj, 'as_volume_img'): obj = obj.as_volume_img(copy=copy) if copy: obj = obj.__copy__() return obj elif isinstance(obj, string_types): if not os.path.exists(obj): raise ValueError("The file '%s' cannot be found" % obj) obj = nib.load(obj) copy = False if isinstance(obj, SpatialImage): data = obj.get_data() affine = get_affine(obj) header = dict(get_header(obj)) fname = obj.file_map['image'].filename if fname: header['filename'] = fname elif hasattr(obj, 'data') and hasattr(obj, 'sform') and \ hasattr(obj, 'getVolumeExtent'): # Duck-types to a pynifti object data = obj.data.T affine = obj.sform header = obj.header filename = obj.getFilename() if filename != '': header['filename'] = filename else: raise ValueError('Invalid type (%s) passed in: cannot convert %s to ' 'VolumeImg' % (type(obj), obj)) if world_space is None and header.get('sform_code', 0) == 4: world_space = 'mni152' data = np.asanyarray(data) affine = np.asanyarray(affine) if copy: data = data.copy() affine = affine.copy() if squeeze: # Squeeze the dimensions above 3 shape = [val for index, val in enumerate(data.shape) if val !=1 or index < 3] data = np.reshape(data, shape) return VolumeImg(data, affine, world_space, metadata=header)
def to_image(self, fid=None, roi=False, method="mean", descrip=None): """Generates a label image that represents self. Parameters ---------- fid: str, Feature to be represented. If None, a binary image of the MROI domain will be we created. roi: bool, Whether or not to write the desired feature as a ROI one. (i.e. a ROI feature corresponding to `fid` will be looked upon, and if not found, a representative feature will be computed from the `fid` feature). method: str, If a feature is written as a ROI feature, this keyword tweaks the way the representative feature is computed. descrip: str, Description of the image, to be written in its header. Notes ----- Requires that self.dom is an ddom.NDGridDomain Returns ------- nim : nibabel nifti image Nifti image corresponding to the ROI feature to be written. """ if not isinstance(self.domain, ddom.NDGridDomain): print('self.domain is not an NDGridDomain; nothing was written.') return None if fid is None: # write a binary representation of the domain if no fid provided nim = self.domain.to_image( data=(self.label != -1).astype(np.int32)) if descrip is None: descrip = 'binary representation of MROI' else: data = -np.ones(self.label.size, dtype=np.int32) tmp_image = self.domain.to_image() mask = tmp_image.get_data().copy().astype(bool) if not roi: # write a feature if fid not in self.features: raise ValueError("`%s` feature could not be found" % fid) for i in self.get_id(): data[self.select_id(i, roi=False)] = \ self.get_feature(fid, i) else: # write a roi feature if fid in self.roi_features: # write from existing roi feature for i in self.get_id(): data[self.select_id(i, roi=False)] = \ self.get_roi_feature( fid, i) elif fid in self.features: # write from representative feature summary_feature = self.representative_feature( fid, method=method) for i in self.get_id(): data[self.select_id(i, roi=False)] = \ summary_feature[self.select_id(i)] # MROI object was defined on a masked image: we square it back. wdata = -np.ones(mask.shape, data.dtype) wdata[mask] = data nim = Nifti1Image(wdata, get_affine(tmp_image)) # set description of the image if descrip is not None: get_header(nim)['descrip'] = descrip return nim
def contrast(self, contrasts, con_id='', contrast_type=None, output_z=True, output_stat=False, output_effects=False, output_variance=False): """ Estimation of a contrast as fixed effects on all sessions Parameters ---------- contrasts : array or list of arrays of shape (n_col) or (n_dim, n_col) where ``n_col`` is the number of columns of the design matrix, numerical definition of the contrast (one array per run) con_id : str, optional name of the contrast contrast_type : {'t', 'F', 'tmin-conjunction'}, optional type of the contrast output_z : bool, optional Return or not the corresponding z-stat image output_stat : bool, optional Return or not the base (t/F) stat image output_effects : bool, optional Return or not the corresponding effect image output_variance : bool, optional Return or not the corresponding variance image Returns ------- output_images : list of nibabel images The required output images, in the following order: z image, stat(t/F) image, effects image, variance image """ if self.glms == []: raise ValueError('first run fit() to estimate the model') if isinstance(contrasts, np.ndarray): contrasts = [contrasts] if len(contrasts) != len(self.glms): raise ValueError( 'contrasts must be a sequence of %d session contrasts' % len(self.glms)) contrast_ = None for i, (glm, con) in enumerate(zip(self.glms, contrasts)): if np.all(con == 0): warn('Contrast for session %d is null' % i) elif contrast_ is None: contrast_ = glm.contrast(con, contrast_type) else: contrast_ = contrast_ + glm.contrast(con, contrast_type) if output_z or output_stat: # compute the contrast and stat contrast_.z_score() # Prepare the returned images mask = self.mask.get_data().astype(np.bool) do_outputs = [output_z, output_stat, output_effects, output_variance] estimates = ['z_score_', 'stat_', 'effect', 'variance'] descrips = [ 'z statistic', 'Statistical value', 'Estimated effect', 'Estimated variance' ] dims = [1, 1, contrast_.dim, contrast_.dim**2] n_vox = mask.sum() output_images = [] for (do_output, estimate, descrip, dim) in zip(do_outputs, estimates, descrips, dims): if do_output: if dim > 1: result_map = np.tile( mask.astype(np.float)[:, :, :, np.newaxis], dim) result_map[mask] = np.reshape( getattr(contrast_, estimate).T, (n_vox, dim)) else: result_map = mask.astype(np.float) result_map[mask] = np.squeeze(getattr(contrast_, estimate)) output = Nifti1Image(result_map, self.affine) get_header(output)['descrip'] = ( '%s associated with contrast %s' % (descrip, con_id)) output_images.append(output) return output_images