diff --git a/nipype/interfaces/afni/__init__.py b/nipype/interfaces/afni/__init__.py index 7c3498f7c6..7af80059f2 100644 --- a/nipype/interfaces/afni/__init__.py +++ b/nipype/interfaces/afni/__init__.py @@ -18,7 +18,7 @@ from .utils import ( ABoverlap, AFNItoNIFTI, Autobox, Axialize, BrickStat, Bucket, Calc, Cat, CatMatvec, CenterMass, ConvertDset, Copy, Dot, Edge3, Eval, FWHMx, - LocalBistat, MaskTool, Merge, Notes, NwarpApply, NwarpAdjust, NwarpCat, - OneDToolPy, Refit, Resample, TCat, TCatSubBrick, TStat, To3D, Unifize, - Undump, ZCutUp, GCOR, Zcat, Zeropad) + LocalBistat, Localstat, MaskTool, Merge, Notes, NwarpApply, NwarpAdjust, + NwarpCat, OneDToolPy, Refit, ReHo, Resample, TCat, TCatSubBrick, TStat, + To3D, Unifize, Undump, ZCutUp, GCOR, Zcat, Zeropad) from .model import (Deconvolve, Remlfit, Synthesize) diff --git a/nipype/interfaces/afni/preprocess.py b/nipype/interfaces/afni/preprocess.py index edb3e01c1a..891acc17f6 100644 --- a/nipype/interfaces/afni/preprocess.py +++ b/nipype/interfaces/afni/preprocess.py @@ -1802,28 +1802,109 @@ class QualityIndex(CommandLine): class ROIStatsInputSpec(CommandLineInputSpec): in_file = File( - desc='input file to 3dROIstats', + desc='input dataset', argstr='%s', - position=-1, + position=-2, mandatory=True, exists=True) - mask = File(desc='input mask', argstr='-mask %s', position=3, exists=True) + mask = File(desc='input mask', argstr='-mask %s', position=3, exists=True, + deprecated='1.1.4', new_name='mask_file') + mask_file = File(desc='input mask', argstr='-mask %s', exists=True) mask_f2short = traits.Bool( desc='Tells the program to convert a float mask to short integers, ' 'by simple rounding.', - argstr='-mask_f2short', - position=2) - quiet = traits.Bool(desc='execute quietly', argstr='-quiet', position=1) + argstr='-mask_f2short') + num_roi = traits.Int( + desc='Forces the assumption that the mask dataset\'s ROIs are ' + 'denoted by 1 to n inclusive. Normally, the program ' + 'figures out the ROIs on its own. This option is ' + 'useful if a) you are certain that the mask dataset ' + 'has no values outside the range [0 n], b) there may ' + 'be some ROIs missing between [1 n] in the mask data-' + 'set and c) you want those columns in the output any-' + 'way so the output lines up with the output from other ' + 'invocations of 3dROIstats.', + argstr='-numroi %s') + zerofill = traits.Str( + requires=['num_roi'], + desc='For ROI labels not found, use the provided string instead of ' + 'a \'0\' in the output file. Only active if `num_roi` is ' + 'enabled.', + argstr='-zerofill %s') + roisel = traits.File( + exists=True, + desc='Only considers ROIs denoted by values found in the specified ' + 'file. Note that the order of the ROIs as specified in the file ' + 'is not preserved. So an SEL.1D of \'2 8 20\' produces the same ' + 'output as \'8 20 2\'', + argstr='-roisel %s') + debug = traits.Bool( + desc='print debug information', + argstr='-debug') + quiet = traits.Bool( + desc='execute quietly', + argstr='-quiet') + nomeanout = traits.Bool( + desc='Do not include the (zero-inclusive) mean among computed stats', + argstr='-nomeanout') + nobriklab = traits.Bool( + desc='Do not print the sub-brick label next to its index', + argstr='-nobriklab') + format1D = traits.Bool( + xor=['format1DR'], + desc='Output results in a 1D format that includes commented labels', + argstr='-1Dformat') + format1DR = traits.Bool( + xor=['format1D'], + desc='Output results in a 1D format that includes uncommented ' + 'labels. May not work optimally with typical 1D functions, ' + 'but is useful for R functions.', + argstr='-1DRformat') + _stat_names = ['mean', 'sum', 'voxels', 'minmax', 'sigma', 'median', + 'mode', 'summary', 'zerominmax', 'zerosigma', 'zeromedian', + 'zeromode'] + stat = InputMultiObject( + traits.Enum(_stat_names), + desc='statistics to compute. Options include: ' + ' * mean = Compute the mean using only non_zero voxels.' + ' Implies the opposite for the mean computed ' + ' by default.\n' + ' * median = Compute the median of nonzero voxels\n' + ' * mode = Compute the mode of nonzero voxels.' + ' (integral valued sets only)\n' + ' * minmax = Compute the min/max of nonzero voxels\n' + ' * sum = Compute the sum using only nonzero voxels.\n' + ' * voxels = Compute the number of nonzero voxels\n' + ' * sigma = Compute the standard deviation of nonzero' + ' voxels\n' + 'Statistics that include zero-valued voxels:\n' + ' * zerominmax = Compute the min/max of all voxels.\n' + ' * zerosigma = Compute the standard deviation of all' + ' voxels.\n' + ' * zeromedian = Compute the median of all voxels.\n' + ' * zeromode = Compute the mode of all voxels.\n' + ' * summary = Only output a summary line with the grand ' + ' mean across all briks in the input dataset.' + ' This option cannot be used with nomeanout.\n' + 'More that one option can be specified.', + argstr='%s...') + out_file = File( + name_template='%s_roistat.1D', + desc='output file', + keep_extension=False, + argstr='> %s', + name_source='in_file', + position=-1) class ROIStatsOutputSpec(TraitedSpec): - stats = File(desc='output tab separated values file', exists=True) + out_file = File(desc='output tab-separated values file', exists=True) class ROIStats(AFNICommandBase): """Display statistics over masked regions - For complete details, see the `3dROIstats Documentation. + For complete details, see the `3dROIstats Documentation `_ Examples @@ -1832,10 +1913,11 @@ class ROIStats(AFNICommandBase): >>> from nipype.interfaces import afni >>> roistats = afni.ROIStats() >>> roistats.inputs.in_file = 'functional.nii' - >>> roistats.inputs.mask = 'skeleton_mask.nii.gz' - >>> roistats.inputs.quiet = True + >>> roistats.inputs.mask_file = 'skeleton_mask.nii.gz' + >>> roistats.inputs.stat = ['mean', 'median', 'voxels'] + >>> roistats.inputs.nomeanout = True >>> roistats.cmdline - '3dROIstats -quiet -mask skeleton_mask.nii.gz functional.nii' + '3dROIstats -mask skeleton_mask.nii.gz -nomeanout -nzmean -nzmedian -nzvoxels functional.nii > functional_roistat.1D' >>> res = roistats.run() # doctest: +SKIP """ @@ -1844,14 +1926,24 @@ class ROIStats(AFNICommandBase): input_spec = ROIStatsInputSpec output_spec = ROIStatsOutputSpec - def aggregate_outputs(self, runtime=None, needed_outputs=None): - outputs = self._outputs() - output_filename = 'roi_stats.csv' - with open(output_filename, 'w') as f: - f.write(runtime.stdout) - - outputs.stats = os.path.abspath(output_filename) - return outputs + def _format_arg(self, name, spec, value): + _stat_dict = { + 'mean': '-nzmean', + 'median': '-nzmedian', + 'mode': '-nzmode', + 'minmax': '-nzminmax', + 'sigma': '-nzsigma', + 'voxels': '-nzvoxels', + 'sum': '-nzsum', + 'summary': '-summary', + 'zerominmax': '-minmax', + 'zeromedian': '-median', + 'zerosigma': '-sigma', + 'zeromode': '-mode' + } + if name == 'stat': + value = [_stat_dict[v] for v in value] + return super(ROIStats, self)._format_arg(name, spec, value) class RetroicorInputSpec(AFNICommandInputSpec): diff --git a/nipype/interfaces/afni/tests/test_auto_Localstat.py b/nipype/interfaces/afni/tests/test_auto_Localstat.py new file mode 100644 index 0000000000..c2f53c3db0 --- /dev/null +++ b/nipype/interfaces/afni/tests/test_auto_Localstat.py @@ -0,0 +1,71 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..utils import Localstat + + +def test_Localstat_inputs(): + input_map = dict( + args=dict(argstr='%s', ), + automask=dict(argstr='-automask', ), + environ=dict( + nohash=True, + usedefault=True, + ), + grid_rmode=dict( + argstr='-grid_rmode %s', + requires=['reduce_restore_grid'], + ), + in_file=dict( + argstr='%s', + mandatory=True, + position=-1, + ), + mask_file=dict(argstr='-mask %s', ), + neighborhood=dict( + argstr="-nbhd '%s(%s)'", + mandatory=True, + ), + nonmask=dict(argstr='-use_nonmask', ), + num_threads=dict( + nohash=True, + usedefault=True, + ), + out_file=dict( + argstr='-prefix %s', + keep_extension=True, + name_source='in_file', + name_template='%s_localstat', + position=0, + ), + outputtype=dict(), + overwrite=dict(argstr='-overwrite', ), + quiet=dict(argstr='-quiet', ), + reduce_grid=dict( + argstr='-reduce_grid %s', + xor=['reduce_restore_grid', 'reduce_max_vox'], + ), + reduce_max_vox=dict( + argstr='-reduce_max_vox %s', + xor=['reduce_restore_grid', 'reduce_grid'], + ), + reduce_restore_grid=dict( + argstr='-reduce_restore_grid %s', + xor=['reduce_max_vox', 'reduce_grid'], + ), + stat=dict( + argstr='-stat %s...', + mandatory=True, + ), + ) + inputs = Localstat.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value +def test_Localstat_outputs(): + output_map = dict(out_file=dict(), ) + outputs = Localstat.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value diff --git a/nipype/interfaces/afni/tests/test_auto_ROIStats.py b/nipype/interfaces/afni/tests/test_auto_ROIStats.py index 57eb59d284..fbda92e9a5 100644 --- a/nipype/interfaces/afni/tests/test_auto_ROIStats.py +++ b/nipype/interfaces/afni/tests/test_auto_ROIStats.py @@ -6,26 +6,48 @@ def test_ROIStats_inputs(): input_map = dict( args=dict(argstr='%s', ), + debug=dict(argstr='-debug', ), environ=dict( nohash=True, usedefault=True, ), + format1D=dict( + argstr='-1Dformat', + xor=['format1DR'], + ), + format1DR=dict( + argstr='-1DRformat', + xor=['format1D'], + ), in_file=dict( argstr='%s', mandatory=True, - position=-1, + position=-2, ), mask=dict( argstr='-mask %s', + deprecated='1.1.4', + new_name='mask_file', position=3, ), - mask_f2short=dict( - argstr='-mask_f2short', - position=2, + mask_f2short=dict(argstr='-mask_f2short', ), + mask_file=dict(argstr='-mask %s', ), + nobriklab=dict(argstr='-nobriklab', ), + nomeanout=dict(argstr='-nomeanout', ), + num_roi=dict(argstr='-numroi %s', ), + out_file=dict( + argstr='> %s', + keep_extension=False, + name_source='in_file', + name_template='%s_roistat.1D', + position=-1, ), - quiet=dict( - argstr='-quiet', - position=1, + quiet=dict(argstr='-quiet', ), + roisel=dict(argstr='-roisel %s', ), + stat=dict(argstr='%s...', ), + zerofill=dict( + argstr='-zerofill %s', + requires=['num_roi'], ), ) inputs = ROIStats.input_spec() @@ -34,7 +56,7 @@ def test_ROIStats_inputs(): for metakey, value in list(metadata.items()): assert getattr(inputs.traits()[key], metakey) == value def test_ROIStats_outputs(): - output_map = dict(stats=dict(), ) + output_map = dict(out_file=dict(), ) outputs = ROIStats.output_spec() for key, metadata in list(output_map.items()): diff --git a/nipype/interfaces/afni/tests/test_auto_ReHo.py b/nipype/interfaces/afni/tests/test_auto_ReHo.py new file mode 100644 index 0000000000..1809759485 --- /dev/null +++ b/nipype/interfaces/afni/tests/test_auto_ReHo.py @@ -0,0 +1,56 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..utils import ReHo + + +def test_ReHo_inputs(): + input_map = dict( + args=dict(argstr='%s', ), + chi_sq=dict(argstr='-chi_sq', ), + ellipsoid=dict( + argstr='-neigh_X %s -neigh_Y %s -neigh_Z %s', + xor=['sphere', 'neighborhood'], + ), + environ=dict( + nohash=True, + usedefault=True, + ), + in_file=dict( + argstr='-inset %s', + mandatory=True, + position=1, + ), + label_set=dict(argstr='-in_rois %s', ), + mask_file=dict(argstr='-mask %s', ), + neighborhood=dict( + argstr='-nneigh %s', + xor=['sphere', 'ellipsoid'], + ), + out_file=dict( + argstr='-prefix %s', + keep_extension=True, + name_source='in_file', + name_template='%s_reho', + position=0, + ), + overwrite=dict(argstr='-overwrite', ), + sphere=dict( + argstr='-neigh_RAD %s', + xor=['neighborhood', 'ellipsoid'], + ), + ) + inputs = ReHo.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value +def test_ReHo_outputs(): + output_map = dict( + out_file=dict(), + out_vals=dict(), + ) + outputs = ReHo.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value diff --git a/nipype/interfaces/afni/utils.py b/nipype/interfaces/afni/utils.py index ce32d183ef..987fcec135 100644 --- a/nipype/interfaces/afni/utils.py +++ b/nipype/interfaces/afni/utils.py @@ -18,7 +18,8 @@ from ...utils.filemanip import (load_json, save_json, split_filename) from ..base import (CommandLineInputSpec, CommandLine, Directory, TraitedSpec, - traits, isdefined, File, InputMultiPath, Undefined, Str) + traits, isdefined, File, InputMultiObject, InputMultiPath, + Undefined, Str) from ...external.due import BibTeX from .base import (AFNICommandBase, AFNICommand, AFNICommandInputSpec, AFNICommandOutputSpec, AFNIPythonCommandInputSpec, @@ -1453,6 +1454,187 @@ def _format_arg(self, name, spec, value): return super(LocalBistat, self)._format_arg(name, spec, value) +class LocalstatInputSpec(AFNICommandInputSpec): + in_file = File( + exists=True, + mandatory=True, + argstr='%s', + position=-1, + desc='input dataset') + neighborhood = traits.Either( + traits.Tuple(traits.Enum('SPHERE', 'RHDD', 'TOHD'), traits.Float()), + traits.Tuple(traits.Enum('RECT'), traits.Tuple(traits.Float(), + traits.Float(), + traits.Float())), + mandatory=True, + desc='The region around each voxel that will be extracted for ' + 'the statistics calculation. Possible regions are: ' + '\'SPHERE\', \'RHDD\' (rhombic dodecahedron), \'TOHD\' ' + '(truncated octahedron) with a given radius in mm or ' + '\'RECT\' (rectangular block) with dimensions to specify in mm.', + argstr="-nbhd '%s(%s)'") + _stat_names = ['mean', 'stdev', 'var', 'cvar', 'median', 'MAD', 'min', + 'max', 'absmax', 'num', 'sum', 'FWHM', 'FWHMbar', 'rank', + 'frank', 'P2skew', 'ALL', 'mMP2s', 'mmMP2s'] + stat = InputMultiObject( + traits.Either( + traits.Enum(_stat_names), + traits.Tuple(traits.Enum('perc'), + traits.Tuple(traits.Float, traits.Float, traits.Float))), + mandatory=True, + desc='statistics to compute. Possible names are :\n' + ' * mean = average of the values\n' + ' * stdev = standard deviation\n' + ' * var = variance (stdev*stdev)\n' + ' * cvar = coefficient of variation = stdev/fabs(mean)\n' + ' * median = median of the values\n' + ' * MAD = median absolute deviation\n' + ' * min = minimum\n' + ' * max = maximum\n' + ' * absmax = maximum of the absolute values\n' + ' * num = number of the values in the region:\n' + ' with the use of -mask or -automask,' + ' the size of the region around any given' + ' voxel will vary; this option lets you' + ' map that size. It may be useful if you' + ' plan to compute a t-statistic (say) from' + ' the mean and stdev outputs.\n' + ' * sum = sum of the values in the region\n' + ' * FWHM = compute (like 3dFWHM) image smoothness' + ' inside each voxel\'s neighborhood. Results' + ' are in 3 sub-bricks: FWHMx, FHWMy, and FWHMz.' + ' Places where an output is -1 are locations' + ' where the FWHM value could not be computed' + ' (e.g., outside the mask).\n' + ' * FWHMbar= Compute just the average of the 3 FWHM values' + ' (normally would NOT do this with FWHM also).\n' + ' * perc:P0:P1:Pstep = \n' + ' Compute percentiles between P0 and P1 with a ' + ' step of Pstep.\n' + ' Default P1 is equal to P0 and default P2 = 1\n' + ' * rank = rank of the voxel\'s intensity\n' + ' * frank = rank / number of voxels in neighborhood\n' + ' * P2skew = Pearson\'s second skewness coefficient' + ' 3 * (mean - median) / stdev\n' + ' * ALL = all of the above, in that order ' + ' (except for FWHMbar and perc).\n' + ' * mMP2s = Exactly the same output as:' + ' median, MAD, P2skew,' + ' but a little faster\n' + ' * mmMP2s = Exactly the same output as:' + ' mean, median, MAD, P2skew\n' + 'More than one option can be used.', + argstr='-stat %s...') + mask_file = traits.File( + exists=True, + desc='Mask image file name. Voxels NOT in the mask will not be used ' + 'in the neighborhood of any voxel. Also, a voxel NOT in the ' + 'mask will have its statistic(s) computed as zero (0) unless ' + 'the parameter \'nonmask\' is set to true.', + argstr='-mask %s') + automask = traits.Bool( + desc='Compute the mask as in program 3dAutomask.', + argstr='-automask') + nonmask = traits.Bool( + desc='Voxels not in the mask WILL have their local statistics ' + 'computed from all voxels in their neighborhood that ARE in ' + 'the mask.\n' + ' * For instance, this option can be used to compute the ' + ' average local white matter time series, even at non-WM ' + ' voxels.', + argstr='-use_nonmask') + reduce_grid = traits.Either( + traits.Float, + traits.Tuple(traits.Float, traits.Float, traits.Float), + argstr='-reduce_grid %s', + xor=['reduce_restore_grid', 'reduce_max_vox'], + desc='Compute output on a grid that is reduced by the specified ' + 'factors. If a single value is passed, output is resampled ' + 'to the specified isotropic grid. Otherwise, the 3 inputs ' + 'describe the reduction in the X, Y, and Z directions. This ' + 'option speeds up computations at the expense of resolution. ' + 'It should only be used when the nbhd is quite large with ' + 'respect to the input\'s resolution, and the resultant stats ' + 'are expected to be smooth.') + reduce_restore_grid = traits.Either( + traits.Float, + traits.Tuple(traits.Float, traits.Float, traits.Float), + argstr='-reduce_restore_grid %s', + xor=['reduce_max_vox', 'reduce_grid'], + desc='Like reduce_grid, but also resample output back to input' + 'grid.') + reduce_max_vox = traits.Float( + argstr='-reduce_max_vox %s', + xor=['reduce_restore_grid', 'reduce_grid'], + desc='Like reduce_restore_grid, but automatically set Rx Ry Rz so' + 'that the computation grid is at a resolution of nbhd/MAX_VOX' + 'voxels.') + grid_rmode = traits.Enum( + 'NN', + 'Li', + 'Cu', + 'Bk', + argstr='-grid_rmode %s', + requires=['reduce_restore_grid'], + desc='Interpolant to use when resampling the output with the' + 'reduce_restore_grid option. The resampling method string ' + 'RESAM should come from the set {\'NN\', \'Li\', \'Cu\', ' + '\'Bk\'}. These stand for \'Nearest Neighbor\', \'Linear\', ' + '\'Cubic\', and \'Blocky\' interpolation, respectively.') + quiet = traits.Bool( + argstr='-quiet', + desc='Stop the highly informative progress reports.') + overwrite = traits.Bool( + desc='overwrite output file if it already exists', + argstr='-overwrite') + out_file = traits.File( + desc='Output dataset.', + argstr='-prefix %s', + name_source='in_file', + name_template='%s_localstat', + keep_extension=True, + position=0) + + +class Localstat(AFNICommand): + """3dLocalstat - computes statistics at each voxel, + based on a local neighborhood of that voxel. + + For complete details, see the `3dLocalstat Documentation. + `_ + + Examples + ======== + + >>> from nipype.interfaces import afni + >>> localstat = afni.Localstat() + >>> localstat.inputs.in_file = 'functional.nii' + >>> localstat.inputs.mask_file = 'skeleton_mask.nii.gz' + >>> localstat.inputs.neighborhood = ('SPHERE', 45) + >>> localstat.inputs.stat = 'mean' + >>> localstat.inputs.nonmask = True + >>> localstat.inputs.outputtype = 'NIFTI_GZ' + >>> localstat.cmdline + "3dLocalstat -prefix functional_localstat.nii -mask skeleton_mask.nii.gz -nbhd 'SPHERE(45.0)' -use_nonmask -stat mean functional.nii" + >>> res = localstat.run() # doctest: +SKIP + + """ + _cmd = '3dLocalstat' + input_spec = LocalstatInputSpec + output_spec = AFNICommandOutputSpec + + def _format_arg(self, name, spec, value): + if name == 'neighborhood' and value[0] == 'RECT': + value = ('RECT', '%s,%s,%s' % value[1]) + if name == 'stat': + value = ['perc:%s:%s:%s' % v[1] if len(v) == 2 else v for v in value] + if name == 'reduce_grid' or name == 'reduce_restore_grid': + if len(value) == 3: + value = '%s %s %s' % value + + return super(Localstat, self)._format_arg(name, spec, value) + + class MaskToolInputSpec(AFNICommandInputSpec): in_file = File( desc='input file or files to 3dmask_tool', @@ -2119,6 +2301,126 @@ def _list_outputs(self): return outputs +class ReHoInputSpec(CommandLineInputSpec): + in_file = File( + desc='input dataset', + argstr='-inset %s', + position=1, + mandatory=True, + exists=True) + out_file = traits.File( + desc='Output dataset.', + argstr='-prefix %s', + name_source='in_file', + name_template='%s_reho', + keep_extension=True, + position=0) + chi_sq = traits.Bool( + argstr='-chi_sq', + desc='Output the Friedman chi-squared value in addition to the ' + 'Kendall\'s W. This option is currently compatible only with ' + 'the AFNI (BRIK/HEAD) output type; the chi-squared value will ' + 'be the second sub-brick of the output dataset.') + mask_file = traits.File( + desc='Mask within which ReHo should be calculated voxelwise', + argstr='-mask %s') + neighborhood = traits.Enum( + 'faces', + 'edges', + 'vertices', + xor=['sphere', 'ellipsoid'], + argstr='-nneigh %s', + desc='voxels in neighborhood. can be: ' + '* faces (for voxel and 6 facewise neighbors, only),\n' + '* edges (for voxel and 18 face- and edge-wise neighbors),\n' + '* vertices (for voxel and 26 face-, edge-, and node-wise ' + 'neighbors).\n') + sphere = traits.Float( + argstr='-neigh_RAD %s', + xor=['neighborhood', 'ellipsoid'], + desc='for additional voxelwise neighborhood control, the ' + 'radius R of a desired neighborhood can be put in; R is ' + 'a floating point number, and must be >1. Examples of ' + 'the numbers of voxels in a given radius are as follows ' + '(you can roughly approximate with the ol\' 4*PI*(R^3)/3 ' + 'thing):\n' + ' R=2.0 -> V=33,\n' + ' R=2.3 -> V=57, \n' + ' R=2.9 -> V=93, \n' + ' R=3.1 -> V=123, \n' + ' R=3.9 -> V=251, \n' + ' R=4.5 -> V=389, \n' + ' R=6.1 -> V=949, \n' + 'but you can choose most any value.') + ellipsoid = traits.Tuple( + traits.Float, + traits.Float, + traits.Float, + xor=['sphere', 'neighborhood'], + argstr='-neigh_X %s -neigh_Y %s -neigh_Z %s', + desc='Tuple indicating the x, y, and z radius of an ellipsoid ' + 'defining the neighbourhood of each voxel.\n' + 'The \'hood is then made according to the following relation:' + '(i/A)^2 + (j/B)^2 + (k/C)^2 <=1.\n' + 'which will have approx. V=4*PI*A*B*C/3. The impetus for ' + 'this freedom was for use with data having anisotropic ' + 'voxel edge lengths.') + label_set = File( + exists=True, + argstr='-in_rois %s', + desc='a set of ROIs, each labelled with distinct ' + 'integers. ReHo will then be calculated per ROI.') + overwrite = traits.Bool( + desc='overwrite output file if it already exists', + argstr='-overwrite') + + +class ReHoOutputSpec(TraitedSpec): + out_file = File(exists=True, desc='Voxelwise regional homogeneity map') + out_vals = File(desc='Table of labelwise regional homogenity values') + + +class ReHo(AFNICommandBase): + """Compute regional homogenity for a given neighbourhood.l, + based on a local neighborhood of that voxel. + + For complete details, see the `3dReHo Documentation. + `_ + + Examples + ======== + + >>> from nipype.interfaces import afni + >>> reho = afni.ReHo() + >>> reho.inputs.in_file = 'functional.nii' + >>> reho.inputs.out_file = 'reho.nii.gz' + >>> reho.inputs.neighborhood = 'vertices' + >>> reho.cmdline + '3dReHo -prefix reho.nii.gz -inset functional.nii -nneigh 27' + >>> res = reho.run() # doctest: +SKIP + + """ + _cmd = '3dReHo' + input_spec = ReHoInputSpec + output_spec = ReHoOutputSpec + + def _list_outputs(self): + outputs = super(ReHo, self)._list_outputs() + if self.inputs.label_set: + outputs['out_vals'] = outputs['out_file'] + '_ROI_reho.vals' + return outputs + + def _format_arg(self, name, spec, value): + _neigh_dict = { + 'faces': 7, + 'edges': 19, + 'vertices': 27, + } + if name == 'neighborhood': + value = _neigh_dict[value] + return super(ReHo, self)._format_arg(name, spec, value) + + class ResampleInputSpec(AFNICommandInputSpec): in_file = File(