diff --git a/pydra/tasks/fastsurfer/v2_2/__init__.py b/pydra/tasks/fastsurfer/v2_2/__init__.py index 8422b59..992977c 100644 --- a/pydra/tasks/fastsurfer/v2_2/__init__.py +++ b/pydra/tasks/fastsurfer/v2_2/__init__.py @@ -1,215 +1,10 @@ -from pydra.engine import specs -from pydra.engine import ShellCommandTask +from pydra.compose import shell import typing as ty from pathlib import Path from fileformats.generic import File, Directory from fileformats.medimage import MghGz -input_fields = [ - ( - "subjects_dir", - Path, - { - "help_string": "Subjects directory", - "argstr": "--sd {subjects_dir}", - "output_file_template": "subjects_dir", - }, - ), - ( - "subject_id", - ty.Any, - { - "help_string": "Subject ID", - "argstr": "--sid {subject_id}", - "mandatory": True, - }, - ), - ( - "T1_files", - File, - { - "help_string": "T1 full head input (not bias corrected, global path)", - "argstr": "--t1 {T1_files}", - "mandatory": False, - }, - ), - ( - "fs_license", - File, - { - "help_string": "Path to FreeSurfer license key file.", - "argstr": "--fs_license {fs_license}", - }, - ), - ( - "seg", - File, - {"help_string": "Pre-computed segmentation file", "argstr": "--seg {seg}"}, - ), - ( - "weights_sag", - File, - { - "help_string": "Pretrained weights of sagittal network", - "argstr": "--weights_sag {weights_sag}", - "mandatory": False, - }, - ), - ( - "weights_ax", - File, - { - "help_string": "Pretrained weights of axial network", - "argstr": "--weights_ax {weights_ax}", - "mandatory": False, - }, - ), - ( - "weights_cor", - File, - { - "help_string": "Pretrained weights of coronal network", - "argstr": "--weights_cor {weights_cor}", - "mandatory": False, - }, - ), - ( - "seg_log", - File, - { - "help_string": "Name and location for the log-file for the segmentation (FastSurferCNN).", - "argstr": "--seg_log {seg_log}", - "mandatory": False, - }, - ), - ( - "clean_seg", - bool, - { - "help_string": "Flag to clean up FastSurferCNN segmentation", - "argstr": "--clean_seg", - "mandatory": False, - }, - ), - ( - "run_viewagg_on", - File, - { - "help_string": "Define where the view aggregation should be run on.", - "argstr": "--run_viewagg_on {run_viewagg_on}", - "mandatory": False, - }, - ), - ( - "no_cuda", - bool, - { - "help_string": "Flag to disable CUDA usage in FastSurferCNN (no GPU usage, inference on CPU)", - "argstr": "--no_cuda", - "mandatory": False, - }, - ), - ( - "batch", - int, - 16, - { - "help_string": "Batch size for inference. default=16. Lower this to reduce memory requirement", - "argstr": "--batch {batch}", - "mandatory": False, - }, - ), - ( - "fstess", - bool, - { - "help_string": "Use mri_tesselate instead of marching cube (default) for surface creation", - "argstr": "--fstess", - "mandatory": False, - }, - ), - ( - "fsqsphere", - bool, - { - "help_string": "Use FreeSurfer default instead of novel spectral spherical projection for qsphere", - "argstr": "--fsqsphere", - "mandatory": False, - }, - ), - ( - "fsaparc", - bool, - { - "help_string": "Use FS aparc segmentations in addition to DL prediction", - "argstr": "--fsaparc", - "mandatory": False, - }, - ), - ( - "no_surfreg", - bool, - { - "help_string": "Skip creating Surface-Atlas (sphere.reg) registration with FreeSurfer\n (for cross-subject correspondence or other mappings)", - "argstr": "--no_surfreg", - "mandatory": False, - }, - ), - ( - "parallel", - bool, - True, - { - "help_string": "Run both hemispheres in parallel", - "argstr": "--parallel", - "mandatory": False, - }, - ), - ( - "threads", - int, - 4, - { - "help_string": "Set openMP and ITK threads to", - "argstr": "--threads {threads}", - "mandatory": False, - }, - ), - ( - "py", - ty.Any, - "python3.8", - { - "help_string": "which python version to use. default=python3.6", - "argstr": "--py {py}", - "mandatory": False, - }, - ), - ( - "seg_only", - bool, - { - "help_string": "only run FastSurferCNN (generate segmentation, do not surface)", - "argstr": "--seg_only", - "mandatory": False, - }, - ), - ( - "surf_only", - bool, - { - "help_string": "only run the surface pipeline recon_surf.", - "argstr": "--surf_only", - "mandatory": False, - }, - ), -] -fastsurfer_input_spec = specs.SpecInfo( - name="Input", fields=input_fields, bases=(specs.ShellSpec,) -) - - def subject_dir_path(subjects_dir: Path): return Path(subjects_dir) / "FS_outputs" @@ -218,7 +13,6 @@ def norm_img_path(subjects_dir: Path): return Path(subjects_dir) / "FS_outputs" / "mri" / "norm.mgz" -# # Update this section when Docker is being used def aparcaseg_img_path(subjects_dir: Path): return Path(subjects_dir) / "FS_outputs" / "mri" / "aparc+aseg.mgz" @@ -227,64 +21,172 @@ def brainmask_img_path(subjects_dir: Path): return Path(subjects_dir) / "FS_outputs" / "mri" / "brainmask.mgz" -output_fields = [ - ( - "subjects_dir", - Directory, - { - "help_string": "Subjects directory", - "argstr": "--sd {subjects_dir}", - "output_file_template": "subjects_dir", - }, - ), - ( - "subjects_dir_output", - Directory, - { - "help_string": "path to subject FS outputs", - "callable": subject_dir_path, - }, - ), - ( - "norm_img", - MghGz, - { - "help_string": "norm image", - "callable": norm_img_path, - }, - ), - ( - "aparcaseg_img", - MghGz, - { - "help_string": "aparc+aseg image", - "callable": aparcaseg_img_path, - }, - ), - ( - "brainmask_img", - MghGz, - { - "help_string": "brainmask.mgz image", - "callable": brainmask_img_path, - }, - ), -] -fastsurfer_output_spec = specs.SpecInfo( - name="Output", fields=output_fields, bases=(specs.ShellOutSpec,) -) - - -class Fastsurfer(ShellCommandTask): +@shell.define +class Fastsurfer(shell.Task["Fastsurfer.Outputs"]): """ Examples ------- >>> from fileformats.generic import File - >>> from pydra.tasks.fastsurfer.v1.fast_surfer import fast_surfer + >>> from pydra.tasks.fastsurfer.v2_2 import Fastsurfer """ - input_spec = fastsurfer_input_spec - output_spec = fastsurfer_output_spec executable = "run_fastsurfer.sh" + + # Input arguments + subjects_dir: Path = shell.arg( + argstr="--sd {subjects_dir}", + help="Subjects directory", + ) + + subject_id: ty.Any = shell.arg( + argstr="--sid {subject_id}", + help="Subject ID", + ) + + T1_files: File | None = shell.arg( + argstr="--t1 {T1_files}", + help="T1 full head input (not bias corrected, global path)", + default=None, + ) + + fs_license: File = shell.arg( + argstr="--fs_license {fs_license}", + help="Path to FreeSurfer license key file.", + ) + + seg: File | None = shell.arg( + argstr="--seg {seg}", + help="Pre-computed segmentation file", + default=None, + ) + + weights_sag: File | None = shell.arg( + argstr="--weights_sag {weights_sag}", + help="Pretrained weights of sagittal network", + default=None, + ) + + weights_ax: File | None = shell.arg( + argstr="--weights_ax {weights_ax}", + help="Pretrained weights of axial network", + default=None, + ) + + weights_cor: File | None = shell.arg( + argstr="--weights_cor {weights_cor}", + help="Pretrained weights of coronal network", + default=None, + ) + + seg_log: File | None = shell.arg( + argstr="--seg_log {seg_log}", + help="Name and location for the log-file for the segmentation (FastSurferCNN).", + default=None, + ) + + clean_seg: bool = shell.arg( + argstr="--clean_seg", + help="Flag to clean up FastSurferCNN segmentation", + default=False, + ) + + run_viewagg_on: File | None = shell.arg( + argstr="--run_viewagg_on {run_viewagg_on}", + help="Define where the view aggregation should be run on.", + default=None, + ) + + no_cuda: bool = shell.arg( + argstr="--no_cuda", + help="Flag to disable CUDA usage in FastSurferCNN (no GPU usage, inference on CPU)", + default=False, + ) + + batch: int = shell.arg( + argstr="--batch {batch}", + help="Batch size for inference. default=16. Lower this to reduce memory requirement", + default=16, + ) + + fstess: bool = shell.arg( + argstr="--fstess", + help="Use mri_tesselate instead of marching cube (default) for surface creation", + default=False, + ) + + fsqsphere: bool = shell.arg( + argstr="--fsqsphere", + help="Use FreeSurfer default instead of novel spectral spherical projection for qsphere", + default=False, + ) + + fsaparc: bool = shell.arg( + argstr="--fsaparc", + help="Use FS aparc segmentations in addition to DL prediction", + default=False, + ) + + no_surfreg: bool = shell.arg( + argstr="--no_surfreg", + help="Skip creating Surface-Atlas (sphere.reg) registration with FreeSurfer\n (for cross-subject correspondence or other mappings)", + default=False, + ) + + parallel: bool = shell.arg( + argstr="--parallel", + help="Run both hemispheres in parallel", + default=True, + ) + + threads: int = shell.arg( + argstr="--threads {threads}", + help="Set openMP and ITK threads to", + default=4, + ) + + py: ty.Any = shell.arg( + argstr="--py {py}", + help="which python version to use. default=python3.6", + default="python3.8", + ) + + seg_only: bool = shell.arg( + argstr="--seg_only", + help="only run FastSurferCNN (generate segmentation, do not surface)", + default=False, + ) + + surf_only: bool = shell.arg( + argstr="--surf_only", + help="only run the surface pipeline recon_surf.", + default=False, + ) + + class Outputs(shell.Outputs): + subjects_dir: Directory = shell.outarg( + argstr="--sd {subjects_dir}", + help="Subjects directory", + path_template="{subjects_dir}", + ) + + subjects_dir_output: Directory = shell.out( + help="path to subject FS outputs", + callable=subject_dir_path, + ) + + norm_img: MghGz = shell.out( + help="norm image", + callable=norm_img_path, + ) + + aparcaseg_img: MghGz = shell.out( + help="aparc+aseg image", + callable=aparcaseg_img_path, + ) + + brainmask_img: MghGz = shell.out( + help="brainmask.mgz image", + callable=brainmask_img_path, + ) diff --git a/pyproject.toml b/pyproject.toml index 43d03fa..8c425c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["hatchling", "hatch-vcs"] build-backend = "hatchling.build" [project] -name = "pydra-fastsurfer" +name = "pydra-tasks-fastsurfer" description = "Pydra tasks package for fastsurfer" readme = "README.md" requires-python = ">=3.8" @@ -76,3 +76,6 @@ per-file-ignores = ["__init__.py:F401,F403"] max-line-length = 88 select = "C,E,F,W,B,B950" extend-ignore = ['E203', 'E501', 'E129', 'W503'] + +[tool.pyright] +reportAssignmentType = false