From 533850b33117768e05ee8bc80f6dd46425cbea42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Mon, 1 Mar 2021 15:21:38 +0000 Subject: [PATCH 01/24] Added cat12 interface. --- nipype/interfaces/cat12/__init__.py | 0 nipype/interfaces/cat12/preprocess.py | 216 ++++++++++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 nipype/interfaces/cat12/__init__.py create mode 100644 nipype/interfaces/cat12/preprocess.py diff --git a/nipype/interfaces/cat12/__init__.py b/nipype/interfaces/cat12/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/interfaces/cat12/preprocess.py b/nipype/interfaces/cat12/preprocess.py new file mode 100644 index 0000000000..5fd58f09a5 --- /dev/null +++ b/nipype/interfaces/cat12/preprocess.py @@ -0,0 +1,216 @@ +import os +import sys +import traits +from nipype.interfaces.base import InputMultiPath, TraitedSpec, isdefined +from nipype.interfaces.spm import SPMCommand +from nipype.interfaces.spm.base import SPMCommandInputSpec, ImageFileSPM, scans_for_fnames, scans_for_fname +from nipype.utils.filemanip import split_filename, fname_presuffix +from traits.trait_types import Int, File +from traits.trait_types import List + + +class CAT12SegmentInputSpec(SPMCommandInputSpec): + in_files = InputMultiPath(ImageFileSPM(exists=True), field="data", desc="file to segment", mandatory=True, + copyfile=False) + + tpm = InputMultiPath(ImageFileSPM(exists=True), field="tpm", desc="Tissue Probability Maps", mandatory=False, + copyfile=False) + + n_jobs = traits.trait_types.Int(1, usedefault=True, mandatory=True, field="nproc", desc="Number of threads") + use_prior = traits.trait_types.Str(field="useprior", usedefault=True) + + affine_regularization = traits.trait_types.Str(default_value="mni", + field="opts.affreg", usedefault=True) + + power_spm_inhomogeneity_correction = traits.trait_types.Float(default_value=0.5, field='opts.biasacc', + usedefault=True) + # Extended options for CAT12 preprocessing + affine_preprocessing = traits.trait_types.Int(1070, field="extopts.APP", usedefault=True) + initial_segmentation = traits.trait_types.Int(0, field="extopts.spm_kamap", usedefault=True) + local_adaptive_seg = traits.trait_types.Float(0.5, field="extopts.LASstr", usedefault=True) + skull_strip = traits.trait_types.Float(2, field="extopts.gcutstr", usedefault=True) + wm_hyper_intensity_correction = traits.trait_types.Int(1, field="extopts.WMHC", usedefault=True) + spatial_registration = traits.trait_types.Int(1, field="extopts.WMHC", usedefault=True) + voxel_size = traits.trait_types.Float(1.5, field="extopts.vox", usedefault=True) + internal_resampling_process = traits.trait_types.Tuple(traits.trait_types.Float(1), traits.trait_types.Float(0.1), + minlen=2, maxlen=2, + field="extopts.restypes.optimal", usedefault=True) + ignore_errors = traits.trait_types.Int(1, field="extopts.ignoreErrors", usedefault=True) + + # Writing options + surface_and_thickness_estimation = traits.trait_types.Int(1, field="surface", usedefault=True) + surface_measures = traits.trait_types.Int(1, field="output.surf_measures", usedefault=True) + + # Templates + neuromorphometrics = traits.trait_types.Bool(True, field="output.ROImenu.atlases.neuromorphometrics", + usedefault=True) + lpba40 = traits.trait_types.Bool(False, field="output.ROImenu.atlases.lpba40", usedefault=True) + cobra = traits.trait_types.Bool(True, field="output.ROImenu.atlases.hammers", usedefault=True) + hammers = traits.trait_types.Bool(False, field="output.ROImenu.atlases.cobra", usedefault=True) + own_atlas = InputMultiPath(ImageFileSPM(exists=True), field="output.ROImenu.atlases.ownatlas", + desc="Own Atlas", mandatory=False, copyfile=False) + + # Grey matter + gm_output_native = traits.trait_types.Bool(False, field="output.GM.native", usedefault=True) + gm_output_modulated = traits.trait_types.Bool(True, field="output.GM.mod", usedefault=True) + gm_output_dartel = traits.trait_types.Bool(False, field="output.GM.dartel", usedefault=True) + + # White matter + wm_output_native = traits.trait_types.Bool(False, field="output.WM.native", usedefault=True) + wm_output_modulated = traits.trait_types.Bool(True, field="output.WM.mod", usedefault=True) + wm_output_dartel = traits.trait_types.Bool(False, field="output.WM.dartel", usedefault=True) + + # CSF matter + csf_output_native = traits.trait_types.Bool(False, field="output.CSF.native", usedefault=True) + csf_output_modulated = traits.trait_types.Bool(True, field="output.CSF.mod", usedefault=True) + csf_output_dartel = traits.trait_types.Bool(False, field="output.CSF.dartel", usedefault=True) + + # Labels + label_native = traits.trait_types.Bool(False, field="output.label.native", usedefault=True) + label_warped = traits.trait_types.Bool(True, field="output.label.warped", usedefault=True) + label_dartel = traits.trait_types.Bool(False, field="output.label.dartel", usedefault=True) + output_labelnative = traits.trait_types.Bool(False, field="output.labelnative", usedefault=True) + + # Bias + save_bias_corrected = traits.trait_types.Bool(True, field="output.bias.warped", usedefault=True) + + # las + las_native = traits.trait_types.Bool(False, field="output.las.native", usedefault=True) + las_warped = traits.trait_types.Bool(True, field="output.las.warped", usedefault=True) + las_dartel = traits.trait_types.Bool(False, field="output.las.dartel", usedefault=True) + + # Jacobian Warped + jacobianwarped = traits.trait_types.Bool(True, field="output.jacobianwarped", usedefault=True) + + # Deformation Fields + warps = traits.trait_types.Tuple(traits.trait_types.Int(1), traits.trait_types.Int(0), minlen=2, maxlen=2, + field="output.warps", usedefault=True) + + +class CAT12SegmentOutputSpec(TraitedSpec): + ########################################## + # Label XML files + ########################################## + label_files = List(File(exists=True)) + + label_rois = File(exists=True, desc="ROIs Volumes") + label_roi = File(exists=True, desc="ROI volumes") + + ########################################## + # MRI .nii files + ########################################## + + mri_images = List(File(exists=True)) + + # Grey Matter + gm_modulated_image = File(exists=True) + gm_dartel_image = File(exists=True) + gm_native_image = File(exists=True) + + # White Matter + wm_modulated_image = File(exists=True) + wm_dartel_image = File(exists=True) + wm_native_image = File(exists=True) + + # CSF + csf_modulated_image = File(exists=True) + csf_dartel_image = File(exists=True) + csf_native_image = File(exists=True) + + bias_corrected_image = File(exists=True) + ########################################## + # Surface files + ########################################## + + surface_files = List(File(exists=True)) + + # Right hemisphere + rh_central_surface = File(exists=True) + rh_sphere_surface = File(exists=True) + + # Left hemisphere + lh_central_surface = File(exists=True) + lh_sphere_surface = File(exists=True) + + # Report files + report_files = List(File(exists=True)) + report = File(exists=True) + + +class CAT12Segment(SPMCommand): + input_spec = CAT12SegmentInputSpec + output_spec = CAT12SegmentOutputSpec + + def __init__(self, **inputs): + _local_version = SPMCommand().version + if _local_version and "12." in _local_version: + self._jobtype = "tools" + self._jobname = "cat.estwrite" + + SPMCommand.__init__(self, **inputs) + + def _format_arg(self, opt, spec, val): + """Convert input to appropriate format for spm + """ + if opt in ["in_files"]: + if isinstance(val, list): + return scans_for_fnames(val) + else: + return scans_for_fname(val) + return super(CAT12Segment, self)._format_arg(opt, spec, val) + + def _list_outputs(self): + outputs = self._outputs().get() + f = self.inputs.in_files[0] + pth, base, ext = split_filename(f) + + outputs["mri_images"] = [os.path.join(os.path.join(pth, "mri"), f) for f in + os.listdir(os.path.join(pth, "mri")) + if os.path.isfile(os.path.join(os.path.join(pth, "mri"), f))] + + for tidx, tissue in enumerate(["gm", "wm", "csf"]): + + for idx, (image, prefix) in enumerate([("modulated", "mw"), ("dartel", "r"), ("native", "")]): + outtype = f'{tissue}_output_{image}' + if isdefined(getattr(self.inputs, outtype)) and getattr(self.inputs, outtype): + outfield = f'{tissue}_{image}_image' + prefix = os.path.join("mri", f'{prefix}p{tidx + 1}') + if image != "dartel": + outputs[outfield] = fname_presuffix(f, prefix=prefix) + else: + outputs[outfield] = fname_presuffix(f, prefix=prefix, suffix="_rigid") + + if isdefined(self.inputs.save_bias_corrected) and self.inputs.save_bias_corrected: + outputs["bias_corrected_image"] = fname_presuffix(f, prefix=os.path.join("mri", 'mi')) + + outputs["surface_files"] = [os.path.join(os.path.join(pth, "surf"), f) for f in + os.listdir(os.path.join(pth, "surf")) + if os.path.isfile(os.path.join(os.path.join(pth, "surf"), f))] + + for tidx, hemisphere in enumerate(["rh", "lh"]): + for idx, suffix in enumerate(["central", "sphere"]): + outfield = f'{hemisphere}_{suffix}_surface' + outputs[outfield] = fname_presuffix(f, prefix=os.path.join("surf", f'{hemisphere}.{suffix}.'), + suffix=".gii", use_ext=False) + + outputs["report_files"] = [os.path.join(os.path.join(pth, "report"), f) for f in + os.listdir(os.path.join(pth, "report")) + if os.path.isfile(os.path.join(os.path.join(pth, "report"), f))] + outputs[f'report'] = fname_presuffix(f, prefix=os.path.join("report", f'cat_'), suffix=".xml", use_ext=False) + + outputs["label_files"] = [os.path.join(os.path.join(pth, "label"), f) for f in + os.listdir(os.path.join(pth, "label")) + if os.path.isfile(os.path.join(os.path.join(pth, "label"), f))] + + outputs['label_rois'] = fname_presuffix(f, prefix=os.path.join("label", f'catROIs_'), suffix=".xml", + use_ext=False) + outputs['label_roi'] = fname_presuffix(f, prefix=os.path.join("label", f'catROI_'), suffix=".xml", + use_ext=False) + + return outputs + + +if __name__ == '__main__': + path_mr = sys.argv[1] + cat = CAT12Segment(in_files=path_mr) + cat.run() From eb80b27033fb268a939a6a848d0627d77499549e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Mon, 1 Mar 2021 16:02:10 +0000 Subject: [PATCH 02/24] Updated .zenodo.json. --- .zenodo.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.zenodo.json b/.zenodo.json index e525f14326..d539f2aa95 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -807,6 +807,11 @@ "affiliation": "MIT, HMS", "name": "Ghosh, Satrajit", "orcid": "0000-0002-5312-6729" + }, + { + "affiliation": "CIBIT, UC", + "name": "Machado, Fátima", + "orcid": "0000-0001-8878-1750" } ], "keywords": [ From f1091633c0b36e36971296d48651148dd5d7a70d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Mon, 8 Mar 2021 18:24:30 +0000 Subject: [PATCH 03/24] Added interfaces to extract measures from the surface using CAT12. --- nipype/interfaces/cat12/surface.py | 234 +++++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 nipype/interfaces/cat12/surface.py diff --git a/nipype/interfaces/cat12/surface.py b/nipype/interfaces/cat12/surface.py new file mode 100644 index 0000000000..34b7dbf892 --- /dev/null +++ b/nipype/interfaces/cat12/surface.py @@ -0,0 +1,234 @@ +import os +import sys + +import traits +from traits.trait_base import _Undefined +from traits.trait_types import List + +from nipype.interfaces.base import File, InputMultiPath, TraitedSpec +from nipype.interfaces.spm import SPMCommand +from nipype.interfaces.spm.base import SPMCommandInputSpec +from nipype.utils.filemanip import split_filename + + +class ExtractAdditionalSurfaceParametersInputSpec(SPMCommandInputSpec): + left_central_surfaces = InputMultiPath(File(exists=True), field="data_surf", + desc="Left and central surfaces files", mandatory=True, copyfile=False) + surface_files = InputMultiPath(File(exists=True), + desc="All surface files", mandatory=False, copyfile=False) + + gyrification = traits.trait_types.Bool(True, field="GI", usedefault=True, + desc="Extract gyrification index (GI) based on absolute mean curvature. The" + " method is described in Luders et al. Neuroimage, 29:1224-1230, 2006") + gmv = traits.trait_types.Bool(True, field="gmv", usedefault=True, desc="Extract volume") + area = traits.trait_types.Bool(True, field="area", usedefault=True, desc="Extract area surface") + depth = traits.trait_types.Bool(False, field="SD", usedefault=True, + desc="Extract sulcus depth based on euclidian distance between the central " + "surface anf its convex hull.") + fractal_dimension = traits.trait_types.Bool(False, field="FD", usedefault=True, + desc="Extract cortical complexity (fractal dimension) which is " + "described in Yotter ar al. Neuroimage, 56(3): 961-973, 2011") + + +class ExtractAdditionalSurfaceParametersOutputSpec(TraitedSpec): + lh_extracted_files = List(File(exists=True)) + rh_extracted_files = List(File(exists=True)) + + lh_gyrification = List(File(exists=True)) + rh_gyrification = List(File(exists=True)) + + lh_gyrification_resampled = List(File(exists=True)) + rh_gyrification_resampled = List(File(exists=True)) + + lh_gmv = List(File(exists=True)) + rh_gmv = List(File(exists=True)) + + lh_area = List(File(exists=True)) + rh_area = List(File(exists=True)) + + lh_depth = List(File(exists=True)) + rh_depth = List(File(exists=True)) + + lh_fractaldimension = List(File(exists=True)) + rh_fractaldimension = List(File(exists=True)) + + +class ExtractAdditionalSurfaceParameters(SPMCommand): + """ + Additional surface parameters can be extracted that can be used for statistical analysis, such as: + + * Central surfaces + * Surface area + * Surface GM volume + * Gyrification Index + * Sulcus depth + * Toro's gyrification index + * Shaer's local gyrification index + * Laplacian gyrification indeces + * Addicional surfaces + * Measure normalization + * Lazy processing + + http://www.neuro.uni-jena.de/cat12/CAT12-Manual.pdf#page=53 + + Examples + -------- + # Set the left surface files, both will be processed + lh_path_central = ... + + # Put here all surface files generated by CAT12 Segment, this is only required if the this approach is putted in + # a Node + surf_files = [rh.sphere..., lh.sphere..., etc] + + extract_additional_measures = ExtractAdditionalSurfaceParameters(left_central_surfaces=lh_path_central, + surface_files=surf_files) + extract_additional_measures.run() + + """ + input_spec = ExtractAdditionalSurfaceParametersInputSpec + output_spec = ExtractAdditionalSurfaceParametersOutputSpec + + def __init__(self, **inputs): + _local_version = SPMCommand().version + if _local_version and "12." in _local_version: + self._jobtype = "tools" + self._jobname = "cat.stools.surfextract" + + super().__init__(**inputs) + + def _list_outputs(self): + outputs = self._outputs().get() + + names_outputs = [(self.inputs.gyrification, 'gyrification'), (self.inputs.gmv, 'gmv'), + (self.inputs.area, 'area'), (self.inputs.depth, 'depth'), + (self.inputs.fractal_dimension, 'fractaldimension')] + + for filename in self.inputs.left_central_surfaces: + pth, base, ext = split_filename(filename) + # The first part of the filename is rh.central or lh.central + original_filename = base.split(".", 2)[-1] + for i, (extracted_parameter, parameter_name) in enumerate(names_outputs): + if extracted_parameter: + for hemisphere in ["rh", "lh"]: + all_files_hemisphere = hemisphere + '_extracted_files' + name_hemisphere = hemisphere + "_" + parameter_name + if isinstance(outputs[name_hemisphere], _Undefined): + outputs[name_hemisphere] = [] + if isinstance(outputs[all_files_hemisphere], _Undefined): + outputs[all_files_hemisphere] = [] + generated_filename = ".".join([hemisphere, parameter_name, original_filename]) + outputs[name_hemisphere].append(os.path.join(pth, generated_filename)) + + # Add all hemisphere files into one list, this is important because only the left hemisphere + # files are used as input in the Surface ROI Tools, fpr instance. + outputs[all_files_hemisphere].append(os.path.join(pth, generated_filename)) + + return outputs + + def _format_arg(self, opt, spec, val): + if opt == "left_central_surfaces": + return Cell2Str(val) + return super(ExtractAdditionalSurfaceParameters, self)._format_arg(opt, spec, val) + + +class ExtractROIBasedSurfaceMeasuresInputSpec(SPMCommandInputSpec): + # Only these files are given as input, yet the right hemisphere (rh) files should also be on the processing + # directory. + + surface_files = InputMultiPath(File(exists=True), desc="Surface data files. This variable should be a list " + "with all", mandatory=False, copyfile=False) + lh_roi_atlas = InputMultiPath(File(exists=True), field="rdata", desc="(Left) ROI Atlas. These are the ROI's ", + mandatory=True, copyfile=False) + + lh_surface_measure = InputMultiPath(File(exists=True), field="cdata", desc="(Left) Surface data files. ", + mandatory=True, copyfile=False) + rh_surface_measure = InputMultiPath(File(exists=True), desc="(Right) Surface data files.", + mandatory=False, copyfile=False) + + rh_roi_atlas = InputMultiPath(File(exists=True), desc="(Right) ROI Atlas. These are the ROI's ", + mandatory=False, copyfile=False) + + +class ExtractROIBasedSurfaceMeasures(SPMCommand): + """ + Extract ROI-based surface values + While ROI-based values for VBM (volume) data are automatically saved in the label folder as XML file it is + necessary to additionally extract these values for surface data (except for thickness which is automatically + extracted during segmentation). This has to be done after preprocessing the data and creating cortical surfaces. + + You can extract ROI-based values for cortical thickness but also for any other surface parameter that was extracted + using the Extract Additional Surface Parameters such as volume, area, depth, gyrification and fractal dimension. + + + http://www.neuro.uni-jena.de/cat12/CAT12-Manual.pdf#page=53 + + Examples + -------- + # Template surface files + lh_atlas = sys.argv[2] + rh_atlas = sys.argv[3] + + # Put here all surface files generated by CAT12 Segment, this is only required if the this approach is putted in + # a Node + surf_files = [rh.sphere..., lh.sphere..., etc] + + # Set the path to the left hemisphere measure file, both will be processed + lh_measure = .... + + extract_additional_measures = ExtractROIBasedSurfaceMeasures(surface_files=surf_files, + lh_surface_measure=lh_measure, + lh_roi_atlas=lh_atlas, + rh_roi_atlas=rh_atlas) + extract_additional_measures.run() + + + """ + + input_spec = ExtractROIBasedSurfaceMeasuresInputSpec + output_spec = None + + def __init__(self, **inputs): + _local_version = SPMCommand().version + if _local_version and "12." in _local_version: + self._jobtype = "tools" + self._jobname = "cat.stools.surf2roi" + + SPMCommand.__init__(self, **inputs) + + def _format_arg(self, opt, spec, val): + if opt == "lh_surface_measure": + return NestedCell(val) + elif opt == "lh_roi_atlas": + return Cell2Str(val) + + return super(ExtractROIBasedSurfaceMeasures, self)._format_arg(opt, spec, val) + + def _list_outputs(self): + pass + + +class Cell: + def __init__(self, arg): + self.arg = arg + + def to_string(self): + if isinstance(self.arg, list): + v = '\n'.join([f"'{el}'" for el in self.arg]) + else: + v = self.arg + return v + + +class NestedCell(Cell): + + def __str__(self): + return "{{%s}}" % self.to_string() + + +class Cell2Str(Cell): + + def __str__(self): + """Convert input to appropriate format for cat12 + """ + return "{%s}" % self.to_string() + From d8ca991d249f5a9436991e8b95d5a4c4213dff41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Tue, 9 Mar 2021 10:02:38 +0000 Subject: [PATCH 04/24] Improved documentation of the surface measures extraction. --- nipype/interfaces/cat12/surface.py | 47 +++++++++++++++--------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/nipype/interfaces/cat12/surface.py b/nipype/interfaces/cat12/surface.py index 34b7dbf892..08cc2b2d7a 100644 --- a/nipype/interfaces/cat12/surface.py +++ b/nipype/interfaces/cat12/surface.py @@ -73,16 +73,16 @@ class ExtractAdditionalSurfaceParameters(SPMCommand): Examples -------- - # Set the left surface files, both will be processed - lh_path_central = ... - - # Put here all surface files generated by CAT12 Segment, this is only required if the this approach is putted in - # a Node - surf_files = [rh.sphere..., lh.sphere..., etc] - - extract_additional_measures = ExtractAdditionalSurfaceParameters(left_central_surfaces=lh_path_central, - surface_files=surf_files) - extract_additional_measures.run() + >>> # Set the left surface files, both will be processed + >>> lh_path_central = 'lh.central.subject_filename.gii' + >>> # Put here all surface files generated by CAT12 Segment, this is only required if the this approach is putted in + >>> surf_files = ["lh.sphere.reg.subject_filename.gii", "rh.sphere.reg.subject_filename.gii", \ + "lh.sphere.subject_filename.gii", "rh.sphere.subject_filename.gii", \ + 'rh.central.subject_filename.gii', \ + 'lh.pbt.subject_filename', 'rh.pbt.subject_filename'] + >>> extract_additional_measures = ExtractAdditionalSurfaceParameters(left_central_surfaces=lh_path_central, \ + surface_files=surf_files) + >>> extract_additional_measures.run() # doctest: +SKIP """ input_spec = ExtractAdditionalSurfaceParametersInputSpec @@ -164,22 +164,23 @@ class ExtractROIBasedSurfaceMeasures(SPMCommand): Examples -------- - # Template surface files - lh_atlas = sys.argv[2] - rh_atlas = sys.argv[3] - - # Put here all surface files generated by CAT12 Segment, this is only required if the this approach is putted in - # a Node - surf_files = [rh.sphere..., lh.sphere..., etc] - + >>> # Template surface files + >>> lh_atlas = "lh.aparc_a2009s.freesurfer.annot" + >>> rh_atlas = "rh.aparc_a2009s.freesurfer.annot" + + >>> # Put here all surface files generated by CAT12 Segment, this is only required if the this approach is putted in a Node + >>> surf_files = ["lh.sphere.reg.subject_filename.gii", "rh.sphere.reg.subject_filename.gii", \ + "lh.sphere.subject_filename.gii", "rh.sphere.subject_filename.gii", \ + 'lh.central.subject_filename.gii', 'rh.central.subject_filename.gii', \ + 'lh.pbt.subject_filename', 'rh.pbt.subject_filename'] # Set the path to the left hemisphere measure file, both will be processed - lh_measure = .... + >>> lh_measure = "lh.area.subject_filename" - extract_additional_measures = ExtractROIBasedSurfaceMeasures(surface_files=surf_files, - lh_surface_measure=lh_measure, - lh_roi_atlas=lh_atlas, + >>> extract_additional_measures = ExtractROIBasedSurfaceMeasures(surface_files=surf_files, \ + lh_surface_measure=lh_measure, \ + lh_roi_atlas=lh_atlas, \ rh_roi_atlas=rh_atlas) - extract_additional_measures.run() + >>> extract_additional_measures.run() # doctest: +SKIP """ From c906740d8ea66f951866adc36e4d30a878fc402a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Tue, 9 Mar 2021 11:31:48 +0000 Subject: [PATCH 05/24] - Added examples to the segmentation. --- nipype/interfaces/cat12/preprocess.py | 222 +++++++++++++++++++++----- 1 file changed, 185 insertions(+), 37 deletions(-) diff --git a/nipype/interfaces/cat12/preprocess.py b/nipype/interfaces/cat12/preprocess.py index 5fd58f09a5..eb3c3c5d4b 100644 --- a/nipype/interfaces/cat12/preprocess.py +++ b/nipype/interfaces/cat12/preprocess.py @@ -10,35 +10,159 @@ class CAT12SegmentInputSpec(SPMCommandInputSpec): + """ + CAT12: Segmentation + This toolbox is an extension to the default segmentation in SPM12, but uses a completely different segmentation + approach. + The segmentation approach is based on an Adaptive Maximum A Posterior (MAP) technique without the need for a priori + information about tissue probabilities. That is, the Tissue Probability Maps (TPM) are not used constantly in the + sense of the classical Unified Segmentation approach (Ashburner et. al. 2005), but just for spatial normalization. + The following AMAP estimation is adaptive in the sense that local variations of the parameters (i.e., means and + variance) are modeled as slowly varying spatial functions (Rajapakse et al. 1997). This not only accounts for + intensity inhomogeneities but also for other local variations of intensity. + Additionally, the segmentation approach uses a Partial Volume Estimation (PVE) with a simplified mixed model of at + most two tissue types (Tohka et al. 2004). We start with an initial segmentation into three pure classes: gray + matter (GM), white matter (WM), and cerebrospinal fluid (CSF) based on the above described AMAP estimation. The + initial segmentation is followed by a PVE of two additional mixed classes: GM-WM and GM-CSF. This results in an + estimation of the amount (or fraction) of each pure tissue type present in every voxel (as single voxels - given by + Another important extension to the SPM12 segmentation is the integration of the Dartel or Geodesic Shooting + registration into the toolbox by an already existing Dartel/Shooting template in MNI space. This template was + derived from 555 healthy control subjects of the IXI-database (http://www.brain-development.org) and provides the + several Dartel or Shooting iterations. Thus, for the majority of studies the creation of sample-specific templates + is not necessary anymore and is mainly recommended for children data.'}; + + http://www.neuro.uni-jena.de/cat12/CAT12-Manual.pdf#page=15 + + Examples + -------- + path_mr = 'structural.nii' + cat = CAT12Segment(in_files=path_mr) + cat.run() # doctest: +SKIP + """ + in_files = InputMultiPath(ImageFileSPM(exists=True), field="data", desc="file to segment", mandatory=True, copyfile=False) - tpm = InputMultiPath(ImageFileSPM(exists=True), field="tpm", desc="Tissue Probability Maps", mandatory=False, + help_tpm = 'Tissue Probability Map. Select the tissue probability image that includes 6 tissue probability ' \ + 'classes for (1) grey matter, (2) white matter, (3) cerebrospinal fluid, (4) bone, (5) non-brain soft ' \ + 'tissue, and (6) the background. CAT uses the TPM only for the initial SPM segmentation.' + tpm = InputMultiPath(ImageFileSPM(exists=True), field="tpm", desc=help_tpm, mandatory=False, copyfile=False) n_jobs = traits.trait_types.Int(1, usedefault=True, mandatory=True, field="nproc", desc="Number of threads") use_prior = traits.trait_types.Str(field="useprior", usedefault=True) + affine_reg_help = 'Affine Regularization. The procedure is a local optimisation, so it needs reasonable initial ' \ + 'starting estimates. Images should be placed in approximate alignment using the Display ' \ + 'function of SPM before beginning. A Mutual Information affine registration with the tissue ' \ + 'probability maps (D''Agostino et al, 2004) is used to achieve approximate alignment.' affine_regularization = traits.trait_types.Str(default_value="mni", - field="opts.affreg", usedefault=True) + field="opts.affreg", usedefault=True, desc=affine_reg_help) + bias_acc_help = "Strength of the SPM inhomogeneity (bias) correction that simultaneously controls the SPM biasreg, " \ + "biasfwhm, samp (resolution), and tol (iteration) parameter." power_spm_inhomogeneity_correction = traits.trait_types.Float(default_value=0.5, field='opts.biasacc', - usedefault=True) + usedefault=True, + desc=bias_acc_help) # Extended options for CAT12 preprocessing - affine_preprocessing = traits.trait_types.Int(1070, field="extopts.APP", usedefault=True) - initial_segmentation = traits.trait_types.Int(0, field="extopts.spm_kamap", usedefault=True) - local_adaptive_seg = traits.trait_types.Float(0.5, field="extopts.LASstr", usedefault=True) - skull_strip = traits.trait_types.Float(2, field="extopts.gcutstr", usedefault=True) - wm_hyper_intensity_correction = traits.trait_types.Int(1, field="extopts.WMHC", usedefault=True) - spatial_registration = traits.trait_types.Int(1, field="extopts.WMHC", usedefault=True) - voxel_size = traits.trait_types.Float(1.5, field="extopts.vox", usedefault=True) + help_app = 'Affine registration and SPM preprocessing can fail in some subjects with deviating anatomy (e.g. ' \ + 'other species/neonates) or in images with strong signal inhomogeneities, or untypical intensities ' \ + '(e.g. synthetic images). An initial bias correction can help to reduce such problems (see details ' \ + 'below). Recommended are the "default" and "full" option.' + affine_preprocessing = traits.trait_types.Int(1070, field="extopts.APP", desc=help_app, usedefault=True) + + help_initial_seg = 'In rare cases the Unified Segmentation can fail in highly abnormal brains, where e.g. the ' \ + 'cerebrospinal fluid of superlarge ventricles (hydrocephalus) were classified as white matter.' \ + ' However, if the affine registration is correct, the AMAP segmentation with an ' \ + 'prior-independent k-means initialization can be used to replace the SPM brain tissue ' \ + 'classification. Moreover, if the default Dartel and Shooting registrations will fail then the' \ + ' "Optimized Shooting - superlarge ventricles" option for "Spatial registration" is required! ' \ + 'Values: \nnone: 0;\nlight: 1;\nfull: 2;\ndefault: 1070.' + initial_segmentation = traits.trait_types.Int(0, field="extopts.spm_kamap", desc=help_initial_seg, usedefault=True) + + help_las = 'Additionally to WM-inhomogeneities, GM intensity can vary across different regions such as the motor' \ + ' cortex, the basal ganglia, or the occipital lobe. These changes have an anatomical background ' \ + '(e.g. iron content, myelinization), but are dependent on the MR-protocol and often lead to ' \ + 'underestimation of GM at higher intensities and overestimation of CSF at lower intensities. ' \ + 'Therefore, a local intensity transformation of all tissue classes is used to reduce these effects in ' \ + 'the image. This local adaptive segmentation (LAS) is applied before the final AMAP segmentation.' \ + 'Possible Values: \nSPM Unified Segmentation: 0 \nk-means AMAP: 2' + local_adaptive_seg = traits.trait_types.Float(0.5, field="extopts.LASstr", usedefault=True, desc=help_las) + + help_gcutstr = 'Method of initial skull-stripping before AMAP segmentation. The SPM approach works quite stable ' \ + 'for the majority of data. However, in some rare cases parts of GM (i.e. in frontal lobe) might ' \ + 'be cut. If this happens the GCUT approach is a good alternative. GCUT is a graph-cut/region-' \ + 'growing approach starting from the WM area. APRG (adaptive probability region-growing) is a new' \ + ' method that refines the probability maps of the SPM approach by region-growing techniques of ' \ + 'the gcut approach with a final surface-based optimization strategy. This is currently the method ' \ + 'with the most accurate and reliable results. If you use already skull-stripped data you can turn' \ + ' off skull-stripping although this is automaticaly detected in most cases. Please note that the' \ + ' choice of the skull-stripping method will also influence the estimation of TIV, because the' \ + ' methods mainly differ in the handling of the outer CSF around the cortical surface. ' \ + '\nPossible Values:\n - none (already skull-stripped): -1;\n - SPM approach: 0; ' \ + '\n - GCUT approach: 0.50; \n - APRG approach: 2' + skull_strip = traits.trait_types.Float(2, field="extopts.gcutstr", desc=help_gcutstr, usedefault=True) + + help_wmhc = 'WARNING: Please note that the detection of WM hyperintensies is still under development and does ' \ + 'not have the same accuracy as approaches that additionally consider FLAIR images (e.g. Lesion ' \ + 'Segmentation Toolbox)! In aging or (neurodegenerative) diseases WM intensity can be reduced ' \ + 'locally in T1 or increased in T2/PD images. These so-called WM hyperintensies (WMHs) can lead to ' \ + 'preprocessing errors. Large GM areas next to the ventricle can cause normalization problems. ' \ + 'Therefore, a temporary correction for normalization is useful if WMHs are expected. CAT allows ' \ + 'different ways to handle WMHs: ' \ + '\n0) No Correction (handled as GM). \n1) Temporary (internal) correction as WM for spatial ' \ + 'normalization and estimation of cortical thickness. \n2) Permanent correction to WM. ' + wm_hyper_intensity_correction = traits.trait_types.Int(1, field="extopts.WMHC", desc=help_wmhc, usedefault=True) + + help_vox = 'The (isotropic) voxel sizes of any spatially normalised written images. A non-finite value will be ' \ + 'replaced by the average voxel size of the tissue probability maps used by the segmentation.' + voxel_size = traits.trait_types.Float(1.5, field="extopts.vox", desc=help_vox, usedefault=True) + + help_resampling = 'Internal resampling for preprocessing.\n The default fixed image resolution offers a good ' \ + 'trade-off between optimal quality and preprocessing time and memory demands. Standard ' \ + 'structural data with a voxel resolution around 1 mm or even data with high in-plane resolution' \ + ' and large slice thickness (e.g. 0.5x0.5x1.5 mm) will benefit from this setting. If you have' \ + ' higher native resolutions the highres option "Fixed 0.8 mm" will sometimes offer slightly' \ + ' better preprocessing quality with an increase of preprocessing time and memory demands. In' \ + ' case of even higher resolutions and high signal-to-noise ratio (e.g. for 7 T data) the ' \ + '"Best native" option will process the data on the highest native resolution. I.e. a resolution' \ + ' of 0.4x0.7x1.0 mm will be interpolated to 0.4x0.4x0.4 mm. A tolerance range of 0.1 mm is used' \ + ' to avoid interpolation artifacts, i.e. a resolution of 0.95x1.01x1.08 mm will not be ' \ + 'interpolated in case of the "Fixed 1.0 mm"! This "optimal" option prefers an isotropic voxel ' \ + 'size with at least 1.1 mm that is controlled by the median voxel size and a volume term that ' \ + 'penalizes highly anisotropic voxels.' \ + 'Values:\nOptimal: [1.0 0.1]\nFixed 1.0 mm: [1.0 0.1];\nFixed 0.8 mm:[0.8 0.1]' \ + '\nBest native: [0.5 0.1]' internal_resampling_process = traits.trait_types.Tuple(traits.trait_types.Float(1), traits.trait_types.Float(0.1), minlen=2, maxlen=2, - field="extopts.restypes.optimal", usedefault=True) - ignore_errors = traits.trait_types.Int(1, field="extopts.ignoreErrors", usedefault=True) + field="extopts.restypes.optimal", desc="help_resampling", + usedefault=True) + errors_help = 'Error handling.\nTry to catch preprocessing errors and continue with the next data set or ignore ' \ + 'all warnings (e.g., bad intensities) and use an experimental pipeline which is still in ' \ + 'development. In case of errors, CAT continues with the next subject if this option is enabled. If ' \ + 'the experimental option with backup functions is selected and warnings occur, CAT will try to use' \ + ' backup routines and skip some processing steps which require good T1 contrasts (e.g., LAS). If ' \ + 'you want to avoid processing of critical data and ensure that only the main pipeline is used then' \ + ' select the option "Ignore errors (continue with the next subject)". It is strongly recommended to' \ + ' check for preprocessing problems, especially with non-T1 contrasts. ' \ + '\nValues:\nnone: 0,\ndefault: 1,\ndetails: 2.' + ignore_errors = traits.trait_types.Int(1, field="extopts.ignoreErrors", desc=errors_help, usedefault=True) # Writing options - surface_and_thickness_estimation = traits.trait_types.Int(1, field="surface", usedefault=True) + help_surf = 'Surface and thickness estimation. \nUse projection-based thickness (PBT) (Dahnke et al. 2012) to' \ + ' estimate cortical thickness and to create the central cortical surface for left and right ' \ + 'hemisphere. Surface reconstruction includes topology correction (Yotter et al. 2011), spherical ' \ + 'inflation (Yotter et al.) and spherical registration. Additionally you can also estimate surface ' \ + 'parameters such as gyrification, cortical complexity or sulcal depth that can be subsequently ' \ + 'analyzed at each vertex of the surface. Please note, that surface reconstruction and spherical ' \ + 'registration additionally requires about 20-60 min of computation time. A fast (1-3 min) surface ' \ + 'pipeline is available for visual preview (e.g., to check preprocessing quality) in the ' \ + 'cross-sectional, but not in the longitudinal pipeline. Only the initial surfaces are created with a' \ + ' lower resolution and without topology correction, spherical mapping and surface registration. ' \ + 'Please note that the files with the estimated surface thickness can therefore not be used for ' \ + 'further analysis! For distinction, these files contain "preview" in their filename and they' \ + ' are not available as batch dependencies objects. ' + surface_and_thickness_estimation = traits.trait_types.Int(1, field="surface", desc=help_surf, usedefault=True) surface_measures = traits.trait_types.Int(1, field="output.surf_measures", usedefault=True) # Templates @@ -50,41 +174,71 @@ class CAT12SegmentInputSpec(SPMCommandInputSpec): own_atlas = InputMultiPath(ImageFileSPM(exists=True), field="output.ROImenu.atlases.ownatlas", desc="Own Atlas", mandatory=False, copyfile=False) + dartel_help = 'This option is to export data into a form that can be used with DARTEL. The SPM default is to ' \ + 'only apply rigid body transformation. However, a more appropriate option is to apply affine ' \ + 'transformation, because the additional scaling of the images requires less deformations to ' \ + 'non-linearly register brains to the template.' + # Grey matter - gm_output_native = traits.trait_types.Bool(False, field="output.GM.native", usedefault=True) - gm_output_modulated = traits.trait_types.Bool(True, field="output.GM.mod", usedefault=True) - gm_output_dartel = traits.trait_types.Bool(False, field="output.GM.dartel", usedefault=True) + gm_desc = 'Options to save grey matter images.' + gm_output_native = traits.trait_types.Bool(False, field="output.GM.native", usedefault=True, desc=gm_desc) + gm_output_modulated = traits.trait_types.Bool(True, field="output.GM.mod", usedefault=True, desc=gm_desc) + gm_output_dartel = traits.trait_types.Bool(False, field="output.GM.dartel", usedefault=True, desc=gm_desc) # White matter - wm_output_native = traits.trait_types.Bool(False, field="output.WM.native", usedefault=True) - wm_output_modulated = traits.trait_types.Bool(True, field="output.WM.mod", usedefault=True) - wm_output_dartel = traits.trait_types.Bool(False, field="output.WM.dartel", usedefault=True) + wm_desc = 'Options to save white matter images.' + wm_output_native = traits.trait_types.Bool(False, field="output.WM.native", usedefault=True, desc=wm_desc) + wm_output_modulated = traits.trait_types.Bool(True, field="output.WM.mod", usedefault=True, desc=wm_desc) + wm_output_dartel = traits.trait_types.Bool(False, field="output.WM.dartel", usedefault=True, desc=wm_desc) # CSF matter - csf_output_native = traits.trait_types.Bool(False, field="output.CSF.native", usedefault=True) - csf_output_modulated = traits.trait_types.Bool(True, field="output.CSF.mod", usedefault=True) - csf_output_dartel = traits.trait_types.Bool(False, field="output.CSF.dartel", usedefault=True) + csf_desc = 'Options to save CSF images.' + csf_output_native = traits.trait_types.Bool(False, field="output.CSF.native", usedefault=True, desc=csf_desc) + csf_output_modulated = traits.trait_types.Bool(True, field="output.CSF.mod", usedefault=True, desc=csf_desc) + csf_output_dartel = traits.trait_types.Bool(False, field="output.CSF.dartel", usedefault=True, desc=csf_desc) # Labels - label_native = traits.trait_types.Bool(False, field="output.label.native", usedefault=True) - label_warped = traits.trait_types.Bool(True, field="output.label.warped", usedefault=True) - label_dartel = traits.trait_types.Bool(False, field="output.label.dartel", usedefault=True) - output_labelnative = traits.trait_types.Bool(False, field="output.labelnative", usedefault=True) + label_desc = 'This is the option to save a labeled version of your segmentations for fast visual comparision. ' \ + 'Labels are saved as Partial Volume Estimation (PVE) values with different mix classes for GM-WM ' \ + '(2.5) and GM-CSF (1.5). BG=0, CSF=1, GM=2, WM=3, WMH=4 (if WMHC=3), SL=1.5 (if SLC)' + label_native = traits.trait_types.Bool(False, field="output.label.native", usedefault=True, desc=label_desc) + label_warped = traits.trait_types.Bool(True, field="output.label.warped", usedefault=True, desc=label_desc) + label_dartel = traits.trait_types.Bool(False, field="output.label.dartel", usedefault=True, desc=label_desc) + output_labelnative = traits.trait_types.Bool(False, field="output.labelnative", usedefault=True, desc=label_desc) # Bias save_bias_corrected = traits.trait_types.Bool(True, field="output.bias.warped", usedefault=True) # las - las_native = traits.trait_types.Bool(False, field="output.las.native", usedefault=True) - las_warped = traits.trait_types.Bool(True, field="output.las.warped", usedefault=True) - las_dartel = traits.trait_types.Bool(False, field="output.las.dartel", usedefault=True) + las_desc = 'This is the option to save a bias, noise, and local intensity corrected version of the original T1' \ + ' image. MR images are usually corrupted by a smooth, spatially varying artifact that modulates the' \ + ' intensity of the image (bias). These artifacts, although not usually a problem for visual ' \ + 'inspection, can impede automated processing of the images. The bias corrected version should have ' \ + 'more uniform intensities within the different types of tissues and can be saved in native space ' \ + 'and/or normalised. Noise is corrected by an adaptive non-local mean (NLM) filter (Manjon 2008, ' \ + 'Medical Image Analysis 12).' + las_native = traits.trait_types.Bool(False, field="output.las.native", usedefault=True, desc=las_desc) + las_warped = traits.trait_types.Bool(True, field="output.las.warped", usedefault=True, desc=las_desc) + las_dartel = traits.trait_types.Bool(False, field="output.las.dartel", usedefault=True, desc=las_desc) # Jacobian Warped - jacobianwarped = traits.trait_types.Bool(True, field="output.jacobianwarped", usedefault=True) + help_jacobian = 'This is the option to save the Jacobian determinant, which expresses local volume changes. This' \ + ' image can be used in a pure deformation based morphometry (DBM) design. Please note that the' \ + ' affine part of the deformation field is ignored. Thus, there is no need for any additional' \ + ' correction for different brain sizes using ICV.' + jacobianwarped = traits.trait_types.Bool(True, field="output.jacobianwarped", usedefault=True, desc=help_jacobian) # Deformation Fields + help_warp = 'Deformation fields can be saved to disk, and used by the Deformations Utility and/or applied to ' \ + 'coregistered data from other modalities (e.g. fMRI). For spatially normalising images to MNI space,' \ + ' you will need the forward deformation, whereas for spatially normalising (eg) GIFTI surface files,' \ + ' you''ll need the inverse. It is also possible to transform data in MNI space on to the individual' \ + ' subject, which also requires the inverse transform. Deformations are saved as .nii files, which' \ + ' contain three volumes to encode the x, y and z coordinates.' \ + '\nValues: No:[0 0];\nImage->Template (forward): [1 0];\nTemplate->Image (inverse): [0 1]; ' \ + '\ninverse + forward: [1 1]' warps = traits.trait_types.Tuple(traits.trait_types.Int(1), traits.trait_types.Int(0), minlen=2, maxlen=2, - field="output.warps", usedefault=True) + field="output.warps", usedefault=True, desc=help_warp) class CAT12SegmentOutputSpec(TraitedSpec): @@ -208,9 +362,3 @@ def _list_outputs(self): use_ext=False) return outputs - - -if __name__ == '__main__': - path_mr = sys.argv[1] - cat = CAT12Segment(in_files=path_mr) - cat.run() From 2885499e7fe935a96d9ed4660596359e472e1a4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Tue, 9 Mar 2021 11:49:50 +0000 Subject: [PATCH 06/24] Improved documentation. --- nipype/interfaces/cat12/preprocess.py | 48 +++++++++++++-------------- nipype/interfaces/cat12/surface.py | 27 +++++++-------- 2 files changed, 36 insertions(+), 39 deletions(-) diff --git a/nipype/interfaces/cat12/preprocess.py b/nipype/interfaces/cat12/preprocess.py index eb3c3c5d4b..767ad58f29 100644 --- a/nipype/interfaces/cat12/preprocess.py +++ b/nipype/interfaces/cat12/preprocess.py @@ -35,9 +35,9 @@ class CAT12SegmentInputSpec(SPMCommandInputSpec): Examples -------- - path_mr = 'structural.nii' - cat = CAT12Segment(in_files=path_mr) - cat.run() # doctest: +SKIP + >>> path_mr = 'structural.nii' + >>> cat = CAT12Segment(in_files=path_mr) + >>> cat.run() # doctest: +SKIP """ in_files = InputMultiPath(ImageFileSPM(exists=True), field="data", desc="file to segment", mandatory=True, @@ -245,50 +245,50 @@ class CAT12SegmentOutputSpec(TraitedSpec): ########################################## # Label XML files ########################################## - label_files = List(File(exists=True)) + label_files = List(File(exists=True), desc="Files with the labeled version of the segmentations.") - label_rois = File(exists=True, desc="ROIs Volumes") - label_roi = File(exists=True, desc="ROI volumes") + label_rois = File(exists=True, desc="Files with labeled version of segmentations of ROIs Volumes") + label_roi = File(exists=True, desc="Files with labeled version of segmentations of ROI Volumes") ########################################## # MRI .nii files ########################################## - mri_images = List(File(exists=True)) + mri_images = List(File(exists=True), desc="Different segmented images.") # Grey Matter - gm_modulated_image = File(exists=True) - gm_dartel_image = File(exists=True) - gm_native_image = File(exists=True) + gm_modulated_image = File(exists=True, desc="Grey matter modulated image.") + gm_dartel_image = File(exists=True, desc="Grey matter dartel image.") + gm_native_image = File(exists=True, desc="Grey matter native space.") # White Matter - wm_modulated_image = File(exists=True) - wm_dartel_image = File(exists=True) - wm_native_image = File(exists=True) + wm_modulated_image = File(exists=True, desc="White matter modulated image.") + wm_dartel_image = File(exists=True, desc="White matter dartel image.") + wm_native_image = File(exists=True, desc="White matter in native space.") # CSF - csf_modulated_image = File(exists=True) - csf_dartel_image = File(exists=True) - csf_native_image = File(exists=True) + csf_modulated_image = File(exists=True, desc="CSF modulated image.") + csf_dartel_image = File(exists=True, desc="CSF dartel image.") + csf_native_image = File(exists=True, desc="CSF in native space.") - bias_corrected_image = File(exists=True) + bias_corrected_image = File(exists=True, desc="Bias corrected image") ########################################## # Surface files ########################################## - surface_files = List(File(exists=True)) + surface_files = List(File(exists=True), desc="Surface files") # Right hemisphere - rh_central_surface = File(exists=True) - rh_sphere_surface = File(exists=True) + rh_central_surface = File(exists=True, desc="Central right hemisphere files") + rh_sphere_surface = File(exists=True, desc="Sphere right hemisphere files") # Left hemisphere - lh_central_surface = File(exists=True) - lh_sphere_surface = File(exists=True) + lh_central_surface = File(exists=True, desc="Central left hemisphere files") + lh_sphere_surface = File(exists=True, desc="Sphere left hemisphere files") # Report files - report_files = List(File(exists=True)) - report = File(exists=True) + report_files = List(File(exists=True), desc="Report files.") + report = File(exists=True, desc="Report file.") class CAT12Segment(SPMCommand): diff --git a/nipype/interfaces/cat12/surface.py b/nipype/interfaces/cat12/surface.py index 08cc2b2d7a..312245b8ce 100644 --- a/nipype/interfaces/cat12/surface.py +++ b/nipype/interfaces/cat12/surface.py @@ -31,26 +31,23 @@ class ExtractAdditionalSurfaceParametersInputSpec(SPMCommandInputSpec): class ExtractAdditionalSurfaceParametersOutputSpec(TraitedSpec): - lh_extracted_files = List(File(exists=True)) - rh_extracted_files = List(File(exists=True)) + lh_extracted_files = List(File(exists=True), desc="Files of left Hemisphere extracted measures") + rh_extracted_files = List(File(exists=True), desc="Files of right Hemisphere extracted measures") - lh_gyrification = List(File(exists=True)) - rh_gyrification = List(File(exists=True)) + lh_gyrification = List(File(exists=True), desc="Gyrification of left Hemisphere") + rh_gyrification = List(File(exists=True), desc="Gyrification of right Hemisphere") - lh_gyrification_resampled = List(File(exists=True)) - rh_gyrification_resampled = List(File(exists=True)) + lh_gmv = List(File(exists=True), desc="Grey matter volume of left Hemisphere") + rh_gmv = List(File(exists=True), desc="Grey matter volume of right Hemisphere") - lh_gmv = List(File(exists=True)) - rh_gmv = List(File(exists=True)) + lh_area = List(File(exists=True), desc="Area of left Hemisphere") + rh_area = List(File(exists=True), desc="Area of right Hemisphere") - lh_area = List(File(exists=True)) - rh_area = List(File(exists=True)) + lh_depth = List(File(exists=True), desc="Depth of left Hemisphere") + rh_depth = List(File(exists=True), desc="Depth of right Hemisphere") - lh_depth = List(File(exists=True)) - rh_depth = List(File(exists=True)) - - lh_fractaldimension = List(File(exists=True)) - rh_fractaldimension = List(File(exists=True)) + lh_fractaldimension = List(File(exists=True), desc="Fractal Dimension of left Hemisphere") + rh_fractaldimension = List(File(exists=True), desc="Fractal Dimension of right Hemisphere") class ExtractAdditionalSurfaceParameters(SPMCommand): From 98f118dcaf959fc77240853224961beb2771d928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Tue, 9 Mar 2021 12:02:20 +0000 Subject: [PATCH 07/24] Added outputs to ExtractROIBasedSurfaceMeasures interface. --- nipype/interfaces/cat12/preprocess.py | 6 +++--- nipype/interfaces/cat12/surface.py | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/nipype/interfaces/cat12/preprocess.py b/nipype/interfaces/cat12/preprocess.py index 767ad58f29..c6746f110e 100644 --- a/nipype/interfaces/cat12/preprocess.py +++ b/nipype/interfaces/cat12/preprocess.py @@ -245,10 +245,10 @@ class CAT12SegmentOutputSpec(TraitedSpec): ########################################## # Label XML files ########################################## - label_files = List(File(exists=True), desc="Files with the labeled version of the segmentations.") + label_files = List(File(exists=True), desc="Files with the measures extracted for OI ands ROIs") - label_rois = File(exists=True, desc="Files with labeled version of segmentations of ROIs Volumes") - label_roi = File(exists=True, desc="Files with labeled version of segmentations of ROI Volumes") + label_rois = File(exists=True, desc="Files with thickness values of ROIs.") + label_roi = File(exists=True, desc="Files with thickness values of ROI.") ########################################## # MRI .nii files diff --git a/nipype/interfaces/cat12/surface.py b/nipype/interfaces/cat12/surface.py index 312245b8ce..aab38d1aa0 100644 --- a/nipype/interfaces/cat12/surface.py +++ b/nipype/interfaces/cat12/surface.py @@ -146,6 +146,10 @@ class ExtractROIBasedSurfaceMeasuresInputSpec(SPMCommandInputSpec): mandatory=False, copyfile=False) +class ExtractROIBasedSurfaceMeasuresOutputSpec(TraitedSpec): + label_files = List(File(exists=True), desc="Files with the measures extracted for ROIs.") + + class ExtractROIBasedSurfaceMeasures(SPMCommand): """ Extract ROI-based surface values @@ -183,7 +187,7 @@ class ExtractROIBasedSurfaceMeasures(SPMCommand): """ input_spec = ExtractROIBasedSurfaceMeasuresInputSpec - output_spec = None + output_spec = ExtractROIBasedSurfaceMeasuresOutputSpec def __init__(self, **inputs): _local_version = SPMCommand().version @@ -202,7 +206,15 @@ def _format_arg(self, opt, spec, val): return super(ExtractROIBasedSurfaceMeasures, self)._format_arg(opt, spec, val) def _list_outputs(self): - pass + outputs = self._outputs().get() + + outputs["label_files"] = [] + for f in self.inputs.lh_roi_atlas: + pth, base, ext = split_filename(f) + + outputs["label_files"].extend([os.path.join(os.path.join(pth, "label"), f) for f in + os.listdir(os.path.join(pth, "label")) + if os.path.isfile(os.path.join(os.path.join(pth, "label"), f))]) class Cell: From 7713f788845faab77c11925711551c0dfdde95a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Tue, 9 Mar 2021 14:20:47 +0000 Subject: [PATCH 08/24] Added files to test the interfaces. --- nipype/interfaces/cat12/surface.py | 20 +++++++++---------- .../data/lh.aparc_a2009s.freesurfer.annot | 0 nipype/testing/data/lh.area.structural | 0 nipype/testing/data/lh.central.structural.gii | 0 nipype/testing/data/lh.pbt.structural | 0 .../testing/data/lh.sphere.reg.structural.gii | 0 nipype/testing/data/lh.sphere.structural.gii | 0 .../data/rh.aparc_a2009s.freesurfer.annot | 0 nipype/testing/data/rh.central.structural.gii | 0 nipype/testing/data/rh.pbt.structural | 0 .../testing/data/rh.sphere.reg.structural.gii | 0 nipype/testing/data/rh.sphere.structural.gii | 0 12 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 nipype/testing/data/lh.aparc_a2009s.freesurfer.annot create mode 100644 nipype/testing/data/lh.area.structural create mode 100644 nipype/testing/data/lh.central.structural.gii create mode 100644 nipype/testing/data/lh.pbt.structural create mode 100644 nipype/testing/data/lh.sphere.reg.structural.gii create mode 100644 nipype/testing/data/lh.sphere.structural.gii create mode 100644 nipype/testing/data/rh.aparc_a2009s.freesurfer.annot create mode 100644 nipype/testing/data/rh.central.structural.gii create mode 100644 nipype/testing/data/rh.pbt.structural create mode 100644 nipype/testing/data/rh.sphere.reg.structural.gii create mode 100644 nipype/testing/data/rh.sphere.structural.gii diff --git a/nipype/interfaces/cat12/surface.py b/nipype/interfaces/cat12/surface.py index aab38d1aa0..b8583c3e9b 100644 --- a/nipype/interfaces/cat12/surface.py +++ b/nipype/interfaces/cat12/surface.py @@ -71,12 +71,12 @@ class ExtractAdditionalSurfaceParameters(SPMCommand): Examples -------- >>> # Set the left surface files, both will be processed - >>> lh_path_central = 'lh.central.subject_filename.gii' + >>> lh_path_central = 'lh.central.structural.gii' >>> # Put here all surface files generated by CAT12 Segment, this is only required if the this approach is putted in - >>> surf_files = ["lh.sphere.reg.subject_filename.gii", "rh.sphere.reg.subject_filename.gii", \ - "lh.sphere.subject_filename.gii", "rh.sphere.subject_filename.gii", \ - 'rh.central.subject_filename.gii', \ - 'lh.pbt.subject_filename', 'rh.pbt.subject_filename'] + >>> surf_files = ["lh.sphere.reg.structural.gii", "rh.sphere.reg.structural.gii", \ + "lh.sphere.structural.gii", "rh.sphere.structural.gii", \ + 'rh.central.structural.gii', \ + 'lh.pbt.structural', 'rh.pbt.structural'] >>> extract_additional_measures = ExtractAdditionalSurfaceParameters(left_central_surfaces=lh_path_central, \ surface_files=surf_files) >>> extract_additional_measures.run() # doctest: +SKIP @@ -170,12 +170,12 @@ class ExtractROIBasedSurfaceMeasures(SPMCommand): >>> rh_atlas = "rh.aparc_a2009s.freesurfer.annot" >>> # Put here all surface files generated by CAT12 Segment, this is only required if the this approach is putted in a Node - >>> surf_files = ["lh.sphere.reg.subject_filename.gii", "rh.sphere.reg.subject_filename.gii", \ - "lh.sphere.subject_filename.gii", "rh.sphere.subject_filename.gii", \ - 'lh.central.subject_filename.gii', 'rh.central.subject_filename.gii', \ - 'lh.pbt.subject_filename', 'rh.pbt.subject_filename'] + >>> surf_files = ["lh.sphere.reg.structural.gii", "rh.sphere.reg.structural.gii", \ + "lh.sphere.structural.gii", "rh.sphere.structural.gii", \ + 'lh.central.structural.gii', 'rh.central.structural.gii', \ + 'lh.pbt.structural', 'rh.pbt.structural'] # Set the path to the left hemisphere measure file, both will be processed - >>> lh_measure = "lh.area.subject_filename" + >>> lh_measure = "lh.area.structural" >>> extract_additional_measures = ExtractROIBasedSurfaceMeasures(surface_files=surf_files, \ lh_surface_measure=lh_measure, \ diff --git a/nipype/testing/data/lh.aparc_a2009s.freesurfer.annot b/nipype/testing/data/lh.aparc_a2009s.freesurfer.annot new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/lh.area.structural b/nipype/testing/data/lh.area.structural new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/lh.central.structural.gii b/nipype/testing/data/lh.central.structural.gii new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/lh.pbt.structural b/nipype/testing/data/lh.pbt.structural new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/lh.sphere.reg.structural.gii b/nipype/testing/data/lh.sphere.reg.structural.gii new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/lh.sphere.structural.gii b/nipype/testing/data/lh.sphere.structural.gii new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/rh.aparc_a2009s.freesurfer.annot b/nipype/testing/data/rh.aparc_a2009s.freesurfer.annot new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/rh.central.structural.gii b/nipype/testing/data/rh.central.structural.gii new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/rh.pbt.structural b/nipype/testing/data/rh.pbt.structural new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/rh.sphere.reg.structural.gii b/nipype/testing/data/rh.sphere.reg.structural.gii new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/rh.sphere.structural.gii b/nipype/testing/data/rh.sphere.structural.gii new file mode 100644 index 0000000000..e69de29bb2 From 44b860534dbcca7fc1a756fad18a3432ff1e9521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Tue, 9 Mar 2021 14:20:47 +0000 Subject: [PATCH 09/24] Added files to test the interfaces. --- nipype/interfaces/cat12/surface.py | 20 +++++++++---------- .../data/lh.aparc_a2009s.freesurfer.annot | 0 nipype/testing/data/lh.area.structural | 0 nipype/testing/data/lh.central.structural.gii | 0 nipype/testing/data/lh.pbt.structural | 0 .../testing/data/lh.sphere.reg.structural.gii | 0 nipype/testing/data/lh.sphere.structural.gii | 0 .../data/rh.aparc_a2009s.freesurfer.annot | 0 nipype/testing/data/rh.central.structural.gii | 0 nipype/testing/data/rh.pbt.structural | 0 .../testing/data/rh.sphere.reg.structural.gii | 0 nipype/testing/data/rh.sphere.structural.gii | 0 12 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 nipype/testing/data/lh.aparc_a2009s.freesurfer.annot create mode 100644 nipype/testing/data/lh.area.structural create mode 100644 nipype/testing/data/lh.central.structural.gii create mode 100644 nipype/testing/data/lh.pbt.structural create mode 100644 nipype/testing/data/lh.sphere.reg.structural.gii create mode 100644 nipype/testing/data/lh.sphere.structural.gii create mode 100644 nipype/testing/data/rh.aparc_a2009s.freesurfer.annot create mode 100644 nipype/testing/data/rh.central.structural.gii create mode 100644 nipype/testing/data/rh.pbt.structural create mode 100644 nipype/testing/data/rh.sphere.reg.structural.gii create mode 100644 nipype/testing/data/rh.sphere.structural.gii diff --git a/nipype/interfaces/cat12/surface.py b/nipype/interfaces/cat12/surface.py index aab38d1aa0..b8583c3e9b 100644 --- a/nipype/interfaces/cat12/surface.py +++ b/nipype/interfaces/cat12/surface.py @@ -71,12 +71,12 @@ class ExtractAdditionalSurfaceParameters(SPMCommand): Examples -------- >>> # Set the left surface files, both will be processed - >>> lh_path_central = 'lh.central.subject_filename.gii' + >>> lh_path_central = 'lh.central.structural.gii' >>> # Put here all surface files generated by CAT12 Segment, this is only required if the this approach is putted in - >>> surf_files = ["lh.sphere.reg.subject_filename.gii", "rh.sphere.reg.subject_filename.gii", \ - "lh.sphere.subject_filename.gii", "rh.sphere.subject_filename.gii", \ - 'rh.central.subject_filename.gii', \ - 'lh.pbt.subject_filename', 'rh.pbt.subject_filename'] + >>> surf_files = ["lh.sphere.reg.structural.gii", "rh.sphere.reg.structural.gii", \ + "lh.sphere.structural.gii", "rh.sphere.structural.gii", \ + 'rh.central.structural.gii', \ + 'lh.pbt.structural', 'rh.pbt.structural'] >>> extract_additional_measures = ExtractAdditionalSurfaceParameters(left_central_surfaces=lh_path_central, \ surface_files=surf_files) >>> extract_additional_measures.run() # doctest: +SKIP @@ -170,12 +170,12 @@ class ExtractROIBasedSurfaceMeasures(SPMCommand): >>> rh_atlas = "rh.aparc_a2009s.freesurfer.annot" >>> # Put here all surface files generated by CAT12 Segment, this is only required if the this approach is putted in a Node - >>> surf_files = ["lh.sphere.reg.subject_filename.gii", "rh.sphere.reg.subject_filename.gii", \ - "lh.sphere.subject_filename.gii", "rh.sphere.subject_filename.gii", \ - 'lh.central.subject_filename.gii', 'rh.central.subject_filename.gii', \ - 'lh.pbt.subject_filename', 'rh.pbt.subject_filename'] + >>> surf_files = ["lh.sphere.reg.structural.gii", "rh.sphere.reg.structural.gii", \ + "lh.sphere.structural.gii", "rh.sphere.structural.gii", \ + 'lh.central.structural.gii', 'rh.central.structural.gii', \ + 'lh.pbt.structural', 'rh.pbt.structural'] # Set the path to the left hemisphere measure file, both will be processed - >>> lh_measure = "lh.area.subject_filename" + >>> lh_measure = "lh.area.structural" >>> extract_additional_measures = ExtractROIBasedSurfaceMeasures(surface_files=surf_files, \ lh_surface_measure=lh_measure, \ diff --git a/nipype/testing/data/lh.aparc_a2009s.freesurfer.annot b/nipype/testing/data/lh.aparc_a2009s.freesurfer.annot new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/lh.area.structural b/nipype/testing/data/lh.area.structural new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/lh.central.structural.gii b/nipype/testing/data/lh.central.structural.gii new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/lh.pbt.structural b/nipype/testing/data/lh.pbt.structural new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/lh.sphere.reg.structural.gii b/nipype/testing/data/lh.sphere.reg.structural.gii new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/lh.sphere.structural.gii b/nipype/testing/data/lh.sphere.structural.gii new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/rh.aparc_a2009s.freesurfer.annot b/nipype/testing/data/rh.aparc_a2009s.freesurfer.annot new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/rh.central.structural.gii b/nipype/testing/data/rh.central.structural.gii new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/rh.pbt.structural b/nipype/testing/data/rh.pbt.structural new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/rh.sphere.reg.structural.gii b/nipype/testing/data/rh.sphere.reg.structural.gii new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/rh.sphere.structural.gii b/nipype/testing/data/rh.sphere.structural.gii new file mode 100644 index 0000000000..e69de29bb2 From 1bed8ede05890c941ea3bed76ecef9ad88bf2534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Tue, 9 Mar 2021 14:59:18 +0000 Subject: [PATCH 10/24] Minor fix --- nipype/interfaces/cat12/surface.py | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/nipype/interfaces/cat12/surface.py b/nipype/interfaces/cat12/surface.py index b8583c3e9b..fcc77df97f 100644 --- a/nipype/interfaces/cat12/surface.py +++ b/nipype/interfaces/cat12/surface.py @@ -73,12 +73,8 @@ class ExtractAdditionalSurfaceParameters(SPMCommand): >>> # Set the left surface files, both will be processed >>> lh_path_central = 'lh.central.structural.gii' >>> # Put here all surface files generated by CAT12 Segment, this is only required if the this approach is putted in - >>> surf_files = ["lh.sphere.reg.structural.gii", "rh.sphere.reg.structural.gii", \ - "lh.sphere.structural.gii", "rh.sphere.structural.gii", \ - 'rh.central.structural.gii', \ - 'lh.pbt.structural', 'rh.pbt.structural'] - >>> extract_additional_measures = ExtractAdditionalSurfaceParameters(left_central_surfaces=lh_path_central, \ - surface_files=surf_files) + >>> surf_files = ['lh.sphere.reg.structural.gii', 'rh.sphere.reg.structural.gii', 'lh.sphere.structural.gii', 'rh.sphere.structural.gii', 'rh.central.structural.gii', 'lh.pbt.structural', 'rh.pbt.structural'] + >>> extract_additional_measures = ExtractAdditionalSurfaceParameters(left_central_surfaces=lh_path_central, surface_files=surf_files) >>> extract_additional_measures.run() # doctest: +SKIP """ @@ -166,21 +162,11 @@ class ExtractROIBasedSurfaceMeasures(SPMCommand): Examples -------- >>> # Template surface files - >>> lh_atlas = "lh.aparc_a2009s.freesurfer.annot" - >>> rh_atlas = "rh.aparc_a2009s.freesurfer.annot" - - >>> # Put here all surface files generated by CAT12 Segment, this is only required if the this approach is putted in a Node - >>> surf_files = ["lh.sphere.reg.structural.gii", "rh.sphere.reg.structural.gii", \ - "lh.sphere.structural.gii", "rh.sphere.structural.gii", \ - 'lh.central.structural.gii', 'rh.central.structural.gii', \ - 'lh.pbt.structural', 'rh.pbt.structural'] - # Set the path to the left hemisphere measure file, both will be processed - >>> lh_measure = "lh.area.structural" - - >>> extract_additional_measures = ExtractROIBasedSurfaceMeasures(surface_files=surf_files, \ - lh_surface_measure=lh_measure, \ - lh_roi_atlas=lh_atlas, \ - rh_roi_atlas=rh_atlas) + >>> lh_atlas = 'lh.aparc_a2009s.freesurfer.annot' + >>> rh_atlas = 'rh.aparc_a2009s.freesurfer.annot' + >>> surf_files = ['lh.sphere.reg.structural.gii', 'rh.sphere.reg.structural.gii', 'lh.sphere.structural.gii', 'rh.sphere.structural.gii', 'lh.central.structural.gii', 'rh.central.structural.gii', 'lh.pbt.structural', 'rh.pbt.structural'] + >>> lh_measure = 'lh.area.structural' + >>> extract_additional_measures = ExtractROIBasedSurfaceMeasures(surface_files=surf_files, lh_surface_measure=lh_measure, lh_roi_atlas=lh_atlas, rh_roi_atlas=rh_atlas) >>> extract_additional_measures.run() # doctest: +SKIP From aa41da61fe65c4a900c9796635e7d7a6337cac56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Mon, 15 Mar 2021 21:53:40 +0000 Subject: [PATCH 11/24] Bug fix in the output of ROIMeasures. --- nipype/interfaces/cat12/surface.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/nipype/interfaces/cat12/surface.py b/nipype/interfaces/cat12/surface.py index fcc77df97f..02fe3d906e 100644 --- a/nipype/interfaces/cat12/surface.py +++ b/nipype/interfaces/cat12/surface.py @@ -133,14 +133,14 @@ class ExtractROIBasedSurfaceMeasuresInputSpec(SPMCommandInputSpec): lh_roi_atlas = InputMultiPath(File(exists=True), field="rdata", desc="(Left) ROI Atlas. These are the ROI's ", mandatory=True, copyfile=False) + rh_roi_atlas = InputMultiPath(File(exists=True), desc="(Right) ROI Atlas. These are the ROI's ", + mandatory=False, copyfile=False) + lh_surface_measure = InputMultiPath(File(exists=True), field="cdata", desc="(Left) Surface data files. ", mandatory=True, copyfile=False) rh_surface_measure = InputMultiPath(File(exists=True), desc="(Right) Surface data files.", mandatory=False, copyfile=False) - rh_roi_atlas = InputMultiPath(File(exists=True), desc="(Right) ROI Atlas. These are the ROI's ", - mandatory=False, copyfile=False) - class ExtractROIBasedSurfaceMeasuresOutputSpec(TraitedSpec): label_files = List(File(exists=True), desc="Files with the measures extracted for ROIs.") @@ -194,13 +194,12 @@ def _format_arg(self, opt, spec, val): def _list_outputs(self): outputs = self._outputs().get() - outputs["label_files"] = [] - for f in self.inputs.lh_roi_atlas: - pth, base, ext = split_filename(f) + pth, base, ext = split_filename(self.inputs.lh_surface_measure[0]) - outputs["label_files"].extend([os.path.join(os.path.join(pth, "label"), f) for f in - os.listdir(os.path.join(pth, "label")) - if os.path.isfile(os.path.join(os.path.join(pth, "label"), f))]) + outputs["label_files"] = [os.path.join(os.path.join(pth, "label"), f) for f in + os.listdir(os.path.join(pth, "label")) + if os.path.isfile(os.path.join(os.path.join(pth, "label"), f))] + return outputs class Cell: @@ -227,4 +226,3 @@ def __str__(self): """Convert input to appropriate format for cat12 """ return "{%s}" % self.to_string() - From dd152ee08e458c696087087450eae68fa51ffa9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Wed, 31 Mar 2021 11:16:56 +0100 Subject: [PATCH 12/24] Bug fix bias corrected file. --- nipype/interfaces/cat12/preprocess.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/nipype/interfaces/cat12/preprocess.py b/nipype/interfaces/cat12/preprocess.py index c6746f110e..64c32ad53e 100644 --- a/nipype/interfaces/cat12/preprocess.py +++ b/nipype/interfaces/cat12/preprocess.py @@ -1,5 +1,4 @@ import os -import sys import traits from nipype.interfaces.base import InputMultiPath, TraitedSpec, isdefined from nipype.interfaces.spm import SPMCommand @@ -59,8 +58,8 @@ class CAT12SegmentInputSpec(SPMCommandInputSpec): affine_regularization = traits.trait_types.Str(default_value="mni", field="opts.affreg", usedefault=True, desc=affine_reg_help) - bias_acc_help = "Strength of the SPM inhomogeneity (bias) correction that simultaneously controls the SPM biasreg, " \ - "biasfwhm, samp (resolution), and tol (iteration) parameter." + bias_acc_help = "Strength of the SPM inhomogeneity (bias) correction that simultaneously controls the SPM " \ + "biasreg, biasfwhm, samp (resolution), and tol (iteration) parameter." power_spm_inhomogeneity_correction = traits.trait_types.Float(default_value=0.5, field='opts.biasacc', usedefault=True, desc=bias_acc_help) @@ -168,9 +167,9 @@ class CAT12SegmentInputSpec(SPMCommandInputSpec): # Templates neuromorphometrics = traits.trait_types.Bool(True, field="output.ROImenu.atlases.neuromorphometrics", usedefault=True) - lpba40 = traits.trait_types.Bool(False, field="output.ROImenu.atlases.lpba40", usedefault=True) + lpba40 = traits.trait_types.Bool(True, field="output.ROImenu.atlases.lpba40", usedefault=True) cobra = traits.trait_types.Bool(True, field="output.ROImenu.atlases.hammers", usedefault=True) - hammers = traits.trait_types.Bool(False, field="output.ROImenu.atlases.cobra", usedefault=True) + hammers = traits.trait_types.Bool(True, field="output.ROImenu.atlases.cobra", usedefault=True) own_atlas = InputMultiPath(ImageFileSPM(exists=True), field="output.ROImenu.atlases.ownatlas", desc="Own Atlas", mandatory=False, copyfile=False) @@ -335,7 +334,7 @@ def _list_outputs(self): outputs[outfield] = fname_presuffix(f, prefix=prefix, suffix="_rigid") if isdefined(self.inputs.save_bias_corrected) and self.inputs.save_bias_corrected: - outputs["bias_corrected_image"] = fname_presuffix(f, prefix=os.path.join("mri", 'mi')) + outputs["bias_corrected_image"] = fname_presuffix(f, prefix=os.path.join("mri", 'wmi')) outputs["surface_files"] = [os.path.join(os.path.join(pth, "surf"), f) for f in os.listdir(os.path.join(pth, "surf")) From ea72f5e58ba3a7c1dc5f310ce634baf5243732d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Thu, 1 Apr 2021 15:41:20 +0100 Subject: [PATCH 13/24] Update nipype/interfaces/cat12/preprocess.py Update nipype/interfaces/cat12/surface.py --- nipype/interfaces/cat12/preprocess.py | 438 +++++++++++++------------- nipype/interfaces/cat12/surface.py | 46 ++- 2 files changed, 236 insertions(+), 248 deletions(-) diff --git a/nipype/interfaces/cat12/preprocess.py b/nipype/interfaces/cat12/preprocess.py index 64c32ad53e..18da122061 100644 --- a/nipype/interfaces/cat12/preprocess.py +++ b/nipype/interfaces/cat12/preprocess.py @@ -1,250 +1,218 @@ import os -import traits -from nipype.interfaces.base import InputMultiPath, TraitedSpec, isdefined +from pathlib import Path + +from nipype.interfaces.base import InputMultiPath, TraitedSpec, traits, isdefined, File, Str from nipype.interfaces.spm import SPMCommand from nipype.interfaces.spm.base import SPMCommandInputSpec, ImageFileSPM, scans_for_fnames, scans_for_fname from nipype.utils.filemanip import split_filename, fname_presuffix -from traits.trait_types import Int, File -from traits.trait_types import List class CAT12SegmentInputSpec(SPMCommandInputSpec): - """ - CAT12: Segmentation - This toolbox is an extension to the default segmentation in SPM12, but uses a completely different segmentation - approach. - The segmentation approach is based on an Adaptive Maximum A Posterior (MAP) technique without the need for a priori - information about tissue probabilities. That is, the Tissue Probability Maps (TPM) are not used constantly in the - sense of the classical Unified Segmentation approach (Ashburner et. al. 2005), but just for spatial normalization. - The following AMAP estimation is adaptive in the sense that local variations of the parameters (i.e., means and - variance) are modeled as slowly varying spatial functions (Rajapakse et al. 1997). This not only accounts for - intensity inhomogeneities but also for other local variations of intensity. - Additionally, the segmentation approach uses a Partial Volume Estimation (PVE) with a simplified mixed model of at - most two tissue types (Tohka et al. 2004). We start with an initial segmentation into three pure classes: gray - matter (GM), white matter (WM), and cerebrospinal fluid (CSF) based on the above described AMAP estimation. The - initial segmentation is followed by a PVE of two additional mixed classes: GM-WM and GM-CSF. This results in an - estimation of the amount (or fraction) of each pure tissue type present in every voxel (as single voxels - given by - Another important extension to the SPM12 segmentation is the integration of the Dartel or Geodesic Shooting - registration into the toolbox by an already existing Dartel/Shooting template in MNI space. This template was - derived from 555 healthy control subjects of the IXI-database (http://www.brain-development.org) and provides the - several Dartel or Shooting iterations. Thus, for the majority of studies the creation of sample-specific templates - is not necessary anymore and is mainly recommended for children data.'}; - - http://www.neuro.uni-jena.de/cat12/CAT12-Manual.pdf#page=15 - - Examples - -------- - >>> path_mr = 'structural.nii' - >>> cat = CAT12Segment(in_files=path_mr) - >>> cat.run() # doctest: +SKIP - """ - in_files = InputMultiPath(ImageFileSPM(exists=True), field="data", desc="file to segment", mandatory=True, copyfile=False) - help_tpm = 'Tissue Probability Map. Select the tissue probability image that includes 6 tissue probability ' \ - 'classes for (1) grey matter, (2) white matter, (3) cerebrospinal fluid, (4) bone, (5) non-brain soft ' \ - 'tissue, and (6) the background. CAT uses the TPM only for the initial SPM segmentation.' - tpm = InputMultiPath(ImageFileSPM(exists=True), field="tpm", desc=help_tpm, mandatory=False, + _help_tpm = 'Tissue Probability Map. Select the tissue probability image that includes 6 tissue probability ' \ + 'classes for (1) grey matter, (2) white matter, (3) cerebrospinal fluid, (4) bone, (5) non-brain ' \ + 'soft tissue, and (6) the background. CAT uses the TPM only for the initial SPM segmentation.' + tpm = InputMultiPath(ImageFileSPM(exists=True), field="tpm", desc=_help_tpm, mandatory=False, copyfile=False) - n_jobs = traits.trait_types.Int(1, usedefault=True, mandatory=True, field="nproc", desc="Number of threads") - use_prior = traits.trait_types.Str(field="useprior", usedefault=True) - - affine_reg_help = 'Affine Regularization. The procedure is a local optimisation, so it needs reasonable initial ' \ - 'starting estimates. Images should be placed in approximate alignment using the Display ' \ - 'function of SPM before beginning. A Mutual Information affine registration with the tissue ' \ - 'probability maps (D''Agostino et al, 2004) is used to achieve approximate alignment.' - affine_regularization = traits.trait_types.Str(default_value="mni", - field="opts.affreg", usedefault=True, desc=affine_reg_help) - - bias_acc_help = "Strength of the SPM inhomogeneity (bias) correction that simultaneously controls the SPM " \ - "biasreg, biasfwhm, samp (resolution), and tol (iteration) parameter." - power_spm_inhomogeneity_correction = traits.trait_types.Float(default_value=0.5, field='opts.biasacc', - usedefault=True, - desc=bias_acc_help) + n_jobs = traits.Int(1, usedefault=True, mandatory=True, field="nproc", desc="Number of threads") + use_prior = Str(field="useprior", usedefault=True) + + _help_affine_reg = 'Affine Regularization. The procedure is a local optimisation, so it needs reasonable initial ' \ + 'starting estimates. Images should be placed in approximate alignment using the Display ' \ + 'function of SPM before beginning. A Mutual Information affine registration with the tissue ' \ + 'probability maps (D''Agostino et al, 2004) is used to achieve approximate alignment.' + affine_regularization = Str(default_value="mni", + field="opts.affreg", usedefault=True, desc=_help_affine_reg) + + _help_bias_acc = "Strength of the SPM inhomogeneity (bias) correction that simultaneously controls the SPM " \ + "biasreg, biasfwhm, samp (resolution), and tol (iteration) parameter." + power_spm_inhomogeneity_correction = traits.Float(default_value=0.5, field='opts.biasacc', + usedefault=True, + desc=_help_bias_acc) # Extended options for CAT12 preprocessing - help_app = 'Affine registration and SPM preprocessing can fail in some subjects with deviating anatomy (e.g. ' \ - 'other species/neonates) or in images with strong signal inhomogeneities, or untypical intensities ' \ - '(e.g. synthetic images). An initial bias correction can help to reduce such problems (see details ' \ - 'below). Recommended are the "default" and "full" option.' - affine_preprocessing = traits.trait_types.Int(1070, field="extopts.APP", desc=help_app, usedefault=True) - - help_initial_seg = 'In rare cases the Unified Segmentation can fail in highly abnormal brains, where e.g. the ' \ - 'cerebrospinal fluid of superlarge ventricles (hydrocephalus) were classified as white matter.' \ - ' However, if the affine registration is correct, the AMAP segmentation with an ' \ - 'prior-independent k-means initialization can be used to replace the SPM brain tissue ' \ - 'classification. Moreover, if the default Dartel and Shooting registrations will fail then the' \ - ' "Optimized Shooting - superlarge ventricles" option for "Spatial registration" is required! ' \ - 'Values: \nnone: 0;\nlight: 1;\nfull: 2;\ndefault: 1070.' - initial_segmentation = traits.trait_types.Int(0, field="extopts.spm_kamap", desc=help_initial_seg, usedefault=True) - - help_las = 'Additionally to WM-inhomogeneities, GM intensity can vary across different regions such as the motor' \ - ' cortex, the basal ganglia, or the occipital lobe. These changes have an anatomical background ' \ - '(e.g. iron content, myelinization), but are dependent on the MR-protocol and often lead to ' \ - 'underestimation of GM at higher intensities and overestimation of CSF at lower intensities. ' \ - 'Therefore, a local intensity transformation of all tissue classes is used to reduce these effects in ' \ - 'the image. This local adaptive segmentation (LAS) is applied before the final AMAP segmentation.' \ - 'Possible Values: \nSPM Unified Segmentation: 0 \nk-means AMAP: 2' - local_adaptive_seg = traits.trait_types.Float(0.5, field="extopts.LASstr", usedefault=True, desc=help_las) - - help_gcutstr = 'Method of initial skull-stripping before AMAP segmentation. The SPM approach works quite stable ' \ - 'for the majority of data. However, in some rare cases parts of GM (i.e. in frontal lobe) might ' \ - 'be cut. If this happens the GCUT approach is a good alternative. GCUT is a graph-cut/region-' \ - 'growing approach starting from the WM area. APRG (adaptive probability region-growing) is a new' \ - ' method that refines the probability maps of the SPM approach by region-growing techniques of ' \ - 'the gcut approach with a final surface-based optimization strategy. This is currently the method ' \ - 'with the most accurate and reliable results. If you use already skull-stripped data you can turn' \ - ' off skull-stripping although this is automaticaly detected in most cases. Please note that the' \ - ' choice of the skull-stripping method will also influence the estimation of TIV, because the' \ - ' methods mainly differ in the handling of the outer CSF around the cortical surface. ' \ - '\nPossible Values:\n - none (already skull-stripped): -1;\n - SPM approach: 0; ' \ - '\n - GCUT approach: 0.50; \n - APRG approach: 2' - skull_strip = traits.trait_types.Float(2, field="extopts.gcutstr", desc=help_gcutstr, usedefault=True) - - help_wmhc = 'WARNING: Please note that the detection of WM hyperintensies is still under development and does ' \ - 'not have the same accuracy as approaches that additionally consider FLAIR images (e.g. Lesion ' \ - 'Segmentation Toolbox)! In aging or (neurodegenerative) diseases WM intensity can be reduced ' \ - 'locally in T1 or increased in T2/PD images. These so-called WM hyperintensies (WMHs) can lead to ' \ - 'preprocessing errors. Large GM areas next to the ventricle can cause normalization problems. ' \ - 'Therefore, a temporary correction for normalization is useful if WMHs are expected. CAT allows ' \ - 'different ways to handle WMHs: ' \ - '\n0) No Correction (handled as GM). \n1) Temporary (internal) correction as WM for spatial ' \ - 'normalization and estimation of cortical thickness. \n2) Permanent correction to WM. ' - wm_hyper_intensity_correction = traits.trait_types.Int(1, field="extopts.WMHC", desc=help_wmhc, usedefault=True) - - help_vox = 'The (isotropic) voxel sizes of any spatially normalised written images. A non-finite value will be ' \ - 'replaced by the average voxel size of the tissue probability maps used by the segmentation.' - voxel_size = traits.trait_types.Float(1.5, field="extopts.vox", desc=help_vox, usedefault=True) - - help_resampling = 'Internal resampling for preprocessing.\n The default fixed image resolution offers a good ' \ - 'trade-off between optimal quality and preprocessing time and memory demands. Standard ' \ - 'structural data with a voxel resolution around 1 mm or even data with high in-plane resolution' \ - ' and large slice thickness (e.g. 0.5x0.5x1.5 mm) will benefit from this setting. If you have' \ - ' higher native resolutions the highres option "Fixed 0.8 mm" will sometimes offer slightly' \ - ' better preprocessing quality with an increase of preprocessing time and memory demands. In' \ - ' case of even higher resolutions and high signal-to-noise ratio (e.g. for 7 T data) the ' \ - '"Best native" option will process the data on the highest native resolution. I.e. a resolution' \ - ' of 0.4x0.7x1.0 mm will be interpolated to 0.4x0.4x0.4 mm. A tolerance range of 0.1 mm is used' \ - ' to avoid interpolation artifacts, i.e. a resolution of 0.95x1.01x1.08 mm will not be ' \ - 'interpolated in case of the "Fixed 1.0 mm"! This "optimal" option prefers an isotropic voxel ' \ - 'size with at least 1.1 mm that is controlled by the median voxel size and a volume term that ' \ - 'penalizes highly anisotropic voxels.' \ - 'Values:\nOptimal: [1.0 0.1]\nFixed 1.0 mm: [1.0 0.1];\nFixed 0.8 mm:[0.8 0.1]' \ - '\nBest native: [0.5 0.1]' - internal_resampling_process = traits.trait_types.Tuple(traits.trait_types.Float(1), traits.trait_types.Float(0.1), - minlen=2, maxlen=2, - field="extopts.restypes.optimal", desc="help_resampling", - usedefault=True) - errors_help = 'Error handling.\nTry to catch preprocessing errors and continue with the next data set or ignore ' \ - 'all warnings (e.g., bad intensities) and use an experimental pipeline which is still in ' \ - 'development. In case of errors, CAT continues with the next subject if this option is enabled. If ' \ - 'the experimental option with backup functions is selected and warnings occur, CAT will try to use' \ - ' backup routines and skip some processing steps which require good T1 contrasts (e.g., LAS). If ' \ - 'you want to avoid processing of critical data and ensure that only the main pipeline is used then' \ - ' select the option "Ignore errors (continue with the next subject)". It is strongly recommended to' \ - ' check for preprocessing problems, especially with non-T1 contrasts. ' \ - '\nValues:\nnone: 0,\ndefault: 1,\ndetails: 2.' - ignore_errors = traits.trait_types.Int(1, field="extopts.ignoreErrors", desc=errors_help, usedefault=True) + _help_app = 'Affine registration and SPM preprocessing can fail in some subjects with deviating anatomy (e.g. ' \ + 'other species/neonates) or in images with strong signal inhomogeneities, or untypical intensities ' \ + '(e.g. synthetic images). An initial bias correction can help to reduce such problems (see details ' \ + 'below). Recommended are the "default" and "full" option.' + affine_preprocessing = traits.Int(1070, field="extopts.APP", desc=_help_app, usedefault=True) + + _help_initial_seg = 'In rare cases the Unified Segmentation can fail in highly abnormal brains, where e.g. the ' \ + 'cerebrospinal fluid of superlarge ventricles (hydrocephalus) were classified as white ' \ + 'matter. However, if the affine registration is correct, the AMAP segmentation with an ' \ + 'prior-independent k-means initialization can be used to replace the SPM brain tissue ' \ + 'classification. Moreover, if the default Dartel and Shooting registrations will fail then ' \ + 'rhe "Optimized Shooting - superlarge ventricles" option for "Spatial registration" is ! ' \ + 'required Values: \nnone: 0;\nlight: 1;\nfull: 2;\ndefault: 1070.' + initial_segmentation = traits.Int(0, field="extopts.spm_kamap", desc=_help_initial_seg, usedefault=True) + + _help_las = 'Additionally to WM-inhomogeneities, GM intensity can vary across different regions such as the motor' \ + ' cortex, the basal ganglia, or the occipital lobe. These changes have an anatomical background ' \ + '(e.g. iron content, myelinization), but are dependent on the MR-protocol and often lead to ' \ + 'underestimation of GM at higher intensities and overestimation of CSF at lower intensities. ' \ + 'Therefore, a local intensity transformation of all tissue classes is used to reduce these effects in' \ + ' the image. This local adaptive segmentation (LAS) is applied before the final AMAP segmentation.' \ + 'Possible Values: \nSPM Unified Segmentation: 0 \nk-means AMAP: 2' + local_adaptive_seg = traits.Float(0.5, field="extopts.LASstr", usedefault=True, desc=_help_las) + + _help_gcutstr = 'Method of initial skull-stripping before AMAP segmentation. The SPM approach works quite stable ' \ + 'for the majority of data. However, in some rare cases parts of GM (i.e. in frontal lobe) might ' \ + 'be cut. If this happens the GCUT approach is a good alternative. GCUT is a graph-cut/region-' \ + 'growing approach starting from the WM area. APRG (adaptive probability region-growing) is a new' \ + ' method that refines the probability maps of the SPM approach by region-growing techniques of ' \ + 'the gcut approach with a final surface-based optimization strategy. This is currently the method' \ + ' with the most accurate and reliable results. If you use already skull-stripped data you can ' \ + 'turn off skull-stripping although this is automaticaly detected in most cases. Please note that ' \ + 'the choice of the skull-stripping method will also influence the estimation of TIV, because the' \ + ' methods mainly differ in the handling of the outer CSF around the cortical surface. ' \ + '\nPossible Values:\n - none (already skull-stripped): -1;\n - SPM approach: 0; ' \ + '\n - GCUT approach: 0.50; \n - APRG approach: 2' + skull_strip = traits.Float(2, field="extopts.gcutstr", desc=_help_gcutstr, usedefault=True) + + _help_wmhc = 'WARNING: Please note that the detection of WM hyperintensies is still under development and does ' \ + 'not have the same accuracy as approaches that additionally consider FLAIR images (e.g. Lesion ' \ + 'Segmentation Toolbox)! In aging or (neurodegenerative) diseases WM intensity can be reduced ' \ + 'locally in T1 or increased in T2/PD images. These so-called WM hyperintensies (WMHs) can lead to ' \ + 'preprocessing errors. Large GM areas next to the ventricle can cause normalization problems. ' \ + 'Therefore, a temporary correction for normalization is useful if WMHs are expected. CAT allows ' \ + 'different ways to handle WMHs: ' \ + '\n0) No Correction (handled as GM). \n1) Temporary (internal) correction as WM for spatial ' \ + 'normalization and estimation of cortical thickness. \n2) Permanent correction to WM. ' + wm_hyper_intensity_correction = traits.Int(1, field="extopts.WMHC", desc=_help_wmhc, usedefault=True) + + _help_vox = 'The (isotropic) voxel sizes of any spatially normalised written images. A non-finite value will be ' \ + 'replaced by the average voxel size of the tissue probability maps used by the segmentation.' + voxel_size = traits.Float(1.5, field="extopts.vox", desc=_help_vox, usedefault=True) + + _help_resampling = 'Internal resampling for preprocessing.\n The default fixed image resolution offers a good ' \ + 'trade-off between optimal quality and preprocessing time and memory demands. Standard ' \ + 'structural data with a voxel resolution around 1mm or even data with high in-plane resolution' \ + ' and large slice thickness (e.g. 0.5x0.5x1.5 mm) will benefit from this setting. If you have' \ + ' higher native resolutions the highres option "Fixed 0.8 mm" will sometimes offer slightly' \ + ' better preprocessing quality with an increase of preprocessing time and memory demands. In' \ + ' case of even higher resolutions and high signal-to-noise ratio (e.g. for 7 T data) the ' \ + '"Best native" option will process the data on the highest native resolution. A resolution' \ + ' of 0.4x0.7x1.0 mm will be interpolated to 0.4x0.4x0.4 mm. A tolerance range of 0.1 mm is ' \ + 'used to avoid interpolation artifacts, i.e. a resolution of 0.95x1.01x1.08 mm will not be ' \ + 'interpolated in case of the "Fixed 1.0 mm"! This "optimal" option prefers an isotropic voxel ' \ + 'size with at least 1.1 mm that is controlled by the median voxel size and a volume term that ' \ + 'penalizes highly anisotropic voxels.' \ + 'Values:\nOptimal: [1.0 0.1]\nFixed 1.0 mm: [1.0 0.1];\nFixed 0.8 mm:[0.8 0.1]' \ + '\nBest native: [0.5 0.1]' + internal_resampling_process = traits.Tuple(traits.Float(1), traits.Float(0.1), minlen=2, maxlen=2, usedefault=True, + field="extopts.restypes.optimal", desc="help_resampling") + _errors_help = 'Error handling.\nTry to catch preprocessing errors and continue with the next data set or ignore ' \ + 'all warnings (e.g., bad intensities) and use an experimental pipeline which is still in ' \ + 'development. In case of errors, CAT continues with the next subject if this option is enabled. If ' \ + 'the experimental option with backup functions is selected and warnings occur, CAT will try to use' \ + ' backup routines and skip some processing steps which require good T1 contrasts (e.g., LAS). If ' \ + 'you want to avoid processing of critical data and ensure that only the main pipeline is used then' \ + ' select the option "Ignore errors (continue with the next subject)". It is strongly recommended ' \ + 'to check for preprocessing problems, especially with non-T1 contrasts. ' \ + '\nValues:\nnone: 0,\ndefault: 1,\ndetails: 2.' + ignore_errors = traits.Int(1, field="extopts.ignoreErrors", desc=_errors_help, usedefault=True) # Writing options - help_surf = 'Surface and thickness estimation. \nUse projection-based thickness (PBT) (Dahnke et al. 2012) to' \ - ' estimate cortical thickness and to create the central cortical surface for left and right ' \ - 'hemisphere. Surface reconstruction includes topology correction (Yotter et al. 2011), spherical ' \ - 'inflation (Yotter et al.) and spherical registration. Additionally you can also estimate surface ' \ - 'parameters such as gyrification, cortical complexity or sulcal depth that can be subsequently ' \ - 'analyzed at each vertex of the surface. Please note, that surface reconstruction and spherical ' \ - 'registration additionally requires about 20-60 min of computation time. A fast (1-3 min) surface ' \ - 'pipeline is available for visual preview (e.g., to check preprocessing quality) in the ' \ - 'cross-sectional, but not in the longitudinal pipeline. Only the initial surfaces are created with a' \ - ' lower resolution and without topology correction, spherical mapping and surface registration. ' \ - 'Please note that the files with the estimated surface thickness can therefore not be used for ' \ - 'further analysis! For distinction, these files contain "preview" in their filename and they' \ - ' are not available as batch dependencies objects. ' - surface_and_thickness_estimation = traits.trait_types.Int(1, field="surface", desc=help_surf, usedefault=True) - surface_measures = traits.trait_types.Int(1, field="output.surf_measures", usedefault=True) + _help_surf = 'Surface and thickness estimation. \nUse projection-based thickness (PBT) (Dahnke et al. 2012) to' \ + ' estimate cortical thickness and to create the central cortical surface for left and right ' \ + 'hemisphere. Surface reconstruction includes topology correction (Yotter et al. 2011), spherical ' \ + 'inflation (Yotter et al.) and spherical registration. Additionally you can also estimate surface ' \ + 'parameters such as gyrification, cortical complexity or sulcal depth that can be subsequently ' \ + 'analyzed at each vertex of the surface. Please note, that surface reconstruction and spherical ' \ + 'registration additionally requires about 20-60 min of computation time. A fast (1-3 min) surface ' \ + 'pipeline is available for visual preview (e.g., to check preprocessing quality) in the ' \ + 'cross-sectional, but not in the longitudinal pipeline. Only the initial surfaces are created with ' \ + 'a lower resolution and without topology correction, spherical mapping and surface registration. ' \ + 'Please note that the files with the estimated surface thickness can therefore not be used for ' \ + 'further analysis! For distinction, these files contain "preview" in their filename and they' \ + ' are not available as batch dependencies objects. ' + surface_and_thickness_estimation = traits.Int(1, field="surface", desc=_help_surf, usedefault=True) + surface_measures = traits.Int(1, field="output.surf_measures", usedefault=True) # Templates - neuromorphometrics = traits.trait_types.Bool(True, field="output.ROImenu.atlases.neuromorphometrics", - usedefault=True) - lpba40 = traits.trait_types.Bool(True, field="output.ROImenu.atlases.lpba40", usedefault=True) - cobra = traits.trait_types.Bool(True, field="output.ROImenu.atlases.hammers", usedefault=True) - hammers = traits.trait_types.Bool(True, field="output.ROImenu.atlases.cobra", usedefault=True) + neuromorphometrics = traits.Bool(True, field="output.ROImenu.atlases.neuromorphometrics", + usedefault=True) + lpba40 = traits.Bool(True, field="output.ROImenu.atlases.lpba40", usedefault=True) + cobra = traits.Bool(True, field="output.ROImenu.atlases.hammers", usedefault=True) + hammers = traits.Bool(True, field="output.ROImenu.atlases.cobra", usedefault=True) own_atlas = InputMultiPath(ImageFileSPM(exists=True), field="output.ROImenu.atlases.ownatlas", desc="Own Atlas", mandatory=False, copyfile=False) - dartel_help = 'This option is to export data into a form that can be used with DARTEL. The SPM default is to ' \ - 'only apply rigid body transformation. However, a more appropriate option is to apply affine ' \ - 'transformation, because the additional scaling of the images requires less deformations to ' \ - 'non-linearly register brains to the template.' + _dartel_help = 'This option is to export data into a form that can be used with DARTEL. The SPM default is to ' \ + 'only apply rigid body transformation. However, a more appropriate option is to apply affine ' \ + 'transformation, because the additional scaling of the images requires less deformations to ' \ + 'non-linearly register brains to the template.' # Grey matter - gm_desc = 'Options to save grey matter images.' - gm_output_native = traits.trait_types.Bool(False, field="output.GM.native", usedefault=True, desc=gm_desc) - gm_output_modulated = traits.trait_types.Bool(True, field="output.GM.mod", usedefault=True, desc=gm_desc) - gm_output_dartel = traits.trait_types.Bool(False, field="output.GM.dartel", usedefault=True, desc=gm_desc) + _gm_desc = 'Options to save grey matter images.' + gm_output_native = traits.Bool(False, field="output.GM.native", usedefault=True, desc=_gm_desc) + gm_output_modulated = traits.Bool(True, field="output.GM.mod", usedefault=True, desc=_gm_desc) + gm_output_dartel = traits.Bool(False, field="output.GM.dartel", usedefault=True, desc=_gm_desc) # White matter - wm_desc = 'Options to save white matter images.' - wm_output_native = traits.trait_types.Bool(False, field="output.WM.native", usedefault=True, desc=wm_desc) - wm_output_modulated = traits.trait_types.Bool(True, field="output.WM.mod", usedefault=True, desc=wm_desc) - wm_output_dartel = traits.trait_types.Bool(False, field="output.WM.dartel", usedefault=True, desc=wm_desc) + _wm_desc = 'Options to save white matter images.' + wm_output_native = traits.Bool(False, field="output.WM.native", usedefault=True, desc=_wm_desc) + wm_output_modulated = traits.Bool(True, field="output.WM.mod", usedefault=True, desc=_wm_desc) + wm_output_dartel = traits.Bool(False, field="output.WM.dartel", usedefault=True, desc=_wm_desc) # CSF matter - csf_desc = 'Options to save CSF images.' - csf_output_native = traits.trait_types.Bool(False, field="output.CSF.native", usedefault=True, desc=csf_desc) - csf_output_modulated = traits.trait_types.Bool(True, field="output.CSF.mod", usedefault=True, desc=csf_desc) - csf_output_dartel = traits.trait_types.Bool(False, field="output.CSF.dartel", usedefault=True, desc=csf_desc) + _csf_desc = 'Options to save CSF images.' + csf_output_native = traits.Bool(False, field="output.CSF.native", usedefault=True, desc=_csf_desc) + csf_output_modulated = traits.Bool(True, field="output.CSF.mod", usedefault=True, desc=_csf_desc) + csf_output_dartel = traits.Bool(False, field="output.CSF.dartel", usedefault=True, desc=_csf_desc) # Labels - label_desc = 'This is the option to save a labeled version of your segmentations for fast visual comparision. ' \ - 'Labels are saved as Partial Volume Estimation (PVE) values with different mix classes for GM-WM ' \ - '(2.5) and GM-CSF (1.5). BG=0, CSF=1, GM=2, WM=3, WMH=4 (if WMHC=3), SL=1.5 (if SLC)' - label_native = traits.trait_types.Bool(False, field="output.label.native", usedefault=True, desc=label_desc) - label_warped = traits.trait_types.Bool(True, field="output.label.warped", usedefault=True, desc=label_desc) - label_dartel = traits.trait_types.Bool(False, field="output.label.dartel", usedefault=True, desc=label_desc) - output_labelnative = traits.trait_types.Bool(False, field="output.labelnative", usedefault=True, desc=label_desc) + _help_label_desc = 'This is the option to save a labeled version of your segmentations for fast visual ' \ + 'comparision. Labels are saved as Partial Volume Estimation (PVE) values with different mix ' \ + 'classes for GM-WM (2.5) and GM-CSF (1.5). BG=0, CSF=1, GM=2, WM=3, WMH=4 (if WMHC=3), ' \ + 'SL=1.5 (if SLC)' + label_native = traits.Bool(False, field="output.label.native", usedefault=True, desc=_help_label_desc) + label_warped = traits.Bool(True, field="output.label.warped", usedefault=True, desc=_help_label_desc) + label_dartel = traits.Bool(False, field="output.label.dartel", usedefault=True, desc=_help_label_desc) + output_labelnative = traits.Bool(False, field="output.labelnative", usedefault=True, desc=_help_label_desc) # Bias - save_bias_corrected = traits.trait_types.Bool(True, field="output.bias.warped", usedefault=True) + save_bias_corrected = traits.Bool(True, field="output.bias.warped", usedefault=True) # las - las_desc = 'This is the option to save a bias, noise, and local intensity corrected version of the original T1' \ - ' image. MR images are usually corrupted by a smooth, spatially varying artifact that modulates the' \ - ' intensity of the image (bias). These artifacts, although not usually a problem for visual ' \ - 'inspection, can impede automated processing of the images. The bias corrected version should have ' \ - 'more uniform intensities within the different types of tissues and can be saved in native space ' \ - 'and/or normalised. Noise is corrected by an adaptive non-local mean (NLM) filter (Manjon 2008, ' \ - 'Medical Image Analysis 12).' - las_native = traits.trait_types.Bool(False, field="output.las.native", usedefault=True, desc=las_desc) - las_warped = traits.trait_types.Bool(True, field="output.las.warped", usedefault=True, desc=las_desc) - las_dartel = traits.trait_types.Bool(False, field="output.las.dartel", usedefault=True, desc=las_desc) + _las_desc = 'This is the option to save a bias, noise, and local intensity corrected version of the original T1' \ + ' image. MR images are usually corrupted by a smooth, spatially varying artifact that modulates the' \ + ' intensity of the image (bias). These artifacts, although not usually a problem for visual ' \ + 'inspection, can impede automated processing of the images. The bias corrected version should have ' \ + 'more uniform intensities within the different types of tissues and can be saved in native space ' \ + 'and/or normalised. Noise is corrected by an adaptive non-local mean (NLM) filter (Manjon 2008, ' \ + 'Medical Image Analysis 12).' + las_native = traits.Bool(False, field="output.las.native", usedefault=True, desc=_las_desc) + las_warped = traits.Bool(True, field="output.las.warped", usedefault=True, desc=_las_desc) + las_dartel = traits.Bool(False, field="output.las.dartel", usedefault=True, desc=_las_desc) # Jacobian Warped - help_jacobian = 'This is the option to save the Jacobian determinant, which expresses local volume changes. This' \ - ' image can be used in a pure deformation based morphometry (DBM) design. Please note that the' \ - ' affine part of the deformation field is ignored. Thus, there is no need for any additional' \ - ' correction for different brain sizes using ICV.' - jacobianwarped = traits.trait_types.Bool(True, field="output.jacobianwarped", usedefault=True, desc=help_jacobian) + _help_jacobian = 'This is the option to save the Jacobian determinant, which expresses local volume changes. This' \ + ' image can be used in a pure deformation based morphometry (DBM) design. Please note that the' \ + ' affine part of the deformation field is ignored. Thus, there is no need for any additional' \ + ' correction for different brain sizes using ICV.' + jacobianwarped = traits.Bool(True, field="output.jacobianwarped", usedefault=True, desc=_help_jacobian) # Deformation Fields - help_warp = 'Deformation fields can be saved to disk, and used by the Deformations Utility and/or applied to ' \ - 'coregistered data from other modalities (e.g. fMRI). For spatially normalising images to MNI space,' \ - ' you will need the forward deformation, whereas for spatially normalising (eg) GIFTI surface files,' \ - ' you''ll need the inverse. It is also possible to transform data in MNI space on to the individual' \ - ' subject, which also requires the inverse transform. Deformations are saved as .nii files, which' \ - ' contain three volumes to encode the x, y and z coordinates.' \ - '\nValues: No:[0 0];\nImage->Template (forward): [1 0];\nTemplate->Image (inverse): [0 1]; ' \ - '\ninverse + forward: [1 1]' - warps = traits.trait_types.Tuple(traits.trait_types.Int(1), traits.trait_types.Int(0), minlen=2, maxlen=2, - field="output.warps", usedefault=True, desc=help_warp) + _help_warp = 'Deformation fields can be saved to disk, and used by the Deformations Utility and/or applied to ' \ + 'coregistered data from other modalities (e.g. fMRI). For spatially normalising images to MNI space,' \ + ' you will need the forward deformation, whereas for spatially normalising (eg) GIFTI surface files,' \ + ' you''ll need the inverse. It is also possible to transform data in MNI space on to the individual' \ + ' subject, which also requires the inverse transform. Deformations are saved as .nii files, which' \ + ' contain three volumes to encode the x, y and z coordinates.' \ + '\nValues: No:[0 0];\nImage->Template (forward): [1 0];\nTemplate->Image (inverse): [0 1]; ' \ + '\ninverse + forward: [1 1]' + warps = traits.Tuple(traits.Int(1), traits.Int(0), minlen=2, maxlen=2, + field="output.warps", usedefault=True, desc=_help_warp) class CAT12SegmentOutputSpec(TraitedSpec): ########################################## # Label XML files ########################################## - label_files = List(File(exists=True), desc="Files with the measures extracted for OI ands ROIs") + label_files = traits.List(File(exists=True), desc="Files with the measures extracted for OI ands ROIs") label_rois = File(exists=True, desc="Files with thickness values of ROIs.") label_roi = File(exists=True, desc="Files with thickness values of ROI.") @@ -253,7 +221,7 @@ class CAT12SegmentOutputSpec(TraitedSpec): # MRI .nii files ########################################## - mri_images = List(File(exists=True), desc="Different segmented images.") + mri_images = traits.List(File(exists=True), desc="Different segmented images.") # Grey Matter gm_modulated_image = File(exists=True, desc="Grey matter modulated image.") @@ -275,7 +243,7 @@ class CAT12SegmentOutputSpec(TraitedSpec): # Surface files ########################################## - surface_files = List(File(exists=True), desc="Surface files") + surface_files = traits.List(File(exists=True), desc="Surface files") # Right hemisphere rh_central_surface = File(exists=True, desc="Central right hemisphere files") @@ -286,11 +254,41 @@ class CAT12SegmentOutputSpec(TraitedSpec): lh_sphere_surface = File(exists=True, desc="Sphere left hemisphere files") # Report files - report_files = List(File(exists=True), desc="Report files.") + report_files = traits.List(File(exists=True), desc="Report files.") report = File(exists=True, desc="Report file.") class CAT12Segment(SPMCommand): + """ + CAT12: Segmentation + This toolbox is an extension to the default segmentation in SPM12, but uses a completely different segmentation + approach. + The segmentation approach is based on an Adaptive Maximum A Posterior (MAP) technique without the need for a priori + information about tissue probabilities. That is, the Tissue Probability Maps (TPM) are not used constantly in the + sense of the classical Unified Segmentation approach (Ashburner et. al. 2005), but just for spatial normalization. + The following AMAP estimation is adaptive in the sense that local variations of the parameters (i.e., means and + variance) are modeled as slowly varying spatial functions (Rajapakse et al. 1997). This not only accounts for + intensity inhomogeneities but also for other local variations of intensity. + Additionally, the segmentation approach uses a Partial Volume Estimation (PVE) with a simplified mixed model of at + most two tissue types (Tohka et al. 2004). We start with an initial segmentation into three pure classes: gray + matter (GM), white matter (WM), and cerebrospinal fluid (CSF) based on the above described AMAP estimation. The + initial segmentation is followed by a PVE of two additional mixed classes: GM-WM and GM-CSF. This results in an + estimation of the amount (or fraction) of each pure tissue type present in every voxel (as single voxels - given by + Another important extension to the SPM12 segmentation is the integration of the Dartel or Geodesic Shooting + registration into the toolbox by an already existing Dartel/Shooting template in MNI space. This template was + derived from 555 healthy control subjects of the IXI-database (http://www.brain-development.org) and provides the + several Dartel or Shooting iterations. Thus, for the majority of studies the creation of sample-specific templates + is not necessary anymore and is mainly recommended for children data.'}; + + http://www.neuro.uni-jena.de/cat12/CAT12-Manual.pdf#page=15 + + Examples + -------- + >>> path_mr = 'structural.nii' + >>> cat = CAT12Segment(in_files=path_mr) + >>> cat.run() # doctest: +SKIP + """ + input_spec = CAT12SegmentInputSpec output_spec = CAT12SegmentOutputSpec @@ -317,9 +315,7 @@ def _list_outputs(self): f = self.inputs.in_files[0] pth, base, ext = split_filename(f) - outputs["mri_images"] = [os.path.join(os.path.join(pth, "mri"), f) for f in - os.listdir(os.path.join(pth, "mri")) - if os.path.isfile(os.path.join(os.path.join(pth, "mri"), f))] + outputs["mri_images"] = [str(mri) for mri in Path(pth).glob("mri/*") if mri.is_file()] for tidx, tissue in enumerate(["gm", "wm", "csf"]): @@ -333,27 +329,23 @@ def _list_outputs(self): else: outputs[outfield] = fname_presuffix(f, prefix=prefix, suffix="_rigid") - if isdefined(self.inputs.save_bias_corrected) and self.inputs.save_bias_corrected: + if self.inputs.save_bias_corrected: outputs["bias_corrected_image"] = fname_presuffix(f, prefix=os.path.join("mri", 'wmi')) - outputs["surface_files"] = [os.path.join(os.path.join(pth, "surf"), f) for f in - os.listdir(os.path.join(pth, "surf")) - if os.path.isfile(os.path.join(os.path.join(pth, "surf"), f))] + outputs["surface_files"] = [str(surf) for surf in Path(pth).glob("surf/*") if surf.is_file()] - for tidx, hemisphere in enumerate(["rh", "lh"]): - for idx, suffix in enumerate(["central", "sphere"]): + for hemisphere in ["rh", "lh"]: + for suffix in ["central", "sphere"]: outfield = f'{hemisphere}_{suffix}_surface' outputs[outfield] = fname_presuffix(f, prefix=os.path.join("surf", f'{hemisphere}.{suffix}.'), suffix=".gii", use_ext=False) - outputs["report_files"] = [os.path.join(os.path.join(pth, "report"), f) for f in - os.listdir(os.path.join(pth, "report")) - if os.path.isfile(os.path.join(os.path.join(pth, "report"), f))] + outputs["report_files"] = outputs["report_files"] = [str(report) for report in Path(pth).glob("report/*") + if report.is_file()] + outputs[f'report'] = fname_presuffix(f, prefix=os.path.join("report", f'cat_'), suffix=".xml", use_ext=False) - outputs["label_files"] = [os.path.join(os.path.join(pth, "label"), f) for f in - os.listdir(os.path.join(pth, "label")) - if os.path.isfile(os.path.join(os.path.join(pth, "label"), f))] + outputs["label_files"] = [str(label) for label in Path(pth).glob("label/*") if label.is_file()] outputs['label_rois'] = fname_presuffix(f, prefix=os.path.join("label", f'catROIs_'), suffix=".xml", use_ext=False) diff --git a/nipype/interfaces/cat12/surface.py b/nipype/interfaces/cat12/surface.py index 02fe3d906e..dedaa53b7c 100644 --- a/nipype/interfaces/cat12/surface.py +++ b/nipype/interfaces/cat12/surface.py @@ -1,11 +1,9 @@ import os -import sys +from pathlib import Path -import traits from traits.trait_base import _Undefined -from traits.trait_types import List -from nipype.interfaces.base import File, InputMultiPath, TraitedSpec +from nipype.interfaces.base import File, InputMultiPath, TraitedSpec, traits from nipype.interfaces.spm import SPMCommand from nipype.interfaces.spm.base import SPMCommandInputSpec from nipype.utils.filemanip import split_filename @@ -17,37 +15,37 @@ class ExtractAdditionalSurfaceParametersInputSpec(SPMCommandInputSpec): surface_files = InputMultiPath(File(exists=True), desc="All surface files", mandatory=False, copyfile=False) - gyrification = traits.trait_types.Bool(True, field="GI", usedefault=True, + gyrification = traits.Bool(True, field="GI", usedefault=True, desc="Extract gyrification index (GI) based on absolute mean curvature. The" " method is described in Luders et al. Neuroimage, 29:1224-1230, 2006") - gmv = traits.trait_types.Bool(True, field="gmv", usedefault=True, desc="Extract volume") - area = traits.trait_types.Bool(True, field="area", usedefault=True, desc="Extract area surface") - depth = traits.trait_types.Bool(False, field="SD", usedefault=True, + gmv = traits.Bool(True, field="gmv", usedefault=True, desc="Extract volume") + area = traits.Bool(True, field="area", usedefault=True, desc="Extract area surface") + depth = traits.Bool(False, field="SD", usedefault=True, desc="Extract sulcus depth based on euclidian distance between the central " "surface anf its convex hull.") - fractal_dimension = traits.trait_types.Bool(False, field="FD", usedefault=True, + fractal_dimension = traits.Bool(False, field="FD", usedefault=True, desc="Extract cortical complexity (fractal dimension) which is " "described in Yotter ar al. Neuroimage, 56(3): 961-973, 2011") class ExtractAdditionalSurfaceParametersOutputSpec(TraitedSpec): - lh_extracted_files = List(File(exists=True), desc="Files of left Hemisphere extracted measures") - rh_extracted_files = List(File(exists=True), desc="Files of right Hemisphere extracted measures") + lh_extracted_files = traits.List(File(exists=True), desc="Files of left Hemisphere extracted measures") + rh_extracted_files = traits.List(File(exists=True), desc="Files of right Hemisphere extracted measures") - lh_gyrification = List(File(exists=True), desc="Gyrification of left Hemisphere") - rh_gyrification = List(File(exists=True), desc="Gyrification of right Hemisphere") + lh_gyrification = traits.List(File(exists=True), desc="Gyrification of left Hemisphere") + rh_gyrification = traits.List(File(exists=True), desc="Gyrification of right Hemisphere") - lh_gmv = List(File(exists=True), desc="Grey matter volume of left Hemisphere") - rh_gmv = List(File(exists=True), desc="Grey matter volume of right Hemisphere") + lh_gmv = traits.List(File(exists=True), desc="Grey matter volume of left Hemisphere") + rh_gmv = traits.List(File(exists=True), desc="Grey matter volume of right Hemisphere") - lh_area = List(File(exists=True), desc="Area of left Hemisphere") - rh_area = List(File(exists=True), desc="Area of right Hemisphere") + lh_area = traits.List(File(exists=True), desc="Area of left Hemisphere") + rh_area = traits.List(File(exists=True), desc="Area of right Hemisphere") - lh_depth = List(File(exists=True), desc="Depth of left Hemisphere") - rh_depth = List(File(exists=True), desc="Depth of right Hemisphere") + lh_depth = traits.List(File(exists=True), desc="Depth of left Hemisphere") + rh_depth = traits.List(File(exists=True), desc="Depth of right Hemisphere") - lh_fractaldimension = List(File(exists=True), desc="Fractal Dimension of left Hemisphere") - rh_fractaldimension = List(File(exists=True), desc="Fractal Dimension of right Hemisphere") + lh_fractaldimension = traits.List(File(exists=True), desc="Fractal Dimension of left Hemisphere") + rh_fractaldimension = traits.List(File(exists=True), desc="Fractal Dimension of right Hemisphere") class ExtractAdditionalSurfaceParameters(SPMCommand): @@ -143,7 +141,7 @@ class ExtractROIBasedSurfaceMeasuresInputSpec(SPMCommandInputSpec): class ExtractROIBasedSurfaceMeasuresOutputSpec(TraitedSpec): - label_files = List(File(exists=True), desc="Files with the measures extracted for ROIs.") + label_files = traits.List(File(exists=True), desc="Files with the measures extracted for ROIs.") class ExtractROIBasedSurfaceMeasures(SPMCommand): @@ -196,9 +194,7 @@ def _list_outputs(self): pth, base, ext = split_filename(self.inputs.lh_surface_measure[0]) - outputs["label_files"] = [os.path.join(os.path.join(pth, "label"), f) for f in - os.listdir(os.path.join(pth, "label")) - if os.path.isfile(os.path.join(os.path.join(pth, "label"), f))] + outputs["label_files"] = [str(label) for label in Path(pth).glob("label/*") if label.is_file()] return outputs From 89d3059b7be8e026fdc5c384836c6ab94a0a7020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Thu, 29 Apr 2021 17:35:44 +0100 Subject: [PATCH 14/24] Added more variable descriptions. --- nipype/interfaces/cat12/preprocess.py | 64 ++++++++++++++++----------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/nipype/interfaces/cat12/preprocess.py b/nipype/interfaces/cat12/preprocess.py index 18da122061..2fab299f15 100644 --- a/nipype/interfaces/cat12/preprocess.py +++ b/nipype/interfaces/cat12/preprocess.py @@ -18,7 +18,6 @@ class CAT12SegmentInputSpec(SPMCommandInputSpec): copyfile=False) n_jobs = traits.Int(1, usedefault=True, mandatory=True, field="nproc", desc="Number of threads") - use_prior = Str(field="useprior", usedefault=True) _help_affine_reg = 'Affine Regularization. The procedure is a local optimisation, so it needs reasonable initial ' \ 'starting estimates. Images should be placed in approximate alignment using the Display ' \ @@ -129,16 +128,19 @@ class CAT12SegmentInputSpec(SPMCommandInputSpec): 'further analysis! For distinction, these files contain "preview" in their filename and they' \ ' are not available as batch dependencies objects. ' surface_and_thickness_estimation = traits.Int(1, field="surface", desc=_help_surf, usedefault=True) - surface_measures = traits.Int(1, field="output.surf_measures", usedefault=True) + surface_measures = traits.Int(1, field="output.surf_measures", usedefault=True, desc="Extract surface measures") # Templates neuromorphometrics = traits.Bool(True, field="output.ROImenu.atlases.neuromorphometrics", - usedefault=True) - lpba40 = traits.Bool(True, field="output.ROImenu.atlases.lpba40", usedefault=True) - cobra = traits.Bool(True, field="output.ROImenu.atlases.hammers", usedefault=True) - hammers = traits.Bool(True, field="output.ROImenu.atlases.cobra", usedefault=True) + usedefault=True, desc="Extract brain measures for Neuromorphometrics template") + lpba40 = traits.Bool(True, field="output.ROImenu.atlases.lpba40", usedefault=True, + desc="Extract brain measures for LPBA40 template") + cobra = traits.Bool(True, field="output.ROImenu.atlases.hammers", usedefault=True, + desc="Extract brain measures for COBRA template") + hammers = traits.Bool(True, field="output.ROImenu.atlases.cobra", usedefault=True, + desc="Extract brain measures for Hammers template") own_atlas = InputMultiPath(ImageFileSPM(exists=True), field="output.ROImenu.atlases.ownatlas", - desc="Own Atlas", mandatory=False, copyfile=False) + desc="Extract brain measures for a given template", mandatory=False, copyfile=False) _dartel_help = 'This option is to export data into a form that can be used with DARTEL. The SPM default is to ' \ 'only apply rigid body transformation. However, a more appropriate option is to apply affine ' \ @@ -146,47 +148,55 @@ class CAT12SegmentInputSpec(SPMCommandInputSpec): 'non-linearly register brains to the template.' # Grey matter - _gm_desc = 'Options to save grey matter images.' - gm_output_native = traits.Bool(False, field="output.GM.native", usedefault=True, desc=_gm_desc) - gm_output_modulated = traits.Bool(True, field="output.GM.mod", usedefault=True, desc=_gm_desc) - gm_output_dartel = traits.Bool(False, field="output.GM.dartel", usedefault=True, desc=_gm_desc) + gm_output_native = traits.Bool(False, field="output.GM.native", usedefault=True, + desc='Save modulated grey matter images.') + gm_output_modulated = traits.Bool(True, field="output.GM.mod", usedefault=True, + desc='Save native grey matter images.') + gm_output_dartel = traits.Bool(False, field="output.GM.dartel", usedefault=True, + desc='Save dartel grey matter images.') # White matter _wm_desc = 'Options to save white matter images.' - wm_output_native = traits.Bool(False, field="output.WM.native", usedefault=True, desc=_wm_desc) - wm_output_modulated = traits.Bool(True, field="output.WM.mod", usedefault=True, desc=_wm_desc) - wm_output_dartel = traits.Bool(False, field="output.WM.dartel", usedefault=True, desc=_wm_desc) + wm_output_native = traits.Bool(False, field="output.WM.native", usedefault=True, + desc='Save dartel white matter images.') + wm_output_modulated = traits.Bool(True, field="output.WM.mod", usedefault=True, + desc='Save dartel white matter images.') + wm_output_dartel = traits.Bool(False, field="output.WM.dartel", usedefault=True, + desc='Save dartel white matter images.') # CSF matter _csf_desc = 'Options to save CSF images.' - csf_output_native = traits.Bool(False, field="output.CSF.native", usedefault=True, desc=_csf_desc) - csf_output_modulated = traits.Bool(True, field="output.CSF.mod", usedefault=True, desc=_csf_desc) - csf_output_dartel = traits.Bool(False, field="output.CSF.dartel", usedefault=True, desc=_csf_desc) + csf_output_native = traits.Bool(False, field="output.CSF.native", usedefault=True, + desc='Save dartel CSF images.') + csf_output_modulated = traits.Bool(True, field="output.CSF.mod", usedefault=True, + desc='Save dartel CSF images.') + csf_output_dartel = traits.Bool(False, field="output.CSF.dartel", usedefault=True, + desc='Save dartel CSF images.') # Labels - _help_label_desc = 'This is the option to save a labeled version of your segmentations for fast visual ' \ + _help_label_desc = 'This is the option to save a labeled version of your segmentations in the %s space for fast visual ' \ 'comparision. Labels are saved as Partial Volume Estimation (PVE) values with different mix ' \ 'classes for GM-WM (2.5) and GM-CSF (1.5). BG=0, CSF=1, GM=2, WM=3, WMH=4 (if WMHC=3), ' \ 'SL=1.5 (if SLC)' - label_native = traits.Bool(False, field="output.label.native", usedefault=True, desc=_help_label_desc) - label_warped = traits.Bool(True, field="output.label.warped", usedefault=True, desc=_help_label_desc) - label_dartel = traits.Bool(False, field="output.label.dartel", usedefault=True, desc=_help_label_desc) - output_labelnative = traits.Bool(False, field="output.labelnative", usedefault=True, desc=_help_label_desc) + label_native = traits.Bool(False, field="output.label.native", usedefault=True, desc=_help_label_desc % "native") + label_warped = traits.Bool(True, field="output.label.warped", usedefault=True, desc=_help_label_desc % "warped") + label_dartel = traits.Bool(False, field="output.label.dartel", usedefault=True, desc=_help_label_desc % "dartel") + output_labelnative = traits.Bool(False, field="output.labelnative", usedefault=True, desc=_help_label_desc % "native") # Bias - save_bias_corrected = traits.Bool(True, field="output.bias.warped", usedefault=True) + save_bias_corrected = traits.Bool(True, field="output.bias.warped", usedefault=True, desc="Save bias corrected image") # las _las_desc = 'This is the option to save a bias, noise, and local intensity corrected version of the original T1' \ - ' image. MR images are usually corrupted by a smooth, spatially varying artifact that modulates the' \ + ' image in the %s space. MR images are usually corrupted by a smooth, spatially varying artifact that modulates the' \ ' intensity of the image (bias). These artifacts, although not usually a problem for visual ' \ 'inspection, can impede automated processing of the images. The bias corrected version should have ' \ 'more uniform intensities within the different types of tissues and can be saved in native space ' \ 'and/or normalised. Noise is corrected by an adaptive non-local mean (NLM) filter (Manjon 2008, ' \ 'Medical Image Analysis 12).' - las_native = traits.Bool(False, field="output.las.native", usedefault=True, desc=_las_desc) - las_warped = traits.Bool(True, field="output.las.warped", usedefault=True, desc=_las_desc) - las_dartel = traits.Bool(False, field="output.las.dartel", usedefault=True, desc=_las_desc) + las_native = traits.Bool(False, field="output.las.native", usedefault=True, desc=_las_desc % "native") + las_warped = traits.Bool(True, field="output.las.warped", usedefault=True, desc=_las_desc % "warped") + las_dartel = traits.Bool(False, field="output.las.dartel", usedefault=True, desc=_las_desc % "dartel") # Jacobian Warped _help_jacobian = 'This is the option to save the Jacobian determinant, which expresses local volume changes. This' \ From 04718ac71436b6f283af7575dda0f6998b64f893 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Fri, 30 Apr 2021 17:19:55 -0400 Subject: [PATCH 15/24] STY: black --- nipype/interfaces/cat12/preprocess.py | 633 +++++++++++++++++--------- nipype/interfaces/cat12/surface.py | 172 ++++--- 2 files changed, 540 insertions(+), 265 deletions(-) diff --git a/nipype/interfaces/cat12/preprocess.py b/nipype/interfaces/cat12/preprocess.py index 2fab299f15..caf337a211 100644 --- a/nipype/interfaces/cat12/preprocess.py +++ b/nipype/interfaces/cat12/preprocess.py @@ -1,228 +1,411 @@ import os from pathlib import Path -from nipype.interfaces.base import InputMultiPath, TraitedSpec, traits, isdefined, File, Str +from nipype.interfaces.base import ( + InputMultiPath, + TraitedSpec, + traits, + isdefined, + File, + Str, +) from nipype.interfaces.spm import SPMCommand -from nipype.interfaces.spm.base import SPMCommandInputSpec, ImageFileSPM, scans_for_fnames, scans_for_fname +from nipype.interfaces.spm.base import ( + SPMCommandInputSpec, + ImageFileSPM, + scans_for_fnames, + scans_for_fname, +) from nipype.utils.filemanip import split_filename, fname_presuffix class CAT12SegmentInputSpec(SPMCommandInputSpec): - in_files = InputMultiPath(ImageFileSPM(exists=True), field="data", desc="file to segment", mandatory=True, - copyfile=False) - - _help_tpm = 'Tissue Probability Map. Select the tissue probability image that includes 6 tissue probability ' \ - 'classes for (1) grey matter, (2) white matter, (3) cerebrospinal fluid, (4) bone, (5) non-brain ' \ - 'soft tissue, and (6) the background. CAT uses the TPM only for the initial SPM segmentation.' - tpm = InputMultiPath(ImageFileSPM(exists=True), field="tpm", desc=_help_tpm, mandatory=False, - copyfile=False) - - n_jobs = traits.Int(1, usedefault=True, mandatory=True, field="nproc", desc="Number of threads") - - _help_affine_reg = 'Affine Regularization. The procedure is a local optimisation, so it needs reasonable initial ' \ - 'starting estimates. Images should be placed in approximate alignment using the Display ' \ - 'function of SPM before beginning. A Mutual Information affine registration with the tissue ' \ - 'probability maps (D''Agostino et al, 2004) is used to achieve approximate alignment.' - affine_regularization = Str(default_value="mni", - field="opts.affreg", usedefault=True, desc=_help_affine_reg) - - _help_bias_acc = "Strength of the SPM inhomogeneity (bias) correction that simultaneously controls the SPM " \ - "biasreg, biasfwhm, samp (resolution), and tol (iteration) parameter." - power_spm_inhomogeneity_correction = traits.Float(default_value=0.5, field='opts.biasacc', - usedefault=True, - desc=_help_bias_acc) + in_files = InputMultiPath( + ImageFileSPM(exists=True), + field="data", + desc="file to segment", + mandatory=True, + copyfile=False, + ) + + _help_tpm = ( + "Tissue Probability Map. Select the tissue probability image that includes 6 tissue probability " + "classes for (1) grey matter, (2) white matter, (3) cerebrospinal fluid, (4) bone, (5) non-brain " + "soft tissue, and (6) the background. CAT uses the TPM only for the initial SPM segmentation." + ) + tpm = InputMultiPath( + ImageFileSPM(exists=True), + field="tpm", + desc=_help_tpm, + mandatory=False, + copyfile=False, + ) + + n_jobs = traits.Int( + 1, usedefault=True, mandatory=True, field="nproc", desc="Number of threads" + ) + + _help_affine_reg = ( + "Affine Regularization. The procedure is a local optimisation, so it needs reasonable initial " + "starting estimates. Images should be placed in approximate alignment using the Display " + "function of SPM before beginning. A Mutual Information affine registration with the tissue " + "probability maps (D" + "Agostino et al, 2004) is used to achieve approximate alignment." + ) + affine_regularization = Str( + default_value="mni", field="opts.affreg", usedefault=True, desc=_help_affine_reg + ) + + _help_bias_acc = ( + "Strength of the SPM inhomogeneity (bias) correction that simultaneously controls the SPM " + "biasreg, biasfwhm, samp (resolution), and tol (iteration) parameter." + ) + power_spm_inhomogeneity_correction = traits.Float( + default_value=0.5, field="opts.biasacc", usedefault=True, desc=_help_bias_acc + ) # Extended options for CAT12 preprocessing - _help_app = 'Affine registration and SPM preprocessing can fail in some subjects with deviating anatomy (e.g. ' \ - 'other species/neonates) or in images with strong signal inhomogeneities, or untypical intensities ' \ - '(e.g. synthetic images). An initial bias correction can help to reduce such problems (see details ' \ - 'below). Recommended are the "default" and "full" option.' - affine_preprocessing = traits.Int(1070, field="extopts.APP", desc=_help_app, usedefault=True) - - _help_initial_seg = 'In rare cases the Unified Segmentation can fail in highly abnormal brains, where e.g. the ' \ - 'cerebrospinal fluid of superlarge ventricles (hydrocephalus) were classified as white ' \ - 'matter. However, if the affine registration is correct, the AMAP segmentation with an ' \ - 'prior-independent k-means initialization can be used to replace the SPM brain tissue ' \ - 'classification. Moreover, if the default Dartel and Shooting registrations will fail then ' \ - 'rhe "Optimized Shooting - superlarge ventricles" option for "Spatial registration" is ! ' \ - 'required Values: \nnone: 0;\nlight: 1;\nfull: 2;\ndefault: 1070.' - initial_segmentation = traits.Int(0, field="extopts.spm_kamap", desc=_help_initial_seg, usedefault=True) - - _help_las = 'Additionally to WM-inhomogeneities, GM intensity can vary across different regions such as the motor' \ - ' cortex, the basal ganglia, or the occipital lobe. These changes have an anatomical background ' \ - '(e.g. iron content, myelinization), but are dependent on the MR-protocol and often lead to ' \ - 'underestimation of GM at higher intensities and overestimation of CSF at lower intensities. ' \ - 'Therefore, a local intensity transformation of all tissue classes is used to reduce these effects in' \ - ' the image. This local adaptive segmentation (LAS) is applied before the final AMAP segmentation.' \ - 'Possible Values: \nSPM Unified Segmentation: 0 \nk-means AMAP: 2' - local_adaptive_seg = traits.Float(0.5, field="extopts.LASstr", usedefault=True, desc=_help_las) - - _help_gcutstr = 'Method of initial skull-stripping before AMAP segmentation. The SPM approach works quite stable ' \ - 'for the majority of data. However, in some rare cases parts of GM (i.e. in frontal lobe) might ' \ - 'be cut. If this happens the GCUT approach is a good alternative. GCUT is a graph-cut/region-' \ - 'growing approach starting from the WM area. APRG (adaptive probability region-growing) is a new' \ - ' method that refines the probability maps of the SPM approach by region-growing techniques of ' \ - 'the gcut approach with a final surface-based optimization strategy. This is currently the method' \ - ' with the most accurate and reliable results. If you use already skull-stripped data you can ' \ - 'turn off skull-stripping although this is automaticaly detected in most cases. Please note that ' \ - 'the choice of the skull-stripping method will also influence the estimation of TIV, because the' \ - ' methods mainly differ in the handling of the outer CSF around the cortical surface. ' \ - '\nPossible Values:\n - none (already skull-stripped): -1;\n - SPM approach: 0; ' \ - '\n - GCUT approach: 0.50; \n - APRG approach: 2' - skull_strip = traits.Float(2, field="extopts.gcutstr", desc=_help_gcutstr, usedefault=True) - - _help_wmhc = 'WARNING: Please note that the detection of WM hyperintensies is still under development and does ' \ - 'not have the same accuracy as approaches that additionally consider FLAIR images (e.g. Lesion ' \ - 'Segmentation Toolbox)! In aging or (neurodegenerative) diseases WM intensity can be reduced ' \ - 'locally in T1 or increased in T2/PD images. These so-called WM hyperintensies (WMHs) can lead to ' \ - 'preprocessing errors. Large GM areas next to the ventricle can cause normalization problems. ' \ - 'Therefore, a temporary correction for normalization is useful if WMHs are expected. CAT allows ' \ - 'different ways to handle WMHs: ' \ - '\n0) No Correction (handled as GM). \n1) Temporary (internal) correction as WM for spatial ' \ - 'normalization and estimation of cortical thickness. \n2) Permanent correction to WM. ' - wm_hyper_intensity_correction = traits.Int(1, field="extopts.WMHC", desc=_help_wmhc, usedefault=True) - - _help_vox = 'The (isotropic) voxel sizes of any spatially normalised written images. A non-finite value will be ' \ - 'replaced by the average voxel size of the tissue probability maps used by the segmentation.' + _help_app = ( + "Affine registration and SPM preprocessing can fail in some subjects with deviating anatomy (e.g. " + "other species/neonates) or in images with strong signal inhomogeneities, or untypical intensities " + "(e.g. synthetic images). An initial bias correction can help to reduce such problems (see details " + 'below). Recommended are the "default" and "full" option.' + ) + affine_preprocessing = traits.Int( + 1070, field="extopts.APP", desc=_help_app, usedefault=True + ) + + _help_initial_seg = ( + "In rare cases the Unified Segmentation can fail in highly abnormal brains, where e.g. the " + "cerebrospinal fluid of superlarge ventricles (hydrocephalus) were classified as white " + "matter. However, if the affine registration is correct, the AMAP segmentation with an " + "prior-independent k-means initialization can be used to replace the SPM brain tissue " + "classification. Moreover, if the default Dartel and Shooting registrations will fail then " + 'rhe "Optimized Shooting - superlarge ventricles" option for "Spatial registration" is ! ' + "required Values: \nnone: 0;\nlight: 1;\nfull: 2;\ndefault: 1070." + ) + initial_segmentation = traits.Int( + 0, field="extopts.spm_kamap", desc=_help_initial_seg, usedefault=True + ) + + _help_las = ( + "Additionally to WM-inhomogeneities, GM intensity can vary across different regions such as the motor" + " cortex, the basal ganglia, or the occipital lobe. These changes have an anatomical background " + "(e.g. iron content, myelinization), but are dependent on the MR-protocol and often lead to " + "underestimation of GM at higher intensities and overestimation of CSF at lower intensities. " + "Therefore, a local intensity transformation of all tissue classes is used to reduce these effects in" + " the image. This local adaptive segmentation (LAS) is applied before the final AMAP segmentation." + "Possible Values: \nSPM Unified Segmentation: 0 \nk-means AMAP: 2" + ) + local_adaptive_seg = traits.Float( + 0.5, field="extopts.LASstr", usedefault=True, desc=_help_las + ) + + _help_gcutstr = ( + "Method of initial skull-stripping before AMAP segmentation. The SPM approach works quite stable " + "for the majority of data. However, in some rare cases parts of GM (i.e. in frontal lobe) might " + "be cut. If this happens the GCUT approach is a good alternative. GCUT is a graph-cut/region-" + "growing approach starting from the WM area. APRG (adaptive probability region-growing) is a new" + " method that refines the probability maps of the SPM approach by region-growing techniques of " + "the gcut approach with a final surface-based optimization strategy. This is currently the method" + " with the most accurate and reliable results. If you use already skull-stripped data you can " + "turn off skull-stripping although this is automaticaly detected in most cases. Please note that " + "the choice of the skull-stripping method will also influence the estimation of TIV, because the" + " methods mainly differ in the handling of the outer CSF around the cortical surface. " + "\nPossible Values:\n - none (already skull-stripped): -1;\n - SPM approach: 0; " + "\n - GCUT approach: 0.50; \n - APRG approach: 2" + ) + skull_strip = traits.Float( + 2, field="extopts.gcutstr", desc=_help_gcutstr, usedefault=True + ) + + _help_wmhc = ( + "WARNING: Please note that the detection of WM hyperintensies is still under development and does " + "not have the same accuracy as approaches that additionally consider FLAIR images (e.g. Lesion " + "Segmentation Toolbox)! In aging or (neurodegenerative) diseases WM intensity can be reduced " + "locally in T1 or increased in T2/PD images. These so-called WM hyperintensies (WMHs) can lead to " + "preprocessing errors. Large GM areas next to the ventricle can cause normalization problems. " + "Therefore, a temporary correction for normalization is useful if WMHs are expected. CAT allows " + "different ways to handle WMHs: " + "\n0) No Correction (handled as GM). \n1) Temporary (internal) correction as WM for spatial " + "normalization and estimation of cortical thickness. \n2) Permanent correction to WM. " + ) + wm_hyper_intensity_correction = traits.Int( + 1, field="extopts.WMHC", desc=_help_wmhc, usedefault=True + ) + + _help_vox = ( + "The (isotropic) voxel sizes of any spatially normalised written images. A non-finite value will be " + "replaced by the average voxel size of the tissue probability maps used by the segmentation." + ) voxel_size = traits.Float(1.5, field="extopts.vox", desc=_help_vox, usedefault=True) - _help_resampling = 'Internal resampling for preprocessing.\n The default fixed image resolution offers a good ' \ - 'trade-off between optimal quality and preprocessing time and memory demands. Standard ' \ - 'structural data with a voxel resolution around 1mm or even data with high in-plane resolution' \ - ' and large slice thickness (e.g. 0.5x0.5x1.5 mm) will benefit from this setting. If you have' \ - ' higher native resolutions the highres option "Fixed 0.8 mm" will sometimes offer slightly' \ - ' better preprocessing quality with an increase of preprocessing time and memory demands. In' \ - ' case of even higher resolutions and high signal-to-noise ratio (e.g. for 7 T data) the ' \ - '"Best native" option will process the data on the highest native resolution. A resolution' \ - ' of 0.4x0.7x1.0 mm will be interpolated to 0.4x0.4x0.4 mm. A tolerance range of 0.1 mm is ' \ - 'used to avoid interpolation artifacts, i.e. a resolution of 0.95x1.01x1.08 mm will not be ' \ - 'interpolated in case of the "Fixed 1.0 mm"! This "optimal" option prefers an isotropic voxel ' \ - 'size with at least 1.1 mm that is controlled by the median voxel size and a volume term that ' \ - 'penalizes highly anisotropic voxels.' \ - 'Values:\nOptimal: [1.0 0.1]\nFixed 1.0 mm: [1.0 0.1];\nFixed 0.8 mm:[0.8 0.1]' \ - '\nBest native: [0.5 0.1]' - internal_resampling_process = traits.Tuple(traits.Float(1), traits.Float(0.1), minlen=2, maxlen=2, usedefault=True, - field="extopts.restypes.optimal", desc="help_resampling") - _errors_help = 'Error handling.\nTry to catch preprocessing errors and continue with the next data set or ignore ' \ - 'all warnings (e.g., bad intensities) and use an experimental pipeline which is still in ' \ - 'development. In case of errors, CAT continues with the next subject if this option is enabled. If ' \ - 'the experimental option with backup functions is selected and warnings occur, CAT will try to use' \ - ' backup routines and skip some processing steps which require good T1 contrasts (e.g., LAS). If ' \ - 'you want to avoid processing of critical data and ensure that only the main pipeline is used then' \ - ' select the option "Ignore errors (continue with the next subject)". It is strongly recommended ' \ - 'to check for preprocessing problems, especially with non-T1 contrasts. ' \ - '\nValues:\nnone: 0,\ndefault: 1,\ndetails: 2.' - ignore_errors = traits.Int(1, field="extopts.ignoreErrors", desc=_errors_help, usedefault=True) + _help_resampling = ( + "Internal resampling for preprocessing.\n The default fixed image resolution offers a good " + "trade-off between optimal quality and preprocessing time and memory demands. Standard " + "structural data with a voxel resolution around 1mm or even data with high in-plane resolution" + " and large slice thickness (e.g. 0.5x0.5x1.5 mm) will benefit from this setting. If you have" + ' higher native resolutions the highres option "Fixed 0.8 mm" will sometimes offer slightly' + " better preprocessing quality with an increase of preprocessing time and memory demands. In" + " case of even higher resolutions and high signal-to-noise ratio (e.g. for 7 T data) the " + '"Best native" option will process the data on the highest native resolution. A resolution' + " of 0.4x0.7x1.0 mm will be interpolated to 0.4x0.4x0.4 mm. A tolerance range of 0.1 mm is " + "used to avoid interpolation artifacts, i.e. a resolution of 0.95x1.01x1.08 mm will not be " + 'interpolated in case of the "Fixed 1.0 mm"! This "optimal" option prefers an isotropic voxel ' + "size with at least 1.1 mm that is controlled by the median voxel size and a volume term that " + "penalizes highly anisotropic voxels." + "Values:\nOptimal: [1.0 0.1]\nFixed 1.0 mm: [1.0 0.1];\nFixed 0.8 mm:[0.8 0.1]" + "\nBest native: [0.5 0.1]" + ) + internal_resampling_process = traits.Tuple( + traits.Float(1), + traits.Float(0.1), + minlen=2, + maxlen=2, + usedefault=True, + field="extopts.restypes.optimal", + desc="help_resampling", + ) + _errors_help = ( + "Error handling.\nTry to catch preprocessing errors and continue with the next data set or ignore " + "all warnings (e.g., bad intensities) and use an experimental pipeline which is still in " + "development. In case of errors, CAT continues with the next subject if this option is enabled. If " + "the experimental option with backup functions is selected and warnings occur, CAT will try to use" + " backup routines and skip some processing steps which require good T1 contrasts (e.g., LAS). If " + "you want to avoid processing of critical data and ensure that only the main pipeline is used then" + ' select the option "Ignore errors (continue with the next subject)". It is strongly recommended ' + "to check for preprocessing problems, especially with non-T1 contrasts. " + "\nValues:\nnone: 0,\ndefault: 1,\ndetails: 2." + ) + ignore_errors = traits.Int( + 1, field="extopts.ignoreErrors", desc=_errors_help, usedefault=True + ) # Writing options - _help_surf = 'Surface and thickness estimation. \nUse projection-based thickness (PBT) (Dahnke et al. 2012) to' \ - ' estimate cortical thickness and to create the central cortical surface for left and right ' \ - 'hemisphere. Surface reconstruction includes topology correction (Yotter et al. 2011), spherical ' \ - 'inflation (Yotter et al.) and spherical registration. Additionally you can also estimate surface ' \ - 'parameters such as gyrification, cortical complexity or sulcal depth that can be subsequently ' \ - 'analyzed at each vertex of the surface. Please note, that surface reconstruction and spherical ' \ - 'registration additionally requires about 20-60 min of computation time. A fast (1-3 min) surface ' \ - 'pipeline is available for visual preview (e.g., to check preprocessing quality) in the ' \ - 'cross-sectional, but not in the longitudinal pipeline. Only the initial surfaces are created with ' \ - 'a lower resolution and without topology correction, spherical mapping and surface registration. ' \ - 'Please note that the files with the estimated surface thickness can therefore not be used for ' \ - 'further analysis! For distinction, these files contain "preview" in their filename and they' \ - ' are not available as batch dependencies objects. ' - surface_and_thickness_estimation = traits.Int(1, field="surface", desc=_help_surf, usedefault=True) - surface_measures = traits.Int(1, field="output.surf_measures", usedefault=True, desc="Extract surface measures") + _help_surf = ( + "Surface and thickness estimation. \nUse projection-based thickness (PBT) (Dahnke et al. 2012) to" + " estimate cortical thickness and to create the central cortical surface for left and right " + "hemisphere. Surface reconstruction includes topology correction (Yotter et al. 2011), spherical " + "inflation (Yotter et al.) and spherical registration. Additionally you can also estimate surface " + "parameters such as gyrification, cortical complexity or sulcal depth that can be subsequently " + "analyzed at each vertex of the surface. Please note, that surface reconstruction and spherical " + "registration additionally requires about 20-60 min of computation time. A fast (1-3 min) surface " + "pipeline is available for visual preview (e.g., to check preprocessing quality) in the " + "cross-sectional, but not in the longitudinal pipeline. Only the initial surfaces are created with " + "a lower resolution and without topology correction, spherical mapping and surface registration. " + "Please note that the files with the estimated surface thickness can therefore not be used for " + 'further analysis! For distinction, these files contain "preview" in their filename and they' + " are not available as batch dependencies objects. " + ) + surface_and_thickness_estimation = traits.Int( + 1, field="surface", desc=_help_surf, usedefault=True + ) + surface_measures = traits.Int( + 1, + field="output.surf_measures", + usedefault=True, + desc="Extract surface measures", + ) # Templates - neuromorphometrics = traits.Bool(True, field="output.ROImenu.atlases.neuromorphometrics", - usedefault=True, desc="Extract brain measures for Neuromorphometrics template") - lpba40 = traits.Bool(True, field="output.ROImenu.atlases.lpba40", usedefault=True, - desc="Extract brain measures for LPBA40 template") - cobra = traits.Bool(True, field="output.ROImenu.atlases.hammers", usedefault=True, - desc="Extract brain measures for COBRA template") - hammers = traits.Bool(True, field="output.ROImenu.atlases.cobra", usedefault=True, - desc="Extract brain measures for Hammers template") - own_atlas = InputMultiPath(ImageFileSPM(exists=True), field="output.ROImenu.atlases.ownatlas", - desc="Extract brain measures for a given template", mandatory=False, copyfile=False) - - _dartel_help = 'This option is to export data into a form that can be used with DARTEL. The SPM default is to ' \ - 'only apply rigid body transformation. However, a more appropriate option is to apply affine ' \ - 'transformation, because the additional scaling of the images requires less deformations to ' \ - 'non-linearly register brains to the template.' + neuromorphometrics = traits.Bool( + True, + field="output.ROImenu.atlases.neuromorphometrics", + usedefault=True, + desc="Extract brain measures for Neuromorphometrics template", + ) + lpba40 = traits.Bool( + True, + field="output.ROImenu.atlases.lpba40", + usedefault=True, + desc="Extract brain measures for LPBA40 template", + ) + cobra = traits.Bool( + True, + field="output.ROImenu.atlases.hammers", + usedefault=True, + desc="Extract brain measures for COBRA template", + ) + hammers = traits.Bool( + True, + field="output.ROImenu.atlases.cobra", + usedefault=True, + desc="Extract brain measures for Hammers template", + ) + own_atlas = InputMultiPath( + ImageFileSPM(exists=True), + field="output.ROImenu.atlases.ownatlas", + desc="Extract brain measures for a given template", + mandatory=False, + copyfile=False, + ) + + _dartel_help = ( + "This option is to export data into a form that can be used with DARTEL. The SPM default is to " + "only apply rigid body transformation. However, a more appropriate option is to apply affine " + "transformation, because the additional scaling of the images requires less deformations to " + "non-linearly register brains to the template." + ) # Grey matter - gm_output_native = traits.Bool(False, field="output.GM.native", usedefault=True, - desc='Save modulated grey matter images.') - gm_output_modulated = traits.Bool(True, field="output.GM.mod", usedefault=True, - desc='Save native grey matter images.') - gm_output_dartel = traits.Bool(False, field="output.GM.dartel", usedefault=True, - desc='Save dartel grey matter images.') + gm_output_native = traits.Bool( + False, + field="output.GM.native", + usedefault=True, + desc="Save modulated grey matter images.", + ) + gm_output_modulated = traits.Bool( + True, + field="output.GM.mod", + usedefault=True, + desc="Save native grey matter images.", + ) + gm_output_dartel = traits.Bool( + False, + field="output.GM.dartel", + usedefault=True, + desc="Save dartel grey matter images.", + ) # White matter - _wm_desc = 'Options to save white matter images.' - wm_output_native = traits.Bool(False, field="output.WM.native", usedefault=True, - desc='Save dartel white matter images.') - wm_output_modulated = traits.Bool(True, field="output.WM.mod", usedefault=True, - desc='Save dartel white matter images.') - wm_output_dartel = traits.Bool(False, field="output.WM.dartel", usedefault=True, - desc='Save dartel white matter images.') + _wm_desc = "Options to save white matter images." + wm_output_native = traits.Bool( + False, + field="output.WM.native", + usedefault=True, + desc="Save dartel white matter images.", + ) + wm_output_modulated = traits.Bool( + True, + field="output.WM.mod", + usedefault=True, + desc="Save dartel white matter images.", + ) + wm_output_dartel = traits.Bool( + False, + field="output.WM.dartel", + usedefault=True, + desc="Save dartel white matter images.", + ) # CSF matter - _csf_desc = 'Options to save CSF images.' - csf_output_native = traits.Bool(False, field="output.CSF.native", usedefault=True, - desc='Save dartel CSF images.') - csf_output_modulated = traits.Bool(True, field="output.CSF.mod", usedefault=True, - desc='Save dartel CSF images.') - csf_output_dartel = traits.Bool(False, field="output.CSF.dartel", usedefault=True, - desc='Save dartel CSF images.') + _csf_desc = "Options to save CSF images." + csf_output_native = traits.Bool( + False, + field="output.CSF.native", + usedefault=True, + desc="Save dartel CSF images.", + ) + csf_output_modulated = traits.Bool( + True, field="output.CSF.mod", usedefault=True, desc="Save dartel CSF images." + ) + csf_output_dartel = traits.Bool( + False, + field="output.CSF.dartel", + usedefault=True, + desc="Save dartel CSF images.", + ) # Labels - _help_label_desc = 'This is the option to save a labeled version of your segmentations in the %s space for fast visual ' \ - 'comparision. Labels are saved as Partial Volume Estimation (PVE) values with different mix ' \ - 'classes for GM-WM (2.5) and GM-CSF (1.5). BG=0, CSF=1, GM=2, WM=3, WMH=4 (if WMHC=3), ' \ - 'SL=1.5 (if SLC)' - label_native = traits.Bool(False, field="output.label.native", usedefault=True, desc=_help_label_desc % "native") - label_warped = traits.Bool(True, field="output.label.warped", usedefault=True, desc=_help_label_desc % "warped") - label_dartel = traits.Bool(False, field="output.label.dartel", usedefault=True, desc=_help_label_desc % "dartel") - output_labelnative = traits.Bool(False, field="output.labelnative", usedefault=True, desc=_help_label_desc % "native") + _help_label_desc = ( + "This is the option to save a labeled version of your segmentations in the %s space for fast visual " + "comparision. Labels are saved as Partial Volume Estimation (PVE) values with different mix " + "classes for GM-WM (2.5) and GM-CSF (1.5). BG=0, CSF=1, GM=2, WM=3, WMH=4 (if WMHC=3), " + "SL=1.5 (if SLC)" + ) + label_native = traits.Bool( + False, + field="output.label.native", + usedefault=True, + desc=_help_label_desc % "native", + ) + label_warped = traits.Bool( + True, + field="output.label.warped", + usedefault=True, + desc=_help_label_desc % "warped", + ) + label_dartel = traits.Bool( + False, + field="output.label.dartel", + usedefault=True, + desc=_help_label_desc % "dartel", + ) + output_labelnative = traits.Bool( + False, + field="output.labelnative", + usedefault=True, + desc=_help_label_desc % "native", + ) # Bias - save_bias_corrected = traits.Bool(True, field="output.bias.warped", usedefault=True, desc="Save bias corrected image") + save_bias_corrected = traits.Bool( + True, + field="output.bias.warped", + usedefault=True, + desc="Save bias corrected image", + ) # las - _las_desc = 'This is the option to save a bias, noise, and local intensity corrected version of the original T1' \ - ' image in the %s space. MR images are usually corrupted by a smooth, spatially varying artifact that modulates the' \ - ' intensity of the image (bias). These artifacts, although not usually a problem for visual ' \ - 'inspection, can impede automated processing of the images. The bias corrected version should have ' \ - 'more uniform intensities within the different types of tissues and can be saved in native space ' \ - 'and/or normalised. Noise is corrected by an adaptive non-local mean (NLM) filter (Manjon 2008, ' \ - 'Medical Image Analysis 12).' - las_native = traits.Bool(False, field="output.las.native", usedefault=True, desc=_las_desc % "native") - las_warped = traits.Bool(True, field="output.las.warped", usedefault=True, desc=_las_desc % "warped") - las_dartel = traits.Bool(False, field="output.las.dartel", usedefault=True, desc=_las_desc % "dartel") + _las_desc = ( + "This is the option to save a bias, noise, and local intensity corrected version of the original T1" + " image in the %s space. MR images are usually corrupted by a smooth, spatially varying artifact that modulates the" + " intensity of the image (bias). These artifacts, although not usually a problem for visual " + "inspection, can impede automated processing of the images. The bias corrected version should have " + "more uniform intensities within the different types of tissues and can be saved in native space " + "and/or normalised. Noise is corrected by an adaptive non-local mean (NLM) filter (Manjon 2008, " + "Medical Image Analysis 12)." + ) + las_native = traits.Bool( + False, field="output.las.native", usedefault=True, desc=_las_desc % "native" + ) + las_warped = traits.Bool( + True, field="output.las.warped", usedefault=True, desc=_las_desc % "warped" + ) + las_dartel = traits.Bool( + False, field="output.las.dartel", usedefault=True, desc=_las_desc % "dartel" + ) # Jacobian Warped - _help_jacobian = 'This is the option to save the Jacobian determinant, which expresses local volume changes. This' \ - ' image can be used in a pure deformation based morphometry (DBM) design. Please note that the' \ - ' affine part of the deformation field is ignored. Thus, there is no need for any additional' \ - ' correction for different brain sizes using ICV.' - jacobianwarped = traits.Bool(True, field="output.jacobianwarped", usedefault=True, desc=_help_jacobian) + _help_jacobian = ( + "This is the option to save the Jacobian determinant, which expresses local volume changes. This" + " image can be used in a pure deformation based morphometry (DBM) design. Please note that the" + " affine part of the deformation field is ignored. Thus, there is no need for any additional" + " correction for different brain sizes using ICV." + ) + jacobianwarped = traits.Bool( + True, field="output.jacobianwarped", usedefault=True, desc=_help_jacobian + ) # Deformation Fields - _help_warp = 'Deformation fields can be saved to disk, and used by the Deformations Utility and/or applied to ' \ - 'coregistered data from other modalities (e.g. fMRI). For spatially normalising images to MNI space,' \ - ' you will need the forward deformation, whereas for spatially normalising (eg) GIFTI surface files,' \ - ' you''ll need the inverse. It is also possible to transform data in MNI space on to the individual' \ - ' subject, which also requires the inverse transform. Deformations are saved as .nii files, which' \ - ' contain three volumes to encode the x, y and z coordinates.' \ - '\nValues: No:[0 0];\nImage->Template (forward): [1 0];\nTemplate->Image (inverse): [0 1]; ' \ - '\ninverse + forward: [1 1]' - warps = traits.Tuple(traits.Int(1), traits.Int(0), minlen=2, maxlen=2, - field="output.warps", usedefault=True, desc=_help_warp) + _help_warp = ( + "Deformation fields can be saved to disk, and used by the Deformations Utility and/or applied to " + "coregistered data from other modalities (e.g. fMRI). For spatially normalising images to MNI space," + " you will need the forward deformation, whereas for spatially normalising (eg) GIFTI surface files," + " you" + "ll need the inverse. It is also possible to transform data in MNI space on to the individual" + " subject, which also requires the inverse transform. Deformations are saved as .nii files, which" + " contain three volumes to encode the x, y and z coordinates." + "\nValues: No:[0 0];\nImage->Template (forward): [1 0];\nTemplate->Image (inverse): [0 1]; " + "\ninverse + forward: [1 1]" + ) + warps = traits.Tuple( + traits.Int(1), + traits.Int(0), + minlen=2, + maxlen=2, + field="output.warps", + usedefault=True, + desc=_help_warp, + ) class CAT12SegmentOutputSpec(TraitedSpec): ########################################## # Label XML files ########################################## - label_files = traits.List(File(exists=True), desc="Files with the measures extracted for OI ands ROIs") + label_files = traits.List( + File(exists=True), desc="Files with the measures extracted for OI ands ROIs" + ) label_rois = File(exists=True, desc="Files with thickness values of ROIs.") label_roi = File(exists=True, desc="Files with thickness values of ROI.") @@ -311,8 +494,7 @@ def __init__(self, **inputs): SPMCommand.__init__(self, **inputs) def _format_arg(self, opt, spec, val): - """Convert input to appropriate format for spm - """ + """Convert input to appropriate format for spm""" if opt in ["in_files"]: if isinstance(val, list): return scans_for_fnames(val) @@ -325,41 +507,64 @@ def _list_outputs(self): f = self.inputs.in_files[0] pth, base, ext = split_filename(f) - outputs["mri_images"] = [str(mri) for mri in Path(pth).glob("mri/*") if mri.is_file()] + outputs["mri_images"] = [ + str(mri) for mri in Path(pth).glob("mri/*") if mri.is_file() + ] for tidx, tissue in enumerate(["gm", "wm", "csf"]): - for idx, (image, prefix) in enumerate([("modulated", "mw"), ("dartel", "r"), ("native", "")]): - outtype = f'{tissue}_output_{image}' - if isdefined(getattr(self.inputs, outtype)) and getattr(self.inputs, outtype): - outfield = f'{tissue}_{image}_image' - prefix = os.path.join("mri", f'{prefix}p{tidx + 1}') + for idx, (image, prefix) in enumerate( + [("modulated", "mw"), ("dartel", "r"), ("native", "")] + ): + outtype = f"{tissue}_output_{image}" + if isdefined(getattr(self.inputs, outtype)) and getattr( + self.inputs, outtype + ): + outfield = f"{tissue}_{image}_image" + prefix = os.path.join("mri", f"{prefix}p{tidx + 1}") if image != "dartel": outputs[outfield] = fname_presuffix(f, prefix=prefix) else: - outputs[outfield] = fname_presuffix(f, prefix=prefix, suffix="_rigid") + outputs[outfield] = fname_presuffix( + f, prefix=prefix, suffix="_rigid" + ) if self.inputs.save_bias_corrected: - outputs["bias_corrected_image"] = fname_presuffix(f, prefix=os.path.join("mri", 'wmi')) + outputs["bias_corrected_image"] = fname_presuffix( + f, prefix=os.path.join("mri", "wmi") + ) - outputs["surface_files"] = [str(surf) for surf in Path(pth).glob("surf/*") if surf.is_file()] + outputs["surface_files"] = [ + str(surf) for surf in Path(pth).glob("surf/*") if surf.is_file() + ] for hemisphere in ["rh", "lh"]: for suffix in ["central", "sphere"]: - outfield = f'{hemisphere}_{suffix}_surface' - outputs[outfield] = fname_presuffix(f, prefix=os.path.join("surf", f'{hemisphere}.{suffix}.'), - suffix=".gii", use_ext=False) - - outputs["report_files"] = outputs["report_files"] = [str(report) for report in Path(pth).glob("report/*") - if report.is_file()] - - outputs[f'report'] = fname_presuffix(f, prefix=os.path.join("report", f'cat_'), suffix=".xml", use_ext=False) - - outputs["label_files"] = [str(label) for label in Path(pth).glob("label/*") if label.is_file()] - - outputs['label_rois'] = fname_presuffix(f, prefix=os.path.join("label", f'catROIs_'), suffix=".xml", - use_ext=False) - outputs['label_roi'] = fname_presuffix(f, prefix=os.path.join("label", f'catROI_'), suffix=".xml", - use_ext=False) + outfield = f"{hemisphere}_{suffix}_surface" + outputs[outfield] = fname_presuffix( + f, + prefix=os.path.join("surf", f"{hemisphere}.{suffix}."), + suffix=".gii", + use_ext=False, + ) + + outputs["report_files"] = outputs["report_files"] = [ + str(report) for report in Path(pth).glob("report/*") if report.is_file() + ] + + outputs[f"report"] = fname_presuffix( + f, prefix=os.path.join("report", f"cat_"), suffix=".xml", use_ext=False + ) + + outputs["label_files"] = [ + str(label) for label in Path(pth).glob("label/*") if label.is_file() + ] + + outputs["label_rois"] = fname_presuffix( + f, prefix=os.path.join("label", f"catROIs_"), suffix=".xml", use_ext=False + ) + outputs["label_roi"] = fname_presuffix( + f, prefix=os.path.join("label", f"catROI_"), suffix=".xml", use_ext=False + ) return outputs diff --git a/nipype/interfaces/cat12/surface.py b/nipype/interfaces/cat12/surface.py index dedaa53b7c..39925e6f29 100644 --- a/nipype/interfaces/cat12/surface.py +++ b/nipype/interfaces/cat12/surface.py @@ -10,33 +10,63 @@ class ExtractAdditionalSurfaceParametersInputSpec(SPMCommandInputSpec): - left_central_surfaces = InputMultiPath(File(exists=True), field="data_surf", - desc="Left and central surfaces files", mandatory=True, copyfile=False) - surface_files = InputMultiPath(File(exists=True), - desc="All surface files", mandatory=False, copyfile=False) - - gyrification = traits.Bool(True, field="GI", usedefault=True, - desc="Extract gyrification index (GI) based on absolute mean curvature. The" - " method is described in Luders et al. Neuroimage, 29:1224-1230, 2006") + left_central_surfaces = InputMultiPath( + File(exists=True), + field="data_surf", + desc="Left and central surfaces files", + mandatory=True, + copyfile=False, + ) + surface_files = InputMultiPath( + File(exists=True), desc="All surface files", mandatory=False, copyfile=False + ) + + gyrification = traits.Bool( + True, + field="GI", + usedefault=True, + desc="Extract gyrification index (GI) based on absolute mean curvature. The" + " method is described in Luders et al. Neuroimage, 29:1224-1230, 2006", + ) gmv = traits.Bool(True, field="gmv", usedefault=True, desc="Extract volume") area = traits.Bool(True, field="area", usedefault=True, desc="Extract area surface") - depth = traits.Bool(False, field="SD", usedefault=True, - desc="Extract sulcus depth based on euclidian distance between the central " - "surface anf its convex hull.") - fractal_dimension = traits.Bool(False, field="FD", usedefault=True, - desc="Extract cortical complexity (fractal dimension) which is " - "described in Yotter ar al. Neuroimage, 56(3): 961-973, 2011") + depth = traits.Bool( + False, + field="SD", + usedefault=True, + desc="Extract sulcus depth based on euclidian distance between the central " + "surface anf its convex hull.", + ) + fractal_dimension = traits.Bool( + False, + field="FD", + usedefault=True, + desc="Extract cortical complexity (fractal dimension) which is " + "described in Yotter ar al. Neuroimage, 56(3): 961-973, 2011", + ) class ExtractAdditionalSurfaceParametersOutputSpec(TraitedSpec): - lh_extracted_files = traits.List(File(exists=True), desc="Files of left Hemisphere extracted measures") - rh_extracted_files = traits.List(File(exists=True), desc="Files of right Hemisphere extracted measures") - - lh_gyrification = traits.List(File(exists=True), desc="Gyrification of left Hemisphere") - rh_gyrification = traits.List(File(exists=True), desc="Gyrification of right Hemisphere") - - lh_gmv = traits.List(File(exists=True), desc="Grey matter volume of left Hemisphere") - rh_gmv = traits.List(File(exists=True), desc="Grey matter volume of right Hemisphere") + lh_extracted_files = traits.List( + File(exists=True), desc="Files of left Hemisphere extracted measures" + ) + rh_extracted_files = traits.List( + File(exists=True), desc="Files of right Hemisphere extracted measures" + ) + + lh_gyrification = traits.List( + File(exists=True), desc="Gyrification of left Hemisphere" + ) + rh_gyrification = traits.List( + File(exists=True), desc="Gyrification of right Hemisphere" + ) + + lh_gmv = traits.List( + File(exists=True), desc="Grey matter volume of left Hemisphere" + ) + rh_gmv = traits.List( + File(exists=True), desc="Grey matter volume of right Hemisphere" + ) lh_area = traits.List(File(exists=True), desc="Area of left Hemisphere") rh_area = traits.List(File(exists=True), desc="Area of right Hemisphere") @@ -44,8 +74,12 @@ class ExtractAdditionalSurfaceParametersOutputSpec(TraitedSpec): lh_depth = traits.List(File(exists=True), desc="Depth of left Hemisphere") rh_depth = traits.List(File(exists=True), desc="Depth of right Hemisphere") - lh_fractaldimension = traits.List(File(exists=True), desc="Fractal Dimension of left Hemisphere") - rh_fractaldimension = traits.List(File(exists=True), desc="Fractal Dimension of right Hemisphere") + lh_fractaldimension = traits.List( + File(exists=True), desc="Fractal Dimension of left Hemisphere" + ) + rh_fractaldimension = traits.List( + File(exists=True), desc="Fractal Dimension of right Hemisphere" + ) class ExtractAdditionalSurfaceParameters(SPMCommand): @@ -76,6 +110,7 @@ class ExtractAdditionalSurfaceParameters(SPMCommand): >>> extract_additional_measures.run() # doctest: +SKIP """ + input_spec = ExtractAdditionalSurfaceParametersInputSpec output_spec = ExtractAdditionalSurfaceParametersOutputSpec @@ -90,9 +125,13 @@ def __init__(self, **inputs): def _list_outputs(self): outputs = self._outputs().get() - names_outputs = [(self.inputs.gyrification, 'gyrification'), (self.inputs.gmv, 'gmv'), - (self.inputs.area, 'area'), (self.inputs.depth, 'depth'), - (self.inputs.fractal_dimension, 'fractaldimension')] + names_outputs = [ + (self.inputs.gyrification, "gyrification"), + (self.inputs.gmv, "gmv"), + (self.inputs.area, "area"), + (self.inputs.depth, "depth"), + (self.inputs.fractal_dimension, "fractaldimension"), + ] for filename in self.inputs.left_central_surfaces: pth, base, ext = split_filename(filename) @@ -101,47 +140,79 @@ def _list_outputs(self): for i, (extracted_parameter, parameter_name) in enumerate(names_outputs): if extracted_parameter: for hemisphere in ["rh", "lh"]: - all_files_hemisphere = hemisphere + '_extracted_files' + all_files_hemisphere = hemisphere + "_extracted_files" name_hemisphere = hemisphere + "_" + parameter_name if isinstance(outputs[name_hemisphere], _Undefined): outputs[name_hemisphere] = [] if isinstance(outputs[all_files_hemisphere], _Undefined): outputs[all_files_hemisphere] = [] - generated_filename = ".".join([hemisphere, parameter_name, original_filename]) - outputs[name_hemisphere].append(os.path.join(pth, generated_filename)) + generated_filename = ".".join( + [hemisphere, parameter_name, original_filename] + ) + outputs[name_hemisphere].append( + os.path.join(pth, generated_filename) + ) # Add all hemisphere files into one list, this is important because only the left hemisphere # files are used as input in the Surface ROI Tools, fpr instance. - outputs[all_files_hemisphere].append(os.path.join(pth, generated_filename)) + outputs[all_files_hemisphere].append( + os.path.join(pth, generated_filename) + ) return outputs def _format_arg(self, opt, spec, val): if opt == "left_central_surfaces": return Cell2Str(val) - return super(ExtractAdditionalSurfaceParameters, self)._format_arg(opt, spec, val) + return super(ExtractAdditionalSurfaceParameters, self)._format_arg( + opt, spec, val + ) class ExtractROIBasedSurfaceMeasuresInputSpec(SPMCommandInputSpec): # Only these files are given as input, yet the right hemisphere (rh) files should also be on the processing # directory. - surface_files = InputMultiPath(File(exists=True), desc="Surface data files. This variable should be a list " - "with all", mandatory=False, copyfile=False) - lh_roi_atlas = InputMultiPath(File(exists=True), field="rdata", desc="(Left) ROI Atlas. These are the ROI's ", - mandatory=True, copyfile=False) - - rh_roi_atlas = InputMultiPath(File(exists=True), desc="(Right) ROI Atlas. These are the ROI's ", - mandatory=False, copyfile=False) - - lh_surface_measure = InputMultiPath(File(exists=True), field="cdata", desc="(Left) Surface data files. ", - mandatory=True, copyfile=False) - rh_surface_measure = InputMultiPath(File(exists=True), desc="(Right) Surface data files.", - mandatory=False, copyfile=False) + surface_files = InputMultiPath( + File(exists=True), + desc="Surface data files. This variable should be a list " "with all", + mandatory=False, + copyfile=False, + ) + lh_roi_atlas = InputMultiPath( + File(exists=True), + field="rdata", + desc="(Left) ROI Atlas. These are the ROI's ", + mandatory=True, + copyfile=False, + ) + + rh_roi_atlas = InputMultiPath( + File(exists=True), + desc="(Right) ROI Atlas. These are the ROI's ", + mandatory=False, + copyfile=False, + ) + + lh_surface_measure = InputMultiPath( + File(exists=True), + field="cdata", + desc="(Left) Surface data files. ", + mandatory=True, + copyfile=False, + ) + rh_surface_measure = InputMultiPath( + File(exists=True), + desc="(Right) Surface data files.", + mandatory=False, + copyfile=False, + ) class ExtractROIBasedSurfaceMeasuresOutputSpec(TraitedSpec): - label_files = traits.List(File(exists=True), desc="Files with the measures extracted for ROIs.") + label_files = traits.List( + File(exists=True), desc="Files with the measures extracted for ROIs." + ) class ExtractROIBasedSurfaceMeasures(SPMCommand): @@ -194,7 +265,9 @@ def _list_outputs(self): pth, base, ext = split_filename(self.inputs.lh_surface_measure[0]) - outputs["label_files"] = [str(label) for label in Path(pth).glob("label/*") if label.is_file()] + outputs["label_files"] = [ + str(label) for label in Path(pth).glob("label/*") if label.is_file() + ] return outputs @@ -204,21 +277,18 @@ def __init__(self, arg): def to_string(self): if isinstance(self.arg, list): - v = '\n'.join([f"'{el}'" for el in self.arg]) + v = "\n".join([f"'{el}'" for el in self.arg]) else: v = self.arg return v class NestedCell(Cell): - def __str__(self): return "{{%s}}" % self.to_string() class Cell2Str(Cell): - def __str__(self): - """Convert input to appropriate format for cat12 - """ + """Convert input to appropriate format for cat12""" return "{%s}" % self.to_string() From 8a44b4696b9fd70aaeb9c1384997ecf731758821 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Fri, 30 Apr 2021 17:20:10 -0400 Subject: [PATCH 16/24] TEST: make specs --- nipype/interfaces/cat12/tests/__init__.py | 0 .../cat12/tests/test_auto_CAT12Segment.py | 251 ++++++++++++++++++ ...auto_ExtractAdditionalSurfaceParameters.py | 73 +++++ ...est_auto_ExtractROIBasedSurfaceMeasures.py | 55 ++++ 4 files changed, 379 insertions(+) create mode 100644 nipype/interfaces/cat12/tests/__init__.py create mode 100644 nipype/interfaces/cat12/tests/test_auto_CAT12Segment.py create mode 100644 nipype/interfaces/cat12/tests/test_auto_ExtractAdditionalSurfaceParameters.py create mode 100644 nipype/interfaces/cat12/tests/test_auto_ExtractROIBasedSurfaceMeasures.py diff --git a/nipype/interfaces/cat12/tests/__init__.py b/nipype/interfaces/cat12/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/interfaces/cat12/tests/test_auto_CAT12Segment.py b/nipype/interfaces/cat12/tests/test_auto_CAT12Segment.py new file mode 100644 index 0000000000..67afbb573e --- /dev/null +++ b/nipype/interfaces/cat12/tests/test_auto_CAT12Segment.py @@ -0,0 +1,251 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from ..preprocess import CAT12Segment + + +def test_CAT12Segment_inputs(): + input_map = dict( + affine_preprocessing=dict( + field="extopts.APP", + usedefault=True, + ), + affine_regularization=dict( + field="opts.affreg", + usedefault=True, + ), + cobra=dict( + field="output.ROImenu.atlases.hammers", + usedefault=True, + ), + csf_output_dartel=dict( + field="output.CSF.dartel", + usedefault=True, + ), + csf_output_modulated=dict( + field="output.CSF.mod", + usedefault=True, + ), + csf_output_native=dict( + field="output.CSF.native", + usedefault=True, + ), + gm_output_dartel=dict( + field="output.GM.dartel", + usedefault=True, + ), + gm_output_modulated=dict( + field="output.GM.mod", + usedefault=True, + ), + gm_output_native=dict( + field="output.GM.native", + usedefault=True, + ), + hammers=dict( + field="output.ROImenu.atlases.cobra", + usedefault=True, + ), + ignore_errors=dict( + field="extopts.ignoreErrors", + usedefault=True, + ), + in_files=dict( + copyfile=False, + field="data", + mandatory=True, + ), + initial_segmentation=dict( + field="extopts.spm_kamap", + usedefault=True, + ), + internal_resampling_process=dict( + field="extopts.restypes.optimal", + maxlen=2, + minlen=2, + usedefault=True, + ), + jacobianwarped=dict( + field="output.jacobianwarped", + usedefault=True, + ), + label_dartel=dict( + field="output.label.dartel", + usedefault=True, + ), + label_native=dict( + field="output.label.native", + usedefault=True, + ), + label_warped=dict( + field="output.label.warped", + usedefault=True, + ), + las_dartel=dict( + field="output.las.dartel", + usedefault=True, + ), + las_native=dict( + field="output.las.native", + usedefault=True, + ), + las_warped=dict( + field="output.las.warped", + usedefault=True, + ), + local_adaptive_seg=dict( + field="extopts.LASstr", + usedefault=True, + ), + lpba40=dict( + field="output.ROImenu.atlases.lpba40", + usedefault=True, + ), + matlab_cmd=dict(), + mfile=dict( + usedefault=True, + ), + n_jobs=dict( + field="nproc", + mandatory=True, + usedefault=True, + ), + neuromorphometrics=dict( + field="output.ROImenu.atlases.neuromorphometrics", + usedefault=True, + ), + output_labelnative=dict( + field="output.labelnative", + usedefault=True, + ), + own_atlas=dict( + copyfile=False, + field="output.ROImenu.atlases.ownatlas", + mandatory=False, + ), + paths=dict(), + power_spm_inhomogeneity_correction=dict( + field="opts.biasacc", + usedefault=True, + ), + save_bias_corrected=dict( + field="output.bias.warped", + usedefault=True, + ), + skull_strip=dict( + field="extopts.gcutstr", + usedefault=True, + ), + surface_and_thickness_estimation=dict( + field="surface", + usedefault=True, + ), + surface_measures=dict( + field="output.surf_measures", + usedefault=True, + ), + tpm=dict( + copyfile=False, + field="tpm", + mandatory=False, + ), + use_mcr=dict(), + use_v8struct=dict( + min_ver="8", + usedefault=True, + ), + voxel_size=dict( + field="extopts.vox", + usedefault=True, + ), + warps=dict( + field="output.warps", + maxlen=2, + minlen=2, + usedefault=True, + ), + wm_hyper_intensity_correction=dict( + field="extopts.WMHC", + usedefault=True, + ), + wm_output_dartel=dict( + field="output.WM.dartel", + usedefault=True, + ), + wm_output_modulated=dict( + field="output.WM.mod", + usedefault=True, + ), + wm_output_native=dict( + field="output.WM.native", + usedefault=True, + ), + ) + inputs = CAT12Segment.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_CAT12Segment_outputs(): + output_map = dict( + bias_corrected_image=dict( + extensions=None, + ), + csf_dartel_image=dict( + extensions=None, + ), + csf_modulated_image=dict( + extensions=None, + ), + csf_native_image=dict( + extensions=None, + ), + gm_dartel_image=dict( + extensions=None, + ), + gm_modulated_image=dict( + extensions=None, + ), + gm_native_image=dict( + extensions=None, + ), + label_files=dict(), + label_roi=dict( + extensions=None, + ), + label_rois=dict( + extensions=None, + ), + lh_central_surface=dict( + extensions=None, + ), + lh_sphere_surface=dict( + extensions=None, + ), + mri_images=dict(), + report=dict( + extensions=None, + ), + report_files=dict(), + rh_central_surface=dict( + extensions=None, + ), + rh_sphere_surface=dict( + extensions=None, + ), + surface_files=dict(), + wm_dartel_image=dict( + extensions=None, + ), + wm_modulated_image=dict( + extensions=None, + ), + wm_native_image=dict( + extensions=None, + ), + ) + outputs = CAT12Segment.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/cat12/tests/test_auto_ExtractAdditionalSurfaceParameters.py b/nipype/interfaces/cat12/tests/test_auto_ExtractAdditionalSurfaceParameters.py new file mode 100644 index 0000000000..cde7f2057e --- /dev/null +++ b/nipype/interfaces/cat12/tests/test_auto_ExtractAdditionalSurfaceParameters.py @@ -0,0 +1,73 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from ..surface import ExtractAdditionalSurfaceParameters + + +def test_ExtractAdditionalSurfaceParameters_inputs(): + input_map = dict( + area=dict( + field="area", + usedefault=True, + ), + depth=dict( + field="SD", + usedefault=True, + ), + fractal_dimension=dict( + field="FD", + usedefault=True, + ), + gmv=dict( + field="gmv", + usedefault=True, + ), + gyrification=dict( + field="GI", + usedefault=True, + ), + left_central_surfaces=dict( + copyfile=False, + field="data_surf", + mandatory=True, + ), + matlab_cmd=dict(), + mfile=dict( + usedefault=True, + ), + paths=dict(), + surface_files=dict( + copyfile=False, + mandatory=False, + ), + use_mcr=dict(), + use_v8struct=dict( + min_ver="8", + usedefault=True, + ), + ) + inputs = ExtractAdditionalSurfaceParameters.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_ExtractAdditionalSurfaceParameters_outputs(): + output_map = dict( + lh_area=dict(), + lh_depth=dict(), + lh_extracted_files=dict(), + lh_fractaldimension=dict(), + lh_gmv=dict(), + lh_gyrification=dict(), + rh_area=dict(), + rh_depth=dict(), + rh_extracted_files=dict(), + rh_fractaldimension=dict(), + rh_gmv=dict(), + rh_gyrification=dict(), + ) + outputs = ExtractAdditionalSurfaceParameters.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/cat12/tests/test_auto_ExtractROIBasedSurfaceMeasures.py b/nipype/interfaces/cat12/tests/test_auto_ExtractROIBasedSurfaceMeasures.py new file mode 100644 index 0000000000..ffc18324aa --- /dev/null +++ b/nipype/interfaces/cat12/tests/test_auto_ExtractROIBasedSurfaceMeasures.py @@ -0,0 +1,55 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from ..surface import ExtractROIBasedSurfaceMeasures + + +def test_ExtractROIBasedSurfaceMeasures_inputs(): + input_map = dict( + lh_roi_atlas=dict( + copyfile=False, + field="rdata", + mandatory=True, + ), + lh_surface_measure=dict( + copyfile=False, + field="cdata", + mandatory=True, + ), + matlab_cmd=dict(), + mfile=dict( + usedefault=True, + ), + paths=dict(), + rh_roi_atlas=dict( + copyfile=False, + mandatory=False, + ), + rh_surface_measure=dict( + copyfile=False, + mandatory=False, + ), + surface_files=dict( + copyfile=False, + mandatory=False, + ), + use_mcr=dict(), + use_v8struct=dict( + min_ver="8", + usedefault=True, + ), + ) + inputs = ExtractROIBasedSurfaceMeasures.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_ExtractROIBasedSurfaceMeasures_outputs(): + output_map = dict( + label_files=dict(), + ) + outputs = ExtractROIBasedSurfaceMeasures.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value From e40fcb6422bdae94266d206fd5a2d9761aacd931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Sun, 30 May 2021 22:58:05 +0100 Subject: [PATCH 17/24] Correct TPM and dartel shooting template format; Improved code. --- nipype/interfaces/cat12/preprocess.py | 53 ++++++++++++++++++++------- nipype/interfaces/cat12/surface.py | 10 ++--- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/nipype/interfaces/cat12/preprocess.py b/nipype/interfaces/cat12/preprocess.py index caf337a211..4a754d974b 100644 --- a/nipype/interfaces/cat12/preprocess.py +++ b/nipype/interfaces/cat12/preprocess.py @@ -18,6 +18,8 @@ ) from nipype.utils.filemanip import split_filename, fname_presuffix +from src.interfaces.cat12.surface import Cell + class CAT12SegmentInputSpec(SPMCommandInputSpec): in_files = InputMultiPath( @@ -41,6 +43,27 @@ class CAT12SegmentInputSpec(SPMCommandInputSpec): copyfile=False, ) + _help_shoots_tpm = ( + 'Shooting Template %d. The Shooting template must be in multi-volume nifti format and should contain GM,' + ' WM, and background segmentations and have to be saved with at least 16 bit. ' + ) + + shooting_tpm = ImageFileSPM(exists=True, field="extopts.registration.shooting.shootingtpm", + desc=_help_shoots_tpm % 0, mandatory=False, copyfile=False) + + shooting_tpm_template_1 = ImageFileSPM( + exists=True, desc=_help_shoots_tpm % 1, mandatory=False, copyfile=False + ) + shooting_tpm_template_2 = ImageFileSPM( + exists=True, desc=_help_shoots_tpm % 2, mandatory=False, copyfile=False + ) + shooting_tpm_template_3 = ImageFileSPM( + exists=True, desc=_help_shoots_tpm % 3, mandatory=False, copyfile=False + ) + shooting_tpm_template_4 = ImageFileSPM( + exists=True, desc=_help_shoots_tpm % 4, mandatory=False, copyfile=False + ) + n_jobs = traits.Int( 1, usedefault=True, mandatory=True, field="nproc", desc="Number of threads" ) @@ -239,13 +262,6 @@ class CAT12SegmentInputSpec(SPMCommandInputSpec): copyfile=False, ) - _dartel_help = ( - "This option is to export data into a form that can be used with DARTEL. The SPM default is to " - "only apply rigid body transformation. However, a more appropriate option is to apply affine " - "transformation, because the additional scaling of the images requires less deformations to " - "non-linearly register brains to the template." - ) - # Grey matter gm_output_native = traits.Bool( False, @@ -495,11 +511,14 @@ def __init__(self, **inputs): def _format_arg(self, opt, spec, val): """Convert input to appropriate format for spm""" - if opt in ["in_files"]: + if opt == "in_files": if isinstance(val, list): return scans_for_fnames(val) else: return scans_for_fname(val) + elif opt in ["tpm", "shooting_tpm"]: + return Cell2Str(val) + return super(CAT12Segment, self)._format_arg(opt, spec, val) def _list_outputs(self): @@ -513,9 +532,7 @@ def _list_outputs(self): for tidx, tissue in enumerate(["gm", "wm", "csf"]): - for idx, (image, prefix) in enumerate( - [("modulated", "mw"), ("dartel", "r"), ("native", "")] - ): + for image, prefix in [("modulated", "mw"), ("dartel", "r"), ("native", "")]: outtype = f"{tissue}_output_{image}" if isdefined(getattr(self.inputs, outtype)) and getattr( self.inputs, outtype @@ -552,7 +569,7 @@ def _list_outputs(self): str(report) for report in Path(pth).glob("report/*") if report.is_file() ] - outputs[f"report"] = fname_presuffix( + outputs["report"] = fname_presuffix( f, prefix=os.path.join("report", f"cat_"), suffix=".xml", use_ext=False ) @@ -561,10 +578,18 @@ def _list_outputs(self): ] outputs["label_rois"] = fname_presuffix( - f, prefix=os.path.join("label", f"catROIs_"), suffix=".xml", use_ext=False + f, prefix=os.path.join("label", "catROIs_"), suffix=".xml", use_ext=False ) outputs["label_roi"] = fname_presuffix( - f, prefix=os.path.join("label", f"catROI_"), suffix=".xml", use_ext=False + f, prefix=os.path.join("label", "catROI_"), suffix=".xml", use_ext=False ) return outputs + + +class Cell2Str(Cell): + + def __str__(self): + """Convert input to appropriate format for cat12 + """ + return "{'%s'}" % self.to_string() diff --git a/nipype/interfaces/cat12/surface.py b/nipype/interfaces/cat12/surface.py index 39925e6f29..69afc4e423 100644 --- a/nipype/interfaces/cat12/surface.py +++ b/nipype/interfaces/cat12/surface.py @@ -1,9 +1,7 @@ import os from pathlib import Path -from traits.trait_base import _Undefined - -from nipype.interfaces.base import File, InputMultiPath, TraitedSpec, traits +from nipype.interfaces.base import File, InputMultiPath, TraitedSpec, traits, isdefined from nipype.interfaces.spm import SPMCommand from nipype.interfaces.spm.base import SPMCommandInputSpec from nipype.utils.filemanip import split_filename @@ -137,14 +135,14 @@ def _list_outputs(self): pth, base, ext = split_filename(filename) # The first part of the filename is rh.central or lh.central original_filename = base.split(".", 2)[-1] - for i, (extracted_parameter, parameter_name) in enumerate(names_outputs): + for extracted_parameter, parameter_name in names_outputs: if extracted_parameter: for hemisphere in ["rh", "lh"]: all_files_hemisphere = hemisphere + "_extracted_files" name_hemisphere = hemisphere + "_" + parameter_name - if isinstance(outputs[name_hemisphere], _Undefined): + if not isdefined(outputs[name_hemisphere]): outputs[name_hemisphere] = [] - if isinstance(outputs[all_files_hemisphere], _Undefined): + if not isdefined(outputs[all_files_hemisphere]): outputs[all_files_hemisphere] = [] generated_filename = ".".join( [hemisphere, parameter_name, original_filename] From 920b87967958d7050fa4de79bf0862624659210c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Sun, 30 May 2021 23:48:01 +0100 Subject: [PATCH 18/24] Added format utils. --- nipype/interfaces/cat12/format_utils.py | 15 +++++++++++++++ nipype/interfaces/cat12/preprocess.py | 4 ++-- nipype/interfaces/cat12/surface.py | 18 +----------------- 3 files changed, 18 insertions(+), 19 deletions(-) create mode 100644 nipype/interfaces/cat12/format_utils.py diff --git a/nipype/interfaces/cat12/format_utils.py b/nipype/interfaces/cat12/format_utils.py new file mode 100644 index 0000000000..03091f1fb9 --- /dev/null +++ b/nipype/interfaces/cat12/format_utils.py @@ -0,0 +1,15 @@ +class Cell: + def __init__(self, arg): + self.arg = arg + + def to_string(self): + if isinstance(self.arg, list): + v = "\n".join([f"'{el}'" for el in self.arg]) + else: + v = self.arg + return v + + +class NestedCell(Cell): + def __str__(self): + return "{{%s}}" % self.to_string() diff --git a/nipype/interfaces/cat12/preprocess.py b/nipype/interfaces/cat12/preprocess.py index 4a754d974b..5ee458af0a 100644 --- a/nipype/interfaces/cat12/preprocess.py +++ b/nipype/interfaces/cat12/preprocess.py @@ -9,6 +9,8 @@ File, Str, ) +from nipype.interfaces.cat12.format_utils import Cell + from nipype.interfaces.spm import SPMCommand from nipype.interfaces.spm.base import ( SPMCommandInputSpec, @@ -18,8 +20,6 @@ ) from nipype.utils.filemanip import split_filename, fname_presuffix -from src.interfaces.cat12.surface import Cell - class CAT12SegmentInputSpec(SPMCommandInputSpec): in_files = InputMultiPath( diff --git a/nipype/interfaces/cat12/surface.py b/nipype/interfaces/cat12/surface.py index 69afc4e423..810b2da7e7 100644 --- a/nipype/interfaces/cat12/surface.py +++ b/nipype/interfaces/cat12/surface.py @@ -2,6 +2,7 @@ from pathlib import Path from nipype.interfaces.base import File, InputMultiPath, TraitedSpec, traits, isdefined +from nipype.interfaces.cat12.format_utils import NestedCell, Cell from nipype.interfaces.spm import SPMCommand from nipype.interfaces.spm.base import SPMCommandInputSpec from nipype.utils.filemanip import split_filename @@ -269,23 +270,6 @@ def _list_outputs(self): return outputs -class Cell: - def __init__(self, arg): - self.arg = arg - - def to_string(self): - if isinstance(self.arg, list): - v = "\n".join([f"'{el}'" for el in self.arg]) - else: - v = self.arg - return v - - -class NestedCell(Cell): - def __str__(self): - return "{{%s}}" % self.to_string() - - class Cell2Str(Cell): def __str__(self): """Convert input to appropriate format for cat12""" From 56a02f284caf58ceda3edc83b8aab5a2b20562f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Mon, 31 May 2021 13:57:03 +0100 Subject: [PATCH 19/24] Reformatted preprocess.py. --- nipype/interfaces/cat12/preprocess.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/nipype/interfaces/cat12/preprocess.py b/nipype/interfaces/cat12/preprocess.py index 5ee458af0a..61b5785f48 100644 --- a/nipype/interfaces/cat12/preprocess.py +++ b/nipype/interfaces/cat12/preprocess.py @@ -44,12 +44,17 @@ class CAT12SegmentInputSpec(SPMCommandInputSpec): ) _help_shoots_tpm = ( - 'Shooting Template %d. The Shooting template must be in multi-volume nifti format and should contain GM,' - ' WM, and background segmentations and have to be saved with at least 16 bit. ' + "Shooting Template %d. The Shooting template must be in multi-volume nifti format and should contain GM," + " WM, and background segmentations and have to be saved with at least 16 bit. " ) - shooting_tpm = ImageFileSPM(exists=True, field="extopts.registration.shooting.shootingtpm", - desc=_help_shoots_tpm % 0, mandatory=False, copyfile=False) + shooting_tpm = ImageFileSPM( + exists=True, + field="extopts.registration.shooting.shootingtpm", + desc=_help_shoots_tpm % 0, + mandatory=False, + copyfile=False, + ) shooting_tpm_template_1 = ImageFileSPM( exists=True, desc=_help_shoots_tpm % 1, mandatory=False, copyfile=False @@ -588,8 +593,6 @@ def _list_outputs(self): class Cell2Str(Cell): - def __str__(self): - """Convert input to appropriate format for cat12 - """ + """Convert input to appropriate format for cat12""" return "{'%s'}" % self.to_string() From 8a0ccae5674c01a4cb5efce6245ccc0578f416d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Mon, 31 May 2021 16:29:11 +0100 Subject: [PATCH 20/24] Updated test_auto_CAT12Segment.py. --- .../cat12/tests/test_auto_CAT12Segment.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/nipype/interfaces/cat12/tests/test_auto_CAT12Segment.py b/nipype/interfaces/cat12/tests/test_auto_CAT12Segment.py index 67afbb573e..979b3afa6b 100644 --- a/nipype/interfaces/cat12/tests/test_auto_CAT12Segment.py +++ b/nipype/interfaces/cat12/tests/test_auto_CAT12Segment.py @@ -130,6 +130,32 @@ def test_CAT12Segment_inputs(): field="output.bias.warped", usedefault=True, ), + shooting_tpm=dict( + copyfile=False, + extensions=[".hdr", ".img", ".img.gz", ".nii"], + field="extopts.registration.shooting.shootingtpm", + mandatory=False, + ), + shooting_tpm_template_1=dict( + copyfile=False, + extensions=[".hdr", ".img", ".img.gz", ".nii"], + mandatory=False, + ), + shooting_tpm_template_2=dict( + copyfile=False, + extensions=[".hdr", ".img", ".img.gz", ".nii"], + mandatory=False, + ), + shooting_tpm_template_3=dict( + copyfile=False, + extensions=[".hdr", ".img", ".img.gz", ".nii"], + mandatory=False, + ), + shooting_tpm_template_4=dict( + copyfile=False, + extensions=[".hdr", ".img", ".img.gz", ".nii"], + mandatory=False, + ), skull_strip=dict( field="extopts.gcutstr", usedefault=True, From cdcdba9eccc11ed674cfb9e9ca12eac86858e7f9 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Mon, 31 May 2021 17:13:05 -0400 Subject: [PATCH 21/24] DOC: Add CAT12 to interfaces list --- doc/interfaces.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/interfaces.rst b/doc/interfaces.rst index d04fd88271..dc83450b5a 100644 --- a/doc/interfaces.rst +++ b/doc/interfaces.rst @@ -54,6 +54,9 @@ Nipype provides interfaces for the following **third-party** tools: is an open-source software toolkit for diffusion MRI processing. * `Camino-TrackVis `__ allows interoperability between Camino and TrackVis. + * `CAT12 `__ + (Computational Anatomy Toolbox) extends SPM12 to provide computational + anatomy. * `Connectome Mapper (CMP) `__ implements a full processing pipeline for creating multi-variate and multi-resolution connectomes with dMRI data. From 20629a09e620c6af801efd2db7faaeee9ed2cfe4 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Mon, 31 May 2021 21:38:22 -0400 Subject: [PATCH 22/24] RF: Move format_utils to base --- nipype/interfaces/cat12/{format_utils.py => base.py} | 0 nipype/interfaces/cat12/preprocess.py | 2 +- nipype/interfaces/cat12/surface.py | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename nipype/interfaces/cat12/{format_utils.py => base.py} (100%) diff --git a/nipype/interfaces/cat12/format_utils.py b/nipype/interfaces/cat12/base.py similarity index 100% rename from nipype/interfaces/cat12/format_utils.py rename to nipype/interfaces/cat12/base.py diff --git a/nipype/interfaces/cat12/preprocess.py b/nipype/interfaces/cat12/preprocess.py index 61b5785f48..9c02701542 100644 --- a/nipype/interfaces/cat12/preprocess.py +++ b/nipype/interfaces/cat12/preprocess.py @@ -9,7 +9,7 @@ File, Str, ) -from nipype.interfaces.cat12.format_utils import Cell +from nipype.interfaces.cat12.base import Cell from nipype.interfaces.spm import SPMCommand from nipype.interfaces.spm.base import ( diff --git a/nipype/interfaces/cat12/surface.py b/nipype/interfaces/cat12/surface.py index 810b2da7e7..83307ebee6 100644 --- a/nipype/interfaces/cat12/surface.py +++ b/nipype/interfaces/cat12/surface.py @@ -2,7 +2,7 @@ from pathlib import Path from nipype.interfaces.base import File, InputMultiPath, TraitedSpec, traits, isdefined -from nipype.interfaces.cat12.format_utils import NestedCell, Cell +from nipype.interfaces.cat12.base import NestedCell, Cell from nipype.interfaces.spm import SPMCommand from nipype.interfaces.spm.base import SPMCommandInputSpec from nipype.utils.filemanip import split_filename From fc4f82daa7e092cf5faad0bd1597c1c2e2591dfc Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Mon, 31 May 2021 21:38:40 -0400 Subject: [PATCH 23/24] ENH: Add cat12 interfaces to __init__ --- nipype/interfaces/cat12/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nipype/interfaces/cat12/__init__.py b/nipype/interfaces/cat12/__init__.py index e69de29bb2..99498dc922 100644 --- a/nipype/interfaces/cat12/__init__.py +++ b/nipype/interfaces/cat12/__init__.py @@ -0,0 +1,5 @@ +from .preprocess import CAT12Segment +from .surface import ( + ExtractAdditionalSurfaceParameters, + ExtractROIBasedSurfaceMeasures, +) From 16f62fc0d782655c85335d205a3fcde9fb8871c1 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Mon, 31 May 2021 21:41:18 -0400 Subject: [PATCH 24/24] DOC: Minor cleanups --- nipype/interfaces/cat12/preprocess.py | 1 + nipype/interfaces/cat12/surface.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/nipype/interfaces/cat12/preprocess.py b/nipype/interfaces/cat12/preprocess.py index 9c02701542..5a73f42443 100644 --- a/nipype/interfaces/cat12/preprocess.py +++ b/nipype/interfaces/cat12/preprocess.py @@ -475,6 +475,7 @@ class CAT12SegmentOutputSpec(TraitedSpec): class CAT12Segment(SPMCommand): """ CAT12: Segmentation + This toolbox is an extension to the default segmentation in SPM12, but uses a completely different segmentation approach. The segmentation approach is based on an Adaptive Maximum A Posterior (MAP) technique without the need for a priori diff --git a/nipype/interfaces/cat12/surface.py b/nipype/interfaces/cat12/surface.py index 83307ebee6..41f8a5f680 100644 --- a/nipype/interfaces/cat12/surface.py +++ b/nipype/interfaces/cat12/surface.py @@ -217,7 +217,7 @@ class ExtractROIBasedSurfaceMeasuresOutputSpec(TraitedSpec): class ExtractROIBasedSurfaceMeasures(SPMCommand): """ Extract ROI-based surface values - While ROI-based values for VBM (volume) data are automatically saved in the label folder as XML file it is + While ROI-based values for VBM (volume) data are automatically saved in the ``label`` folder as XML file it is necessary to additionally extract these values for surface data (except for thickness which is automatically extracted during segmentation). This has to be done after preprocessing the data and creating cortical surfaces.