diff --git a/pydra/tasks/freesurfer/v8/__init__.py b/pydra/tasks/freesurfer/v8/__init__.py index bab42270..f4d5a17d 100644 --- a/pydra/tasks/freesurfer/v8/__init__.py +++ b/pydra/tasks/freesurfer/v8/__init__.py @@ -19,7 +19,6 @@ _cifs_table, _generate_cifs_table, _parse_mount_table, - config, copyfile, ensure_list, fmlogger, diff --git a/pydra/tasks/freesurfer/v8/model/concatenate.py b/pydra/tasks/freesurfer/v8/model/concatenate.py index a861a897..46687a92 100644 --- a/pydra/tasks/freesurfer/v8/model/concatenate.py +++ b/pydra/tasks/freesurfer/v8/model/concatenate.py @@ -75,7 +75,7 @@ class Concatenate(shell.Task["Concatenate.Outputs"]): help="Multiply input by an ascii matrix in file", argstr="--mtx {multiply_matrix_file}", ) - combine: bool = shell.arg( + combine_: bool = shell.arg( help="Combine non-zero values into single frame volume", argstr="--combine" ) keep_dtype: bool = shell.arg( diff --git a/pydra/tasks/freesurfer/v8/model/glm_fit.py b/pydra/tasks/freesurfer/v8/model/glm_fit.py index 4ae473e1..7153c936 100644 --- a/pydra/tasks/freesurfer/v8/model/glm_fit.py +++ b/pydra/tasks/freesurfer/v8/model/glm_fit.py @@ -234,8 +234,7 @@ def glm_dir_default(inputs): ["design", "fsgd", "one_sample"], ["fixed_fx_dof", "fixed_fx_dof_file"], ["nii", "nii_gz"], - ["no_prune", "prunethresh"], - ["noprune", "prune_thresh"], + ["no_prune", "prune_thresh"], ["weight_file", "weight_inv", "weight_sqrt", "weighted_ls"], ["weight_file", "weighted_ls"], ["weight_inv", "weighted_ls"], diff --git a/pydra/tasks/freesurfer/v8/model/label_2_vol.py b/pydra/tasks/freesurfer/v8/model/label_2_vol.py index c304168d..fe7cc3c5 100644 --- a/pydra/tasks/freesurfer/v8/model/label_2_vol.py +++ b/pydra/tasks/freesurfer/v8/model/label_2_vol.py @@ -68,7 +68,7 @@ class Label2Vol(shell.Task["Label2Vol.Outputs"]): annot_file: File | None = shell.arg( help="surface annotation file", argstr="--annot {annot_file}", - requires=("subject_id", "hemi"), + requires=["subject_id", "hemi"], ) seg_file: File | None = shell.arg( help="segmentation file", argstr="--seg {seg_file}" @@ -99,7 +99,7 @@ class Label2Vol(shell.Task["Label2Vol.Outputs"]): proj: ty.Any = shell.arg( help="project along surface normal", argstr="--proj {proj[0]} {proj[1]} {proj[2]} {proj[3]}", - requires=("subject_id", "hemi"), + requires=["subject_id", "hemi"], ) subject_id: str = shell.arg(help="subject id", argstr="--subject {subject_id}") hemi: ty.Any = shell.arg(help="hemisphere to use lh or rh", argstr="--hemi {hemi}") diff --git a/pydra/tasks/freesurfer/v8/model/mris_preproc_recon_all.py b/pydra/tasks/freesurfer/v8/model/mris_preproc_recon_all.py index 40331ac9..66d079d0 100644 --- a/pydra/tasks/freesurfer/v8/model/mris_preproc_recon_all.py +++ b/pydra/tasks/freesurfer/v8/model/mris_preproc_recon_all.py @@ -93,10 +93,10 @@ class MRISPreprocReconAll(shell.Task["MRISPreprocReconAll.Outputs"]): requires=["lh_surfreg_target", "rh_surfreg_target"], formatter="surfreg_files_formatter", ) - lh_surfreg_target: File = shell.arg( + lh_surfreg_target: File | None = shell.arg( help="Implicit target surface registration file", requires=["surfreg_files"] ) - rh_surfreg_target: File = shell.arg( + rh_surfreg_target: File | None = shell.arg( help="Implicit target surface registration file", requires=["surfreg_files"] ) subject_id: ty.Any | None = shell.arg( diff --git a/pydra/tasks/freesurfer/v8/model/one_sample_t_test.py b/pydra/tasks/freesurfer/v8/model/one_sample_t_test.py index 41f97d1c..7a6fff0f 100644 --- a/pydra/tasks/freesurfer/v8/model/one_sample_t_test.py +++ b/pydra/tasks/freesurfer/v8/model/one_sample_t_test.py @@ -233,8 +233,7 @@ def glm_dir_default(inputs): ["design", "fsgd", "one_sample"], ["fixed_fx_dof", "fixed_fx_dof_file"], ["nii", "nii_gz"], - ["no_prune", "prunethresh"], - ["noprune", "prune_thresh"], + ["no_prune", "prune_thresh"], ["weight_file", "weight_inv", "weight_sqrt", "weighted_ls"], ["weight_file", "weighted_ls"], ["weight_inv", "weighted_ls"], diff --git a/pydra/tasks/freesurfer/v8/nipype_ports/__init__.py b/pydra/tasks/freesurfer/v8/nipype_ports/__init__.py index e1c6e97b..8ceafd36 100644 --- a/pydra/tasks/freesurfer/v8/nipype_ports/__init__.py +++ b/pydra/tasks/freesurfer/v8/nipype_ports/__init__.py @@ -28,6 +28,3 @@ logger = logging.getLogger(__name__) - - -config = NipypeConfig() diff --git a/pydra/tasks/freesurfer/v8/nipype_ports/interfaces/io.py b/pydra/tasks/freesurfer/v8/nipype_ports/interfaces/io.py index 27f27366..6ea4b9e8 100644 --- a/pydra/tasks/freesurfer/v8/nipype_ports/interfaces/io.py +++ b/pydra/tasks/freesurfer/v8/nipype_ports/interfaces/io.py @@ -5,12 +5,60 @@ simplify_list, ) import os +from pydra.compose import python +from pydra.utils.typing import MultiOutputFile +from fileformats.generic import Directory, File logger = logging.getLogger(__name__) -class FreeSurferSource(IOBase): +def _get_files(inputs, path, key, dirval, altkey=None): + + globsuffix = "" + if dirval == "mri": + globsuffix = ".mgz" + elif dirval == "stats": + globsuffix = ".stats" + globprefix = "" + if dirval in ("surf", "label", "stats"): + if inputs["hemi"] != "both": + globprefix: inputs = python.arg["hemi"] + "." + else: + globprefix = "?h." + if key in ("aseg_stats", "wmparc_stats"): + globprefix = "" + elif key == "ribbon": + if inputs["hemi"] != "both": + globprefix: inputs = python.arg["hemi"] + "." + else: + globprefix = "*" + keys = ensure_list(altkey) if altkey else [key] + globfmt = os.path.join(path, dirval, f"{globprefix}{{}}{globsuffix}") + return [os.path.abspath(f) for key in keys for f in glob.glob(globfmt.format(key))] + + +def _list_outputs(inputs): + + subjects_dir = inputs["subjects_dir"] + subject_path = os.path.join(subjects_dir, inputs["subject_id"]) + output_traits = {} + outputs = output_traits.get() + for k in list(outputs.keys()): + val: _get_files = python.arg( + inputs, + subject_path, + k, + output_traits.traits()[k].loc, + output_traits.traits()[k].altkey, + ) + if val: + outputs[k] = simplify_list(val) + return outputs + + +@python.define +class FreeSurferSource(python.Task["FreeSurferSource.Outputs"]): """Generates freesurfer subject info from their directories. Examples @@ -26,50 +74,179 @@ class FreeSurferSource(IOBase): """ - input_spec = FSSourceInputSpec - output_spec = FSSourceOutputSpec - _always_run = True - _additional_metadata = ["loc", "altkey"] - - def _get_files(self, path, key, dirval, altkey=None): - - globsuffix = "" - if dirval == "mri": - globsuffix = ".mgz" - elif dirval == "stats": - globsuffix = ".stats" - globprefix = "" - if dirval in ("surf", "label", "stats"): - if self.inputs.hemi != "both": - globprefix = self.inputs.hemi + "." - else: - globprefix = "?h." - if key in ("aseg_stats", "wmparc_stats"): - globprefix = "" - elif key == "ribbon": - if self.inputs.hemi != "both": - globprefix = self.inputs.hemi + "." - else: - globprefix = "*" - keys = ensure_list(altkey) if altkey else [key] - globfmt = os.path.join(path, dirval, f"{globprefix}{{}}{globsuffix}") - return [ - os.path.abspath(f) for key in keys for f in glob.glob(globfmt.format(key)) - ] - - def _list_outputs(self): - - subjects_dir = self.inputs.subjects_dir - subject_path = os.path.join(subjects_dir, self.inputs.subject_id) - output_traits = self._outputs() - outputs = output_traits.get() - for k in list(outputs.keys()): - val = self._get_files( - subject_path, - k, - output_traits.traits()[k].loc, - output_traits.traits()[k].altkey, - ) - if val: - outputs[k] = simplify_list(val) - return outputs + subjects_dir: Directory = python.arg(help="Freesurfer subjects directory.") + subject_id: str = python.arg(help="Subject name for whom to retrieve data") + hemi: str = python.arg( + allowed_values=["both", "lh", "rh"], help="Selects hemisphere specific outputs" + ) + + class Outputs(python.Outputs): + T1: File = python.arg( + help="Intensity normalized whole-head volume", + # loc="mri" + ) + aseg: File = python.arg( + # loc=(.*) + help="Volumetric map of regions from automatic segmentation", + ) + brain: File = python.arg( + help="Intensity normalized brain-only volume", + # loc="mri" + ) + brainmask: File = python.arg( + help="Skull-stripped (brain-only) volume", + # loc="mri" + ) + filled: File = python.arg( + help="Subcortical mass volume", + # loc="mri" + ) + norm: File = python.arg( + help="Normalized skull-stripped volume", + # loc="mri" + ) + nu: File = python.arg( + help="Non-uniformity corrected whole-head volume", + # loc="mri" + ) + orig: File = python.arg( + help="Base image conformed to Freesurfer space", + # loc="mri" + ) + rawavg: File = python.arg( + help="Volume formed by averaging input images", + # loc="mri" + ) + ribbon: MultiOutputFile = python.arg( + help="Volumetric maps of cortical ribbons", + # loc=(.*) + # altkey="*ribbon", + ) + wm: File = python.arg( + help="Segmented white-matter volume", + # loc="mri" + ) + wmparc: File = python.arg( + # loc=(.*) + help="Aparc parcellation projected into subcortical white matter", + ) + curv: MultiOutputFile = python.arg( + help="Maps of surface curvature", + # loc="surf" + ) + avg_curv: MultiOutputFile = python.arg( + help="Average atlas curvature, sampled to subject", + # loc=(.*) + ) + inflated: MultiOutputFile = python.arg( + help="Inflated surface meshes", + # loc="surf" + ) + pial: MultiOutputFile = python.arg( + help="Gray matter/pia matter surface meshes", + # loc="surf" + ) + area_pial: MultiOutputFile = python.arg( + help="Mean area of triangles each vertex on the pial surface is " + "associated with", + # loc=(.*) + # altkey="area.pial", + ) + curv_pial: MultiOutputFile = python.arg( + help="Curvature of pial surface", + # loc=(.*) + # altkey="curv.pial", + ) + smoothwm: MultiOutputFile = python.arg( + # loc=(.*) + ) + sphere: MultiOutputFile = python.arg( + help="Spherical surface meshes", + # loc="surf" + ) + sulc: MultiOutputFile = python.arg( + help="Surface maps of sulcal depth", + # loc="surf" + ) + thickness: MultiOutputFile = python.arg( + # loc=(.*) + ) + volume: MultiOutputFile = python.arg( + help="Surface maps of cortical volume", + # loc="surf" + ) + white: MultiOutputFile = python.arg( + help="White/gray matter surface meshes", + # loc="surf" + ) + jacobian_white: MultiOutputFile = python.arg( + help="Distortion required to register to spherical atlas", + # loc=(.*) + ) + graymid: MultiOutputFile = python.arg( + help="Graymid/midthickness surface meshes", + # loc=(.*) + # altkey=["graymid", "midthickness"], + ) + label: MultiOutputFile = python.arg( + help="Volume and surface label files", + # loc=(.*) + # altkey="*label", + ) + annot: MultiOutputFile = python.arg( + help="Surface annotation files", + # loc=(.*) + # altkey="*annot", + ) + aparc_aseg: MultiOutputFile = python.arg( + # loc=(.*) + # altkey="aparc*aseg", + help="Aparc parcellation projected into aseg volume", + ) + sphere_reg: MultiOutputFile = python.arg( + # loc=(.*) + # altkey="sphere.reg", + help="Spherical registration file", + ) + aseg_stats: MultiOutputFile = python.arg( + # loc=(.*) + # altkey="aseg", + help="Automated segmentation statistics file", + ) + wmparc_stats: MultiOutputFile = python.arg( + # loc=(.*) + # altkey="wmparc", + help="White matter parcellation statistics file", + ) + aparc_stats: MultiOutputFile = python.arg( + # loc=(.*) + # altkey="aparc", + help="Aparc parcellation statistics files", + ) + BA_stats: MultiOutputFile = python.arg( + # loc=(.*) + # altkey="BA", + help="Brodmann Area statistics files", + ) + aparc_a2009s_stats: MultiOutputFile = python.arg( + # loc=(.*) + # altkey="aparc.a2009s", + help="Aparc a2009s parcellation statistics files", + ) + curv_stats: MultiOutputFile = python.arg( + # loc=(.*) + # altkey="curv", + help="Curvature statistics files", + ) + entorhinal_exvivo_stats: MultiOutputFile = python.arg( + # loc=(.*) + # altkey="entorhinal_exvivo", + help="Entorhinal exvivo statistics files", + ) + + @staticmethod + def function(): + raise NotImplementedError( + "FreeSurferSource does not implement a function, " + "it is used to generate outputs from the subject directory." + ) diff --git a/pydra/tasks/freesurfer/v8/nipype_ports/utils/filemanip.py b/pydra/tasks/freesurfer/v8/nipype_ports/utils/filemanip.py index c99739a3..77113d79 100644 --- a/pydra/tasks/freesurfer/v8/nipype_ports/utils/filemanip.py +++ b/pydra/tasks/freesurfer/v8/nipype_ports/utils/filemanip.py @@ -2,7 +2,6 @@ from hashlib import md5 import hashlib import logging -from .. import config from pydra.tasks.freesurfer.v8.nipype_ports.utils.misc import is_container import os import os.path as op diff --git a/pydra/tasks/freesurfer/v8/petsurfer/logan.py b/pydra/tasks/freesurfer/v8/petsurfer/logan.py index df5b1d2f..66f259cc 100644 --- a/pydra/tasks/freesurfer/v8/petsurfer/logan.py +++ b/pydra/tasks/freesurfer/v8/petsurfer/logan.py @@ -234,8 +234,7 @@ def glm_dir_default(inputs): ["design", "fsgd", "one_sample"], ["fixed_fx_dof", "fixed_fx_dof_file"], ["nii", "nii_gz"], - ["no_prune", "prunethresh"], - ["noprune", "prune_thresh"], + ["no_prune", "prune_thresh"], ["weight_file", "weight_inv", "weight_sqrt", "weighted_ls"], ["weight_file", "weighted_ls"], ["weight_inv", "weighted_ls"], diff --git a/pydra/tasks/freesurfer/v8/petsurfer/mrtm1.py b/pydra/tasks/freesurfer/v8/petsurfer/mrtm1.py index c1c531b7..f4ac3e11 100644 --- a/pydra/tasks/freesurfer/v8/petsurfer/mrtm1.py +++ b/pydra/tasks/freesurfer/v8/petsurfer/mrtm1.py @@ -234,8 +234,7 @@ def glm_dir_default(inputs): ["design", "fsgd", "one_sample"], ["fixed_fx_dof", "fixed_fx_dof_file"], ["nii", "nii_gz"], - ["no_prune", "prunethresh"], - ["noprune", "prune_thresh"], + ["no_prune", "prune_thresh"], ["weight_file", "weight_inv", "weight_sqrt", "weighted_ls"], ["weight_file", "weighted_ls"], ["weight_inv", "weighted_ls"], diff --git a/pydra/tasks/freesurfer/v8/petsurfer/mrtm2.py b/pydra/tasks/freesurfer/v8/petsurfer/mrtm2.py index 7262a5f9..73055584 100644 --- a/pydra/tasks/freesurfer/v8/petsurfer/mrtm2.py +++ b/pydra/tasks/freesurfer/v8/petsurfer/mrtm2.py @@ -234,8 +234,7 @@ def glm_dir_default(inputs): ["design", "fsgd", "one_sample"], ["fixed_fx_dof", "fixed_fx_dof_file"], ["nii", "nii_gz"], - ["no_prune", "prunethresh"], - ["noprune", "prune_thresh"], + ["no_prune", "prune_thresh"], ["weight_file", "weight_inv", "weight_sqrt", "weighted_ls"], ["weight_file", "weighted_ls"], ["weight_inv", "weighted_ls"], diff --git a/pydra/tasks/freesurfer/v8/preprocess/concatenate_lta.py b/pydra/tasks/freesurfer/v8/preprocess/concatenate_lta.py index 47a1c848..15b4d790 100644 --- a/pydra/tasks/freesurfer/v8/preprocess/concatenate_lta.py +++ b/pydra/tasks/freesurfer/v8/preprocess/concatenate_lta.py @@ -84,13 +84,13 @@ class ConcatenateLTA(shell.Task["ConcatenateLTA.Outputs"]): out_type: ty.Any = shell.arg( help="set final LTA type", formatter="out_type_formatter" ) - tal_source_file: File = shell.arg( + tal_source_file: File | None = shell.arg( help="if in_lta2 is talairach.xfm, specify source for talairach", argstr="-tal {tal_source_file}", position=-5, requires=["tal_template_file"], ) - tal_template_file: File = shell.arg( + tal_template_file: File | None = shell.arg( help="if in_lta2 is talairach.xfm, specify template for talairach", argstr="{tal_template_file}", position=-4, diff --git a/pydra/tasks/freesurfer/v8/preprocess/mri_convert.py b/pydra/tasks/freesurfer/v8/preprocess/mri_convert.py index c244aa61..3eb4d4e2 100644 --- a/pydra/tasks/freesurfer/v8/preprocess/mri_convert.py +++ b/pydra/tasks/freesurfer/v8/preprocess/mri_convert.py @@ -242,7 +242,7 @@ class MRIConvert(shell.Task["MRIConvert.Outputs"]): template_type: ty.Any = shell.arg( help="template file type", argstr="--template_type {template_type}" ) - split: bool = shell.arg( + split_: bool = shell.arg( help="split output frames into separate output files.", argstr="--split" ) frame: int = shell.arg( diff --git a/pydra/tasks/freesurfer/v8/preprocess/recon_all.py b/pydra/tasks/freesurfer/v8/preprocess/recon_all.py index 90e5eb43..57ba13f0 100644 --- a/pydra/tasks/freesurfer/v8/preprocess/recon_all.py +++ b/pydra/tasks/freesurfer/v8/preprocess/recon_all.py @@ -1,7 +1,7 @@ import attrs from fileformats.generic import File import logging -from pydra.tasks.io.v8 import FreeSurferSource +from pydra.tasks.freesurfer.v8.nipype_ports.interfaces.io import FreeSurferSource import os from pydra.compose import shell from pydra.utils.typing import MultiInputObj @@ -529,12 +529,12 @@ class ReconAll(shell.Task["ReconAll.Outputs"]): requires=["subject_id"], formatter="T1_files_formatter", ) - T2_file: File = shell.arg( + T2_file: File | None = shell.arg( help="Convert T2 image to orig directory", argstr="-T2 {T2_file}", requires=["subject_id"], ) - FLAIR_file: File = shell.arg( + FLAIR_file: File | None = shell.arg( help="Convert FLAIR image to orig directory", argstr="-FLAIR {FLAIR_file}", requires=["subject_id"], diff --git a/pydra/tasks/freesurfer/v8/utils/make_surfaces.py b/pydra/tasks/freesurfer/v8/utils/make_surfaces.py index 7dd66e38..c5a85f85 100644 --- a/pydra/tasks/freesurfer/v8/utils/make_surfaces.py +++ b/pydra/tasks/freesurfer/v8/utils/make_surfaces.py @@ -174,7 +174,7 @@ class MakeSurfaces(shell.Task["MakeSurfaces.Outputs"]): orig_white: File = shell.arg( help="Specify a white surface to start with", argstr="-orig_white {orig_white}" ) - orig_pial: File = shell.arg( + orig_pial: File | None = shell.arg( help="Specify a pial surface to start with", argstr="-orig_pial {orig_pial}", requires=["in_label"], diff --git a/pydra/tasks/freesurfer/v8/utils/parcellation_stats.py b/pydra/tasks/freesurfer/v8/utils/parcellation_stats.py index b93bd8cd..7d786bf2 100644 --- a/pydra/tasks/freesurfer/v8/utils/parcellation_stats.py +++ b/pydra/tasks/freesurfer/v8/utils/parcellation_stats.py @@ -39,7 +39,7 @@ def out_table_default(inputs): @shell.define( xor=[ ["in_annotation", "in_label"], - ["in_annotatoin", "in_label", "out_color"], + ["in_annotation", "in_label", "out_color"], ["in_label", "out_color"], ] ) @@ -131,13 +131,13 @@ class ParcellationStats(shell.Task["ParcellationStats.Outputs"]): subjects_dir: Directory = shell.arg(help="subjects directory") class Outputs(shell.Outputs): - out_table: Path = shell.outarg( + out_table: File | None = shell.outarg( help="Table output to tablefile", argstr="-f {out_table}", requires=["tabular_output"], path_template="out_table", ) - out_color: Path | None = shell.outarg( + out_color: File | None = shell.outarg( help="Output annotation files's colortable to text file", argstr="-c {out_color}", path_template='"test.ctab"', diff --git a/pydra/tasks/freesurfer/v8/utils/surface_snapshots.py b/pydra/tasks/freesurfer/v8/utils/surface_snapshots.py index 56fda878..bc4d2618 100644 --- a/pydra/tasks/freesurfer/v8/utils/surface_snapshots.py +++ b/pydra/tasks/freesurfer/v8/utils/surface_snapshots.py @@ -136,7 +136,7 @@ class SurfaceSnapshots(shell.Task["SurfaceSnapshots.Outputs"]): ) show_curv: bool = shell.arg(help="show curvature", argstr="-curv") show_gray_curv: bool = shell.arg(help="show curvature in gray", argstr="-gray") - overlay: File = shell.arg( + overlay: File | None = shell.arg( help="load an overlay volume/surface", argstr="-overlay {overlay}", requires=["overlay_range"], diff --git a/pydra/tasks/freesurfer/v8/utils/tkregister_2.py b/pydra/tasks/freesurfer/v8/utils/tkregister_2.py index 73d54acf..57b52cff 100644 --- a/pydra/tasks/freesurfer/v8/utils/tkregister_2.py +++ b/pydra/tasks/freesurfer/v8/utils/tkregister_2.py @@ -131,7 +131,7 @@ class Tkregister2(shell.Task["Tkregister2.Outputs"]): help="target volume", argstr="--targ {target_image}" ) fstarg: bool = shell.arg(help="use subject's T1 as reference", argstr="--fstarg") - moving_image: Nifti1 | MghGz = shell.arg( + moving_image: Nifti1 | MghGz | None = shell.arg( help="moving volume", argstr="--mov {moving_image}" ) fsl_in_matrix: File = shell.arg(