From ea176bc866d019cdf98800b80b503831b606e08b Mon Sep 17 00:00:00 2001 From: Oscar Esteban Date: Mon, 5 Aug 2013 14:10:25 +0200 Subject: [PATCH 1/7] Base class for FuzzyOverlap misc tool Some details are still to be fixed --- nipype/algorithms/misc.py | 89 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index 1255c1d6f6..34f2ed9758 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -437,6 +437,95 @@ def _list_outputs(self): return outputs +class FuzzyOverlapInputSpec(BaseInterfaceInputSpec): + in_ref = InputMultiPath( File(exists=True), mandatory=True, + desc="Reference image. Requires the same dimensions as in_tst.") + in_tst = InputMultiPath( File(exists=True), mandatory=True, + desc="Test image. Requires the same dimensions as in_ref.") + mask_volume = File( exists=True, desc="calculate overlap only within this mask.") + weighting = traits.Enum("none", "volume", desc='""none": no class-overlap weighting is performed\ + "volume": computed class-overlaps are weighted by class volume',usedefault=True) + out_file = File("diff.nii", usedefault=True) + + +class FuzzyOverlapOutputSpec(TraitedSpec): + jaccard = traits.Float() + dice = traits.Float() + diff_file = File(exists=True) + + +class FuzzyOverlap(BaseInterface): + """ + Calculates various overlap measures between two maps, using the fuzzy definition + proposed in: Crum et al., Generalized Overlap Measures for Evaluation and Validation + in Medical Image Analysis, IEEE Trans. Med. Ima. 25(11),pp 1451-1461, Nov. 2006. + + reference.nii and test.nii are lists of 2/3D images, each element on the list containing + one volume fraction map of a class in a fuzzy partition of the domain. + + Example + ------- + + >>> overlap = FuzzyOverlap() + >>> overlap.inputs.in_ref = [ 'ref_class0.nii', 'ref_class1.nii', 'ref_class2.nii' ] + >>> overlap.inputs.in_tst = [ 'tst_class0.nii', 'tst_class1.nii', 'tst_class2.nii' ] + >>> overlap.inputs.weighting = 'volume' + >>> res = overlap.run() # doctest: +SKIP + """ + + input_spec = FuzzyOverlapInputSpec + output_spec = FuzzyOverlapOutputSpec + + def _bool_vec_dissimilarity(self, booldata1, booldata2, method): + methods = {"dice": dice, "jaccard": jaccard} + if not (np.any(booldata1) or np.any(booldata2)): + return 0 + return 1 - methods[method](booldata1.flat, booldata2.flat) + + def _run_interface(self, runtime): + ncomp = len(self.inputs.in_ref) + assert( ncomp == len(self.inputs.in_tst) ) + weights = np.ones( shape=ncomp ) + + img_ref = np.array( [ nib.load( fname ).get_data() for fname in self.inputs.in_ref ] ) + img_tst = np.array( [ nib.load( fname ).get_data() for fname in self.inputs.in_tst ] ) + + #check that volumes are normalized + img_ref = img_ref / np.sum( img_ref, axis=0 ) + img_tst = img_tst / np.sum( img_tst, axis=0 ) + + num = float( np.minimum( img_ref, img_test ) ) + ddr = float( np.maximum( img_ref, img_test ) ) + both_data = num/ddr + + jaccards = np.sum( num, axis=0 ) / np.sum( ddr, axis=0 ) + dices = 2.0*jaccards / (1.0+jaccards ) + + if self.inputs.weighting != "none": + weights = 1.0 / np.sum( img_ref, axis= 0 ) + + if self.inputs.weighting == "squared_vol": + weights = weights**2 + + setattr( self, '_jaccard', np.sum( weights * jaccards ) / np.sum( weights ) ) + setattr( self, '_dice', np.sum( weights * dices ) / np.sum( weights ) ) + + # todo, this is a N+1 dimensional file, update header and affine. + nb.save(nb.Nifti1Image(both_data, nii1.get_affine(), + nii1.get_header()), self.inputs.out_file) + + return runtime + + def _list_outputs(self): + outputs = self._outputs().get() + for method in ("dice", "jaccard"): + outputs[method] = getattr(self, '_' + method) + #outputs['volume_difference'] = self._volume + outputs['diff_file'] = os.path.abspath(self.inputs.out_file) + return outputs + + + class CreateNiftiInputSpec(BaseInterfaceInputSpec): data_file = File(exists=True, mandatory=True, desc="ANALYZE img file") header_file = File( From 52721c28e838f555e297c531e272f06dd957ff08 Mon Sep 17 00:00:00 2001 From: Oscar Esteban Date: Mon, 5 Aug 2013 20:03:48 +0200 Subject: [PATCH 2/7] Working version --- nipype/algorithms/misc.py | 73 ++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index 34f2ed9758..e9ea6785a4 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -442,9 +442,9 @@ class FuzzyOverlapInputSpec(BaseInterfaceInputSpec): desc="Reference image. Requires the same dimensions as in_tst.") in_tst = InputMultiPath( File(exists=True), mandatory=True, desc="Test image. Requires the same dimensions as in_ref.") - mask_volume = File( exists=True, desc="calculate overlap only within this mask.") - weighting = traits.Enum("none", "volume", desc='""none": no class-overlap weighting is performed\ - "volume": computed class-overlaps are weighted by class volume',usedefault=True) + weighting = traits.Enum("none", "volume", "squared_vol", desc='""none": no class-overlap weighting is performed\ + "volume": computed class-overlaps are weighted by class volume\ + "squared_vol": computed class-overlaps are weighted by the squared volume of the class',usedefault=True) out_file = File("diff.nii", usedefault=True) @@ -452,6 +452,8 @@ class FuzzyOverlapOutputSpec(TraitedSpec): jaccard = traits.Float() dice = traits.Float() diff_file = File(exists=True) + class_ji = traits.List( traits.Float() ) + class_dsc = traits.List( traits.Float() ) class FuzzyOverlap(BaseInterface): @@ -476,44 +478,59 @@ class FuzzyOverlap(BaseInterface): input_spec = FuzzyOverlapInputSpec output_spec = FuzzyOverlapOutputSpec - def _bool_vec_dissimilarity(self, booldata1, booldata2, method): - methods = {"dice": dice, "jaccard": jaccard} - if not (np.any(booldata1) or np.any(booldata2)): - return 0 - return 1 - methods[method](booldata1.flat, booldata2.flat) - def _run_interface(self, runtime): ncomp = len(self.inputs.in_ref) assert( ncomp == len(self.inputs.in_tst) ) weights = np.ones( shape=ncomp ) - img_ref = np.array( [ nib.load( fname ).get_data() for fname in self.inputs.in_ref ] ) - img_tst = np.array( [ nib.load( fname ).get_data() for fname in self.inputs.in_tst ] ) + img_ref = np.array( [ nb.load( fname ).get_data() for fname in self.inputs.in_ref ] ) + img_tst = np.array( [ nb.load( fname ).get_data() for fname in self.inputs.in_tst ] ) + + + msk = np.sum(img_ref, axis=0) + msk[msk>0] = 1.0 + tst_msk = np.sum(img_tst, axis=0) + tst_msk[tst_msk>0] = 1.0 #check that volumes are normalized - img_ref = img_ref / np.sum( img_ref, axis=0 ) - img_tst = img_tst / np.sum( img_tst, axis=0 ) + #img_ref[:][msk>0] = img_ref[:][msk>0] / (np.sum( img_ref, axis=0 ))[msk>0] + #img_tst[tst_msk>0] = img_tst[tst_msk>0] / np.sum( img_tst, axis=0 )[tst_msk>0] - num = float( np.minimum( img_ref, img_test ) ) - ddr = float( np.maximum( img_ref, img_test ) ) - both_data = num/ddr - - jaccards = np.sum( num, axis=0 ) / np.sum( ddr, axis=0 ) - dices = 2.0*jaccards / (1.0+jaccards ) + self._jaccards = [] + volumes = [] + + diff_im = np.zeros( img_ref.shape ) + + for ref_comp, tst_comp, diff_comp in zip( img_ref, img_tst, diff_im ): + num = np.minimum( ref_comp, tst_comp ) + ddr = np.maximum( ref_comp, tst_comp ) + diff_comp[ddr>0]+= 1.0-(num[ddr>0]/ddr[ddr>0]) + self._jaccards.append( np.sum( num ) / np.sum( ddr ) ) + volumes.append( np.sum( ref_comp ) ) + + self._dices = 2.0*np.array(self._jaccards) / (np.array(self._jaccards) +1.0 ) if self.inputs.weighting != "none": - weights = 1.0 / np.sum( img_ref, axis= 0 ) + weights = 1.0 / np.array(volumes) + if self.inputs.weighting == "squared_vol": + weights = weights**2 + + weights = weights / np.sum( weights ) - if self.inputs.weighting == "squared_vol": - weights = weights**2 + setattr( self, '_jaccard', np.sum( weights * self._jaccards ) ) + setattr( self, '_dice', np.sum( weights * self._dices ) ) - setattr( self, '_jaccard', np.sum( weights * jaccards ) / np.sum( weights ) ) - setattr( self, '_dice', np.sum( weights * dices ) / np.sum( weights ) ) + + diff = np.zeros( diff_im[0].shape ) + + for w,ch in zip(weights,diff_im): + ch[msk==0] = 0 + diff+= w* ch - # todo, this is a N+1 dimensional file, update header and affine. - nb.save(nb.Nifti1Image(both_data, nii1.get_affine(), - nii1.get_header()), self.inputs.out_file) + nb.save(nb.Nifti1Image(diff, nb.load( self.inputs.in_ref[0]).get_affine(), + nb.load( self.inputs.in_ref[0]).get_header()), self.inputs.out_file ) + return runtime def _list_outputs(self): @@ -522,6 +539,8 @@ def _list_outputs(self): outputs[method] = getattr(self, '_' + method) #outputs['volume_difference'] = self._volume outputs['diff_file'] = os.path.abspath(self.inputs.out_file) + outputs['class_ji'] = np.array(self._jaccards).astype(float).tolist(); + outputs['class_dsc']= self._dices.astype(float).tolist(); return outputs From e1a6a18114949f39bdad6689dac9e2815bc715f6 Mon Sep 17 00:00:00 2001 From: Oscar Esteban Date: Tue, 6 Aug 2013 10:34:24 +0200 Subject: [PATCH 3/7] Added inputs&outputs descriptions PR #622 --- nipype/algorithms/misc.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index e9ea6785a4..151168e6a0 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -445,15 +445,15 @@ class FuzzyOverlapInputSpec(BaseInterfaceInputSpec): weighting = traits.Enum("none", "volume", "squared_vol", desc='""none": no class-overlap weighting is performed\ "volume": computed class-overlaps are weighted by class volume\ "squared_vol": computed class-overlaps are weighted by the squared volume of the class',usedefault=True) - out_file = File("diff.nii", usedefault=True) + out_file = File("diff.nii", desc="alternative name for resulting difference-map", usedefault=True) class FuzzyOverlapOutputSpec(TraitedSpec): - jaccard = traits.Float() - dice = traits.Float() - diff_file = File(exists=True) - class_ji = traits.List( traits.Float() ) - class_dsc = traits.List( traits.Float() ) + jaccard = traits.Float( desc="Fuzzy Jaccard Index (fJI), all the classes" ) + dice = traits.Float( desc="Fuzzy Dice Index (fDI), all the classes" ) + diff_file = File(exists=True, desc="resulting difference-map of all classes, using the chosen weighting" ) + class_fji = traits.List( traits.Float(), desc="Array containing the fJIs of each computed class" ) + class_fdi = traits.List( traits.Float(), desc="Array containing the fDIs of each computed class" ) class FuzzyOverlap(BaseInterface): @@ -539,8 +539,8 @@ def _list_outputs(self): outputs[method] = getattr(self, '_' + method) #outputs['volume_difference'] = self._volume outputs['diff_file'] = os.path.abspath(self.inputs.out_file) - outputs['class_ji'] = np.array(self._jaccards).astype(float).tolist(); - outputs['class_dsc']= self._dices.astype(float).tolist(); + outputs['class_fji'] = np.array(self._jaccards).astype(float).tolist(); + outputs['class_fdi']= self._dices.astype(float).tolist(); return outputs From 8f08e1c520689e947c7667ba1d202af65f1a63e0 Mon Sep 17 00:00:00 2001 From: Oscar Esteban Date: Mon, 12 Aug 2013 10:16:34 +0200 Subject: [PATCH 4/7] Run autopep8 on misc.py In-place run of the standard tool. Ref #662 --- nipype/algorithms/misc.py | 134 +++++++++++++++++++++----------------- 1 file changed, 73 insertions(+), 61 deletions(-) diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index 151168e6a0..f543f97997 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -55,6 +55,7 @@ class PickAtlasOutputSpec(TraitedSpec): class PickAtlas(BaseInterface): + ''' Returns ROI masks given an atlas and a list of labels. Supports dilation and left right masking (assuming the atlas is properly aligned). @@ -88,9 +89,9 @@ def _get_brodmann_area(self): for lab in labels: newdata[origdata == lab] = 1 if self.inputs.hemi == 'right': - newdata[floor(float(origdata.shape[0]) / 2):, :, :] = 0 + newdata[floor(float(origdata.shape[0]) / 2):, :,:] = 0 elif self.inputs.hemi == 'left': - newdata[:ceil(float(origdata.shape[0]) / 2), :, :] = 0 + newdata[:ceil(float(origdata.shape[0]) / 2), :,:] = 0 if self.inputs.dilation_size != 0: newdata = grey_dilation( @@ -162,6 +163,7 @@ class ModifyAffineOutputSpec(TraitedSpec): class ModifyAffine(BaseInterface): + ''' Left multiplies the affine matrix with a specified values. Saves the volume as a nifti file. ''' @@ -216,6 +218,7 @@ class DistanceOutputSpec(TraitedSpec): class Distance(BaseInterface): + ''' Calculates distance between two volumes. ''' @@ -231,7 +234,7 @@ def _find_border(self, data): def _get_coordinates(self, data, affine): if len(data.shape) == 4: - data = data[:, :, :, 0] + data = data[:, :,:, 0] indices = np.vstack(np.nonzero(data)) indices = np.vstack((indices, np.ones(indices.shape[1]))) coordinates = np.dot(affine, indices) @@ -251,7 +254,7 @@ def _eucl_min(self, nii1, nii2): dist_matrix = cdist(set1_coordinates.T, set2_coordinates.T) (point1, point2) = np.unravel_index( np.argmin(dist_matrix), dist_matrix.shape) - return (euclidean(set1_coordinates.T[point1, :], set2_coordinates.T[point2, :]), set1_coordinates.T[point1, :], set2_coordinates.T[point2, :]) + return (euclidean(set1_coordinates.T[point1, :], set2_coordinates.T[point2,:]), set1_coordinates.T[point1,:], set2_coordinates.T[point2,:]) def _eucl_cog(self, nii1, nii2): origdata1 = nii1.get_data().astype(np.bool) @@ -376,6 +379,7 @@ class OverlapOutputSpec(TraitedSpec): class Overlap(BaseInterface): + """ Calculates various overlap measures between two maps. @@ -438,25 +442,30 @@ def _list_outputs(self): class FuzzyOverlapInputSpec(BaseInterfaceInputSpec): - in_ref = InputMultiPath( File(exists=True), mandatory=True, + in_ref = InputMultiPath(File(exists=True), mandatory=True, desc="Reference image. Requires the same dimensions as in_tst.") - in_tst = InputMultiPath( File(exists=True), mandatory=True, + in_tst = InputMultiPath(File(exists=True), mandatory=True, desc="Test image. Requires the same dimensions as in_ref.") weighting = traits.Enum("none", "volume", "squared_vol", desc='""none": no class-overlap weighting is performed\ "volume": computed class-overlaps are weighted by class volume\ - "squared_vol": computed class-overlaps are weighted by the squared volume of the class',usedefault=True) - out_file = File("diff.nii", desc="alternative name for resulting difference-map", usedefault=True) + "squared_vol": computed class-overlaps are weighted by the squared volume of the class', usedefault=True) + out_file = File( + "diff.nii", desc="alternative name for resulting difference-map", usedefault=True) class FuzzyOverlapOutputSpec(TraitedSpec): - jaccard = traits.Float( desc="Fuzzy Jaccard Index (fJI), all the classes" ) - dice = traits.Float( desc="Fuzzy Dice Index (fDI), all the classes" ) - diff_file = File(exists=True, desc="resulting difference-map of all classes, using the chosen weighting" ) - class_fji = traits.List( traits.Float(), desc="Array containing the fJIs of each computed class" ) - class_fdi = traits.List( traits.Float(), desc="Array containing the fDIs of each computed class" ) + jaccard = traits.Float(desc="Fuzzy Jaccard Index (fJI), all the classes") + dice = traits.Float(desc="Fuzzy Dice Index (fDI), all the classes") + diff_file = File( + exists=True, desc="resulting difference-map of all classes, using the chosen weighting") + class_fji = traits.List( + traits.Float(), desc="Array containing the fJIs of each computed class") + class_fdi = traits.List( + traits.Float(), desc="Array containing the fDIs of each computed class") class FuzzyOverlap(BaseInterface): + """ Calculates various overlap measures between two maps, using the fuzzy definition proposed in: Crum et al., Generalized Overlap Measures for Evaluation and Validation @@ -475,76 +484,72 @@ class FuzzyOverlap(BaseInterface): >>> res = overlap.run() # doctest: +SKIP """ - input_spec = FuzzyOverlapInputSpec + input_spec = FuzzyOverlapInputSpec output_spec = FuzzyOverlapOutputSpec def _run_interface(self, runtime): ncomp = len(self.inputs.in_ref) - assert( ncomp == len(self.inputs.in_tst) ) - weights = np.ones( shape=ncomp ) - - img_ref = np.array( [ nb.load( fname ).get_data() for fname in self.inputs.in_ref ] ) - img_tst = np.array( [ nb.load( fname ).get_data() for fname in self.inputs.in_tst ] ) + assert(ncomp == len(self.inputs.in_tst)) + weights = np.ones(shape=ncomp) + img_ref = np.array([nb.load(fname).get_data() + for fname in self.inputs.in_ref]) + img_tst = np.array([nb.load(fname).get_data() + for fname in self.inputs.in_tst]) msk = np.sum(img_ref, axis=0) - msk[msk>0] = 1.0 + msk[msk > 0] = 1.0 tst_msk = np.sum(img_tst, axis=0) - tst_msk[tst_msk>0] = 1.0 - - #check that volumes are normalized - #img_ref[:][msk>0] = img_ref[:][msk>0] / (np.sum( img_ref, axis=0 ))[msk>0] - #img_tst[tst_msk>0] = img_tst[tst_msk>0] / np.sum( img_tst, axis=0 )[tst_msk>0] + tst_msk[tst_msk > 0] = 1.0 self._jaccards = [] volumes = [] - diff_im = np.zeros( img_ref.shape ) + diff_im = np.zeros(img_ref.shape) - for ref_comp, tst_comp, diff_comp in zip( img_ref, img_tst, diff_im ): - num = np.minimum( ref_comp, tst_comp ) - ddr = np.maximum( ref_comp, tst_comp ) - diff_comp[ddr>0]+= 1.0-(num[ddr>0]/ddr[ddr>0]) - self._jaccards.append( np.sum( num ) / np.sum( ddr ) ) - volumes.append( np.sum( ref_comp ) ) + for ref_comp, tst_comp, diff_comp in zip(img_ref, img_tst, diff_im): + num = np.minimum(ref_comp, tst_comp) + ddr = np.maximum(ref_comp, tst_comp) + diff_comp[ddr > 0] += 1.0 - (num[ddr > 0] / ddr[ddr > 0]) + self._jaccards.append(np.sum(num) / np.sum(ddr)) + volumes.append(np.sum(ref_comp)) - self._dices = 2.0*np.array(self._jaccards) / (np.array(self._jaccards) +1.0 ) + self._dices = 2.0 * \ + np.array(self._jaccards) / (np.array(self._jaccards) + 1.0) if self.inputs.weighting != "none": weights = 1.0 / np.array(volumes) if self.inputs.weighting == "squared_vol": - weights = weights**2 + weights = weights ** 2 - weights = weights / np.sum( weights ) + weights = weights / np.sum(weights) - setattr( self, '_jaccard', np.sum( weights * self._jaccards ) ) - setattr( self, '_dice', np.sum( weights * self._dices ) ) + setattr(self, '_jaccard', np.sum(weights * self._jaccards)) + setattr(self, '_dice', np.sum(weights * self._dices)) + diff = np.zeros(diff_im[0].shape) - diff = np.zeros( diff_im[0].shape ) + for w, ch in zip(weights, diff_im): + ch[msk == 0] = 0 + diff += w * ch - for w,ch in zip(weights,diff_im): - ch[msk==0] = 0 - diff+= w* ch - - nb.save(nb.Nifti1Image(diff, nb.load( self.inputs.in_ref[0]).get_affine(), - nb.load( self.inputs.in_ref[0]).get_header()), self.inputs.out_file ) + nb.save( + nb.Nifti1Image(diff, nb.load(self.inputs.in_ref[0]).get_affine(), + nb.load(self.inputs.in_ref[0]).get_header()), self.inputs.out_file) - return runtime def _list_outputs(self): outputs = self._outputs().get() for method in ("dice", "jaccard"): outputs[method] = getattr(self, '_' + method) - #outputs['volume_difference'] = self._volume outputs['diff_file'] = os.path.abspath(self.inputs.out_file) - outputs['class_fji'] = np.array(self._jaccards).astype(float).tolist(); - outputs['class_fdi']= self._dices.astype(float).tolist(); + outputs['class_fji'] = np.array( + self._jaccards).astype(float).tolist(); + outputs['class_fdi'] = self._dices.astype(float).tolist(); return outputs - class CreateNiftiInputSpec(BaseInterfaceInputSpec): data_file = File(exists=True, mandatory=True, desc="ANALYZE img file") header_file = File( @@ -599,6 +604,7 @@ class TSNROutputSpec(TraitedSpec): class TSNR(BaseInterface): + """Computes the time-course SNR for a time series Typically you want to run this on a realigned time-series. @@ -641,7 +647,7 @@ def _run_interface(self, runtime): betas = np.dot(np.linalg.pinv(X), np.rollaxis(data, 3, 2)) datahat = np.rollaxis(np.dot(X[:, 1:], np.rollaxis( - betas[1:, :, :, :], 0, 3)), + betas[1:, :,:,:], 0, 3)), 0, 4) data = data - datahat img = nb.Nifti1Image(data, img.get_affine(), header) @@ -676,6 +682,7 @@ class GunzipOutputSpec(TraitedSpec): class Gunzip(BaseInterface): + """ """ @@ -717,7 +724,7 @@ def matlab2csv(in_array, name, reshape): if reshape == True: if len(np.shape(output_array)) > 1: output_array = np.reshape(output_array, ( - np.shape(output_array)[0]*np.shape(output_array)[1], 1)) + np.shape(output_array)[0] * np.shape(output_array)[1], 1)) iflogger.info(np.shape(output_array)) output_name = op.abspath(name + '.csv') np.savetxt(output_name, output_array, delimiter=',') @@ -736,6 +743,7 @@ class Matlab2CSVOutputSpec(TraitedSpec): class Matlab2CSV(BaseInterface): + """ Simple interface to save the components of a MATLAB .mat file as a text file with comma-separated values (CSVs). @@ -766,7 +774,8 @@ def _run_interface(self, runtime): if isinstance(in_dict[key][0], np.ndarray): saved_variables.append(key) else: - iflogger.info('One of the keys in the input file, {k}, is not a Numpy array'.format(k=key)) + iflogger.info( + 'One of the keys in the input file, {k}, is not a Numpy array'.format(k=key)) if len(saved_variables) > 1: iflogger.info( @@ -828,7 +837,7 @@ def merge_csvs(in_list): in_file, delimiter=',', skiprows=1, usecols=range(1, n_cols)) except ValueError, ex: in_array = np.loadtxt( - in_file, delimiter=',', skiprows=1, usecols=range(1, n_cols-1)) + in_file, delimiter=',', skiprows=1, usecols=range(1, n_cols - 1)) if idx == 0: out_array = in_array else: @@ -846,7 +855,7 @@ def remove_identical_paths(in_files): out_names = list() commonprefix = op.commonprefix(in_files) lastslash = commonprefix.rfind('/') - commonpath = commonprefix[0:(lastslash+1)] + commonpath = commonprefix[0:(lastslash + 1)] for fileidx, in_file in enumerate(in_files): path, name, ext = split_filename(in_file) in_file = op.join(path, name) @@ -864,10 +873,10 @@ def maketypelist(rowheadings, shape, extraheadingBool, extraheading): if rowheadings: typelist.append(('heading', 'a40')) if len(shape) > 1: - for idx in range(1, (min(shape)+1)): + for idx in range(1, (min(shape) + 1)): typelist.append((str(idx), float)) else: - for idx in range(1, (shape[0]+1)): + for idx in range(1, (shape[0] + 1)): typelist.append((str(idx), float)) if extraheadingBool: typelist.append((extraheading, 'a40')) @@ -881,13 +890,13 @@ def makefmtlist(output_array, typelist, rowheadingsBool, shape, extraheadingBool fmtlist.append('%s') if len(shape) > 1: output = np.zeros(max(shape), typelist) - for idx in range(1, min(shape)+1): - output[str(idx)] = output_array[:, idx-1] + for idx in range(1, min(shape) + 1): + output[str(idx)] = output_array[:, idx - 1] fmtlist.append('%f') else: output = np.zeros(1, typelist) - for idx in range(1, len(output_array)+1): - output[str(idx)] = output_array[idx-1] + for idx in range(1, len(output_array) + 1): + output[str(idx)] = output_array[idx - 1] fmtlist.append('%f') if extraheadingBool: fmtlist.append('%s') @@ -917,6 +926,7 @@ class MergeCSVFilesOutputSpec(TraitedSpec): class MergeCSVFiles(BaseInterface): + """ This interface is designed to facilitate data loading in the R environment. It takes input CSV files and merges them into a single CSV file. @@ -1051,6 +1061,7 @@ class AddCSVColumnOutputSpec(TraitedSpec): class AddCSVColumn(BaseInterface): + """ Short interface to add an extra column and field to a text file @@ -1108,6 +1119,7 @@ class CalculateNormalizedMomentsOutputSpec(TraitedSpec): class CalculateNormalizedMoments(BaseInterface): + """ Calculates moments of timeseries. @@ -1147,4 +1159,4 @@ def calc_moments(timeseries_file, moment): m2 = stats.moment(timeseries, 2, axis=0) m3 = stats.moment(timeseries, moment, axis=0) zero = (m2 == 0) - return np.where(zero, 0, m3 / m2**(moment/2.0)) + return np.where(zero, 0, m3 / m2 ** (moment / 2.0)) From 0f8b096b871cd9a1721581981ed189c19b311057 Mon Sep 17 00:00:00 2001 From: Oscar Esteban Date: Mon, 12 Aug 2013 11:03:03 +0200 Subject: [PATCH 5/7] PEP8 for the whole interface Fixed all errors. #622 --- nipype/algorithms/misc.py | 237 ++++++++++++++++++++++++++------------ 1 file changed, 163 insertions(+), 74 deletions(-) diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index f543f97997..45110addac 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -38,15 +38,21 @@ class PickAtlasInputSpec(BaseInterfaceInputSpec): atlas = File(exists=True, desc="Location of the atlas that will be used.", mandatory=True) - labels = traits.Either(traits.Int, traits.List(traits.Int), - desc=("Labels of regions that will be included in" - "the mask. Must be compatible with the atlas used."), - compulsory=True) - hemi = traits.Enum('both', 'left', 'right', - desc="Restrict the mask to only one hemisphere: left or right", - usedefault=True) - dilation_size = traits.Int(usedefault=True, - desc="Defines how much the mask will be dilated (expanded in 3D).") + labels = traits.Either( + traits.Int, traits.List(traits.Int), + desc=("Labels of regions that will be included in the mask. Must be\ + compatible with the atlas used."), + compulsory=True + ) + hemi = traits.Enum( + 'both', 'left', 'right', + desc="Restrict the mask to only one hemisphere: left or right", + usedefault=True + ) + dilation_size = traits.Int( + usedefault=True, + desc="Defines how much the mask will be dilated (expanded in 3D)." + ) output_file = File(desc="Where to store the output mask.") @@ -89,9 +95,9 @@ def _get_brodmann_area(self): for lab in labels: newdata[origdata == lab] = 1 if self.inputs.hemi == 'right': - newdata[floor(float(origdata.shape[0]) / 2):, :,:] = 0 + newdata[floor(float(origdata.shape[0]) / 2):, :, :] = 0 elif self.inputs.hemi == 'left': - newdata[:ceil(float(origdata.shape[0]) / 2), :,:] = 0 + newdata[:ceil(float(origdata.shape[0]) / 2), :, :] = 0 if self.inputs.dilation_size != 0: newdata = grey_dilation( @@ -112,7 +118,10 @@ class SimpleThresholdInputSpec(BaseInterfaceInputSpec): volumes = InputMultiPath( File(exists=True), desc='volumes to be thresholded', mandatory=True) threshold = traits.Float( - desc='volumes to be thresholdedeverything below this value will be set to zero', mandatory=True) + desc='volumes to be thresholdedeverything below this value will be set\ + to zero', + mandatory=True + ) class SimpleThresholdOutputSpec(TraitedSpec): @@ -153,9 +162,17 @@ def _list_outputs(self): class ModifyAffineInputSpec(BaseInterfaceInputSpec): volumes = InputMultiPath( - File(exists=True), desc='volumes which affine matrices will be modified', mandatory=True) + File(exists=True), + desc='volumes which affine matrices will be modified', + mandatory=True + ) transformation_matrix = traits.Array( - value=np.eye(4), shape=(4, 4), desc="transformation matrix that will be left multiplied by the affine matrix", usedefault=True) + value=np.eye(4), + shape=(4, 4), + desc="transformation matrix that will be left multiplied by the\ + affine matrix", + usedefault=True + ) class ModifyAffineOutputSpec(TraitedSpec): @@ -165,7 +182,8 @@ class ModifyAffineOutputSpec(TraitedSpec): class ModifyAffine(BaseInterface): ''' - Left multiplies the affine matrix with a specified values. Saves the volume as a nifti file. + Left multiplies the affine matrix with a specified values. Saves the volume + as a nifti file. ''' input_spec = ModifyAffineInputSpec output_spec = ModifyAffineOutputSpec @@ -198,14 +216,23 @@ def _list_outputs(self): class DistanceInputSpec(BaseInterfaceInputSpec): volume1 = File(exists=True, mandatory=True, desc="Has to have the same dimensions as volume2.") - volume2 = File(exists=True, mandatory=True, - desc="Has to have the same dimensions as volume1.") - method = traits.Enum("eucl_min", "eucl_cog", "eucl_mean", "eucl_wmean", "eucl_max", desc='""eucl_min": Euclidean distance between two closest points\ - "eucl_cog": mean Euclidian distance between the Center of Gravity of volume1 and CoGs of volume2\ - "eucl_mean": mean Euclidian minimum distance of all volume2 voxels to volume1\ - "eucl_wmean": mean Euclidian minimum distance of all volume2 voxels to volume1 weighted by their values\ - "eucl_max": maximum over minimum Euclidian distances of all volume2 voxels to volume1 (also known as the Hausdorff distance)', - usedefault=True) + volume2 = File( + exists=True, mandatory=True, + desc="Has to have the same dimensions as volume1." + ) + method = traits.Enum( + "eucl_min", "eucl_cog", "eucl_mean", "eucl_wmean", "eucl_max", + desc='""eucl_min": Euclidean distance between two closest points\ + "eucl_cog": mean Euclidian distance between the Center of Gravity\ + of volume1 and CoGs of volume2\ + "eucl_mean": mean Euclidian minimum distance of all volume2 voxels\ + to volume1\ + "eucl_wmean": mean Euclidian minimum distance of all volume2 voxels\ + to volume1 weighted by their values\ + "eucl_max": maximum over minimum Euclidian distances of all volume2\ + voxels to volume1 (also known as the Hausdorff distance)', + usedefault=True + ) mask_volume = File( exists=True, desc="calculate overlap only within this mask.") @@ -234,7 +261,7 @@ def _find_border(self, data): def _get_coordinates(self, data, affine): if len(data.shape) == 4: - data = data[:, :,:, 0] + data = data[:, :, :, 0] indices = np.vstack(np.nonzero(data)) indices = np.vstack((indices, np.ones(indices.shape[1]))) coordinates = np.dot(affine, indices) @@ -254,7 +281,13 @@ def _eucl_min(self, nii1, nii2): dist_matrix = cdist(set1_coordinates.T, set2_coordinates.T) (point1, point2) = np.unravel_index( np.argmin(dist_matrix), dist_matrix.shape) - return (euclidean(set1_coordinates.T[point1, :], set2_coordinates.T[point2,:]), set1_coordinates.T[point1,:], set2_coordinates.T[point2,:]) + return ( + euclidean( + set1_coordinates.T[point1, :], + set2_coordinates.T[point2, :] + ), + set1_coordinates.T[point1, :], set2_coordinates.T[point2, :] + ) def _eucl_cog(self, nii1, nii2): origdata1 = nii1.get_data().astype(np.bool) @@ -296,7 +329,10 @@ def _eucl_mean(self, nii1, nii2, weighted=False): plt.close() if weighted: - return np.average(min_dist_matrix, weights=nii2.get_data()[origdata2].flat) + return np.average( + min_dist_matrix, + weights=nii2.get_data()[origdata2].flat + ) else: return np.mean(min_dist_matrix) @@ -442,44 +478,63 @@ def _list_outputs(self): class FuzzyOverlapInputSpec(BaseInterfaceInputSpec): - in_ref = InputMultiPath(File(exists=True), mandatory=True, - desc="Reference image. Requires the same dimensions as in_tst.") - in_tst = InputMultiPath(File(exists=True), mandatory=True, - desc="Test image. Requires the same dimensions as in_ref.") - weighting = traits.Enum("none", "volume", "squared_vol", desc='""none": no class-overlap weighting is performed\ - "volume": computed class-overlaps are weighted by class volume\ - "squared_vol": computed class-overlaps are weighted by the squared volume of the class', usedefault=True) + in_ref = InputMultiPath( + File(exists=True), mandatory=True, + desc="Reference image. Requires the same dimensions as in_tst." + ) + in_tst = InputMultiPath( + File(exists=True), mandatory=True, + desc="Test image. Requires the same dimensions as in_ref." + ) + weighting = traits.Enum( + "none", "volume", "squared_vol", + desc='""none": no class-overlap weighting is performed\ + "volume": computed class-overlaps are weighted by class volume\ + "squared_vol": computed class-overlaps are weighted by the squared\ + volume of the class', usedefault=True + ) out_file = File( - "diff.nii", desc="alternative name for resulting difference-map", usedefault=True) + "diff.nii", + desc="alternative name for resulting difference-map", + usedefault=True + ) class FuzzyOverlapOutputSpec(TraitedSpec): jaccard = traits.Float(desc="Fuzzy Jaccard Index (fJI), all the classes") dice = traits.Float(desc="Fuzzy Dice Index (fDI), all the classes") diff_file = File( - exists=True, desc="resulting difference-map of all classes, using the chosen weighting") + exists=True, desc="resulting difference-map of all classes,\ + using the chosen weighting" + ) class_fji = traits.List( - traits.Float(), desc="Array containing the fJIs of each computed class") + traits.Float(), + desc="Array containing the fJIs of each computed class" + ) class_fdi = traits.List( - traits.Float(), desc="Array containing the fDIs of each computed class") + traits.Float(), + desc="Array containing the fDIs of each computed class" + ) class FuzzyOverlap(BaseInterface): """ - Calculates various overlap measures between two maps, using the fuzzy definition - proposed in: Crum et al., Generalized Overlap Measures for Evaluation and Validation - in Medical Image Analysis, IEEE Trans. Med. Ima. 25(11),pp 1451-1461, Nov. 2006. + Calculates various overlap measures between two maps, using the fuzzy + definition proposed in: Crum et al., Generalized Overlap Measures for + Evaluation and Validation in Medical Image Analysis, IEEE Trans. Med. + Ima. 25(11),pp 1451-1461, Nov. 2006. - reference.nii and test.nii are lists of 2/3D images, each element on the list containing - one volume fraction map of a class in a fuzzy partition of the domain. + reference.nii and test.nii are lists of 2/3D images, each element on the + list containing one volume fraction map of a class in a fuzzy partition + of the domain. Example ------- >>> overlap = FuzzyOverlap() - >>> overlap.inputs.in_ref = [ 'ref_class0.nii', 'ref_class1.nii', 'ref_class2.nii' ] - >>> overlap.inputs.in_tst = [ 'tst_class0.nii', 'tst_class1.nii', 'tst_class2.nii' ] + >>> overlap.inputs.in_ref = [ 'ref_class0.nii', 'ref_class1.nii' ] + >>> overlap.inputs.in_tst = [ 'tst_class0.nii', 'tst_class1.nii' ] >>> overlap.inputs.weighting = 'volume' >>> res = overlap.run() # doctest: +SKIP """ @@ -534,8 +589,13 @@ def _run_interface(self, runtime): diff += w * ch nb.save( - nb.Nifti1Image(diff, nb.load(self.inputs.in_ref[0]).get_affine(), - nb.load(self.inputs.in_ref[0]).get_header()), self.inputs.out_file) + nb.Nifti1Image( + diff, + nb.load(self.inputs.in_ref[0]).get_affine(), + nb.load(self.inputs.in_ref[0]).get_header() + ), + self.inputs.out_file + ) return runtime @@ -545,8 +605,8 @@ def _list_outputs(self): outputs[method] = getattr(self, '_' + method) outputs['diff_file'] = os.path.abspath(self.inputs.out_file) outputs['class_fji'] = np.array( - self._jaccards).astype(float).tolist(); - outputs['class_fdi'] = self._dices.astype(float).tolist(); + self._jaccards).astype(float).tolist() + outputs['class_fdi'] = self._dices.astype(float).tolist() return outputs @@ -647,7 +707,7 @@ def _run_interface(self, runtime): betas = np.dot(np.linalg.pinv(X), np.rollaxis(data, 3, 2)) datahat = np.rollaxis(np.dot(X[:, 1:], np.rollaxis( - betas[1:, :,:,:], 0, 3)), + betas[1:, :, :, :], 0, 3)), 0, 4) data = data - datahat img = nb.Nifti1Image(data, img.get_affine(), header) @@ -721,7 +781,7 @@ def replaceext(in_list, ext): def matlab2csv(in_array, name, reshape): output_array = np.asarray(in_array) - if reshape == True: + if reshape: if len(np.shape(output_array)) > 1: output_array = np.reshape(output_array, ( np.shape(output_array)[0] * np.shape(output_array)[1], 1)) @@ -734,18 +794,24 @@ def matlab2csv(in_array, name, reshape): class Matlab2CSVInputSpec(TraitedSpec): in_file = File(exists=True, mandatory=True, desc='Input MATLAB .mat file') reshape_matrix = traits.Bool( - True, usedefault=True, desc='The output of this interface is meant for R, so matrices will be reshaped to vectors by default.') + True, usedefault=True, + desc='The output of this interface is meant for R, so matrices will be\ + reshaped to vectors by default.' + ) class Matlab2CSVOutputSpec(TraitedSpec): csv_files = OutputMultiPath( - File(desc='Output CSV files for each variable saved in the input .mat file')) + File(desc='Output CSV files for each variable saved in the input .mat\ + file') + ) class Matlab2CSV(BaseInterface): """ - Simple interface to save the components of a MATLAB .mat file as a text file with comma-separated values (CSVs). + Simple interface to save the components of a MATLAB .mat file as a text + file with comma-separated values (CSVs). CSV files are easily loaded in R, for use in statistical processing. For further information, see cran.r-project.org/doc/manuals/R-data.pdf @@ -764,7 +830,8 @@ class Matlab2CSV(BaseInterface): def _run_interface(self, runtime): in_dict = sio.loadmat(op.abspath(self.inputs.in_file)) - # Check if the file has multiple variables in it. If it does, loop through them and save them as individual CSV files. + # Check if the file has multiple variables in it. If it does, loop + # through them and save them as individual CSV files. # If not, save the variable as a single CSV file using the input file # name and a .csv extension. @@ -775,15 +842,19 @@ def _run_interface(self, runtime): saved_variables.append(key) else: iflogger.info( - 'One of the keys in the input file, {k}, is not a Numpy array'.format(k=key)) + 'One of the keys in the input file, {k},\ + is not a Numpy array'.format(k=key) + ) if len(saved_variables) > 1: iflogger.info( '{N} variables found:'.format(N=len(saved_variables))) iflogger.info(saved_variables) for variable in saved_variables: - iflogger.info('...Converting {var} - type {ty} - to CSV'.format( - var=variable, ty=type(in_dict[variable]))) + iflogger.info( + '...Converting {var} - type {ty} - to\ + CSV'.format(var=variable, ty=type(in_dict[variable])) + ) matlab2csv( in_dict[variable], variable, self.inputs.reshape_matrix) elif len(saved_variables) == 1: @@ -807,8 +878,8 @@ def _list_outputs(self): if isinstance(in_dict[key][0], np.ndarray): saved_variables.append(key) else: - iflogger.error( - 'One of the keys in the input file, {k}, is not a Numpy array'.format(k=key)) + iflogger.error('One of the keys in the input file, {k}, is\ + not a Numpy array'.format(k=key)) if len(saved_variables) > 1: outputs['csv_files'] = replaceext(saved_variables, '.csv') @@ -834,10 +905,14 @@ def merge_csvs(in_list): n_cols = len(header_list) try: in_array = np.loadtxt( - in_file, delimiter=',', skiprows=1, usecols=range(1, n_cols)) + in_file, delimiter=',', skiprows=1, + usecols=range(1, n_cols) + ) except ValueError, ex: in_array = np.loadtxt( - in_file, delimiter=',', skiprows=1, usecols=range(1, n_cols - 1)) + in_file, delimiter=',', skiprows=1, + usecols=range(1, n_cols - 1) + ) if idx == 0: out_array = in_array else: @@ -884,7 +959,8 @@ def maketypelist(rowheadings, shape, extraheadingBool, extraheading): return typelist -def makefmtlist(output_array, typelist, rowheadingsBool, shape, extraheadingBool): +def makefmtlist(output_array, typelist, rowheadingsBool, + shape, extraheadingBool): fmtlist = [] if rowheadingsBool: fmtlist.append('%s') @@ -910,15 +986,20 @@ class MergeCSVFilesInputSpec(TraitedSpec): out_file = File('merged.csv', usedefault=True, desc='Output filename for merged CSV file') column_headings = traits.List( - traits.Str, desc='List of column headings to save in merged CSV file (must be equal to number of input files). If left undefined, these will be pulled from the input filenames.') + traits.Str, desc='List of column headings to save in merged CSV file\ + (must be equal to number of input files). If left undefined, these\ + will be pulled from the input filenames.') row_headings = traits.List( - traits.Str, desc='List of row headings to save in merged CSV file (must be equal to number of rows in the input files).') + traits.Str, desc='List of row headings to save in merged CSV file\ + (must be equal to number of rows in the input files).') row_heading_title = traits.Str( - 'label', usedefault=True, desc='Column heading for the row headings added') + 'label', usedefault=True, desc='Column heading for the row headings\ + added') extra_column_heading = traits.Str( desc='New heading to add for the added field.') extra_field = traits.Str( - desc='New field to add to each row. This is useful for saving the group or subject ID in the file.') + desc='New field to add to each row. This is useful for saving the\ + group or subject ID in the file.') class MergeCSVFilesOutputSpec(TraitedSpec): @@ -930,7 +1011,8 @@ class MergeCSVFiles(BaseInterface): """ This interface is designed to facilitate data loading in the R environment. It takes input CSV files and merges them into a single CSV file. - If provided, it will also incorporate column heading names into the resulting CSV file. + If provided, it will also incorporate column heading names into the + resulting CSV file. CSV files are easily loaded in R, for use in statistical processing. For further information, see cran.r-project.org/doc/manuals/R-data.pdf @@ -978,8 +1060,8 @@ def _run_interface(self, runtime): iflogger.warn('Only one file input!') if isdefined(self.inputs.row_headings): - iflogger.info( - 'Row headings have been provided. Adding "labels" column header.') + iflogger.info('Row headings have been provided. Adding "labels"\ + column header.') prefix = '"{p}","'.format(p=self.inputs.row_heading_title) csv_headings = prefix + '","'.join(itertools.chain( headings)) + '"\n' @@ -1053,7 +1135,8 @@ class AddCSVColumnInputSpec(TraitedSpec): extra_column_heading = traits.Str( desc='New heading to add for the added field.') extra_field = traits.Str( - desc='New field to add to each row. This is useful for saving the group or subject ID in the file.') + desc='New field to add to each row. This is useful for saving the\ + group or subject ID in the file.') class AddCSVColumnOutputSpec(TraitedSpec): @@ -1108,10 +1191,14 @@ def _list_outputs(self): class CalculateNormalizedMomentsInputSpec(TraitedSpec): - timeseries_file = File(exists=True, mandatory=True, - desc='Text file with timeseries in columns and timepoints in rows, whitespace separated') + timeseries_file = File( + exists=True, mandatory=True, + desc='Text file with timeseries in columns and timepoints in rows,\ + whitespace separated') moment = traits.Int( - mandatory=True, desc="Define which moment should be calculated, 3 for skewness, 4 for kurtosis.") + mandatory=True, + desc="Define which moment should be calculated, 3 for skewness, 4 for\ + kurtosis.") class CalculateNormalizedMomentsOutputSpec(TraitedSpec): @@ -1148,7 +1235,9 @@ def _list_outputs(self): def calc_moments(timeseries_file, moment): - """Returns nth moment (3 for skewness, 4 for kurtosis) of timeseries (list of values; one per timeseries). + """ + Returns nth moment (3 for skewness, 4 for kurtosis) of timeseries + (list of values; one per timeseries). Keyword arguments: timeseries_file -- text file with white space separated timepoints in rows From 20def39fb8381a170dea9130b66b2b3be67ac713 Mon Sep 17 00:00:00 2001 From: Oscar Esteban Date: Thu, 22 Aug 2013 18:30:12 +0200 Subject: [PATCH 6/7] Added dummy files so doctest doesn't fail Files added in response of https://github.com/nipy/nipype/pull/622#discussion_r5921360 --- nipype/testing/data/ref_class0.nii | 0 nipype/testing/data/ref_class1.nii | 0 nipype/testing/data/tst_class0.nii | 0 nipype/testing/data/tst_class1.nii | 0 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 nipype/testing/data/ref_class0.nii create mode 100644 nipype/testing/data/ref_class1.nii create mode 100644 nipype/testing/data/tst_class0.nii create mode 100644 nipype/testing/data/tst_class1.nii diff --git a/nipype/testing/data/ref_class0.nii b/nipype/testing/data/ref_class0.nii new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/ref_class1.nii b/nipype/testing/data/ref_class1.nii new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/tst_class0.nii b/nipype/testing/data/tst_class0.nii new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/tst_class1.nii b/nipype/testing/data/tst_class1.nii new file mode 100644 index 0000000000..e69de29bb2 From 0e2a3516361079f7c4d85307b23da2aff4f97120 Mon Sep 17 00:00:00 2001 From: Oscar Esteban Date: Thu, 22 Aug 2013 18:39:57 +0200 Subject: [PATCH 7/7] Fixed filenames in documentation Minor change, as the names were not descriptive. --- nipype/algorithms/misc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index 45110addac..daaccde816 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -525,8 +525,8 @@ class FuzzyOverlap(BaseInterface): Evaluation and Validation in Medical Image Analysis, IEEE Trans. Med. Ima. 25(11),pp 1451-1461, Nov. 2006. - reference.nii and test.nii are lists of 2/3D images, each element on the - list containing one volume fraction map of a class in a fuzzy partition + in_ref and in_tst are lists of 2/3D images, each element on the list + containing one volume fraction map of a class in a fuzzy partition of the domain. Example