- Notifications
You must be signed in to change notification settings - Fork 536
[ENH] Issue 3345: Adding FreeSurfer longitudinal interfaces #3529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 14 commits
da35d17 d6ec3bd 0b51c54 6a294be 6971865 2d2072f d38e98f 725cd82 7d5253e bdc39fc 78a5feb c82b279 c52cf92 262bfb4 46e07bb 123ac53 34a4ac6 78d580b File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| | @@ -25,6 +25,7 @@ | |
| CommandLine, | ||
| CommandLineInputSpec, | ||
| isdefined, | ||
| InputMultiObject, | ||
| ) | ||
| from .base import FSCommand, FSTraitedSpec, FSTraitedSpecOpenMP, FSCommandOpenMP, Info | ||
| from .utils import copy2subjdir | ||
| | @@ -816,7 +817,10 @@ def _gen_filename(self, name): | |
| | ||
| class ReconAllInputSpec(CommandLineInputSpec): | ||
| subject_id = traits.Str( | ||
| "recon_all", argstr="-subjid %s", desc="subject name", usedefault=True | ||
| "recon_all", | ||
| argstr="-subjid %s", | ||
| desc="subject name", | ||
| xor=["base_template_id","longitudinal_timepoint_id"], | ||
| ) | ||
| directive = traits.Enum( | ||
| "all", | ||
| | @@ -842,21 +846,28 @@ class ReconAllInputSpec(CommandLineInputSpec): | |
| usedefault=True, | ||
| position=0, | ||
| ) | ||
| hemi = traits.Enum("lh", "rh", desc="hemisphere to process", argstr="-hemi %s") | ||
| hemi = traits.Enum("lh", "rh", | ||
| desc="hemisphere to process", | ||
| argstr="-hemi %s", | ||
| requires=["subject_id"], | ||
| ) | ||
| T1_files = InputMultiPath( | ||
| File(exists=True), argstr="-i %s...", desc="name of T1 file to process" | ||
| File(exists=True), argstr="-i %s...", desc="name of T1 file to process", | ||
| requires=["subject_id"], | ||
| ) | ||
| T2_file = File( | ||
| exists=True, | ||
| argstr="-T2 %s", | ||
| min_ver="5.3.0", | ||
| desc="Convert T2 image to orig directory", | ||
| requires=["subject_id"], | ||
| ) | ||
| FLAIR_file = File( | ||
| exists=True, | ||
| argstr="-FLAIR %s", | ||
| min_ver="5.3.0", | ||
| desc="Convert FLAIR image to orig directory", | ||
| requires=["subject_id"] | ||
| ) | ||
| use_T2 = traits.Bool( | ||
| argstr="-T2pial", | ||
| | @@ -885,18 +896,21 @@ class ReconAllInputSpec(CommandLineInputSpec): | |
| "Assume scan parameters are MGH MP-RAGE " | ||
| "protocol, which produces darker gray matter" | ||
| ), | ||
| requires=["subject_id"], | ||
| ) | ||
| big_ventricles = traits.Bool( | ||
| argstr="-bigventricles", | ||
| desc=("For use in subjects with enlarged " "ventricles"), | ||
| ) | ||
| brainstem = traits.Bool( | ||
| argstr="-brainstem-structures", desc="Segment brainstem structures" | ||
| argstr="-brainstem-structures", desc="Segment brainstem structures", | ||
| requires=["subject_id"], | ||
l-espana marked this conversation as resolved. Outdated Show resolved Hide resolved | ||
| ) | ||
| hippocampal_subfields_T1 = traits.Bool( | ||
| argstr="-hippocampal-subfields-T1", | ||
| min_ver="6.0.0", | ||
| desc="segment hippocampal subfields using input T1 scan", | ||
| requires=["subject_id"], | ||
| ) | ||
| hippocampal_subfields_T2 = traits.Tuple( | ||
| File(exists=True), | ||
| | @@ -907,6 +921,7 @@ class ReconAllInputSpec(CommandLineInputSpec): | |
| "segment hippocampal subfields using T2 scan, identified by " | ||
| "ID (may be combined with hippocampal_subfields_T1)" | ||
| ), | ||
| requires=["subject_id"], | ||
| ) | ||
| expert = File( | ||
| exists=True, argstr="-expert %s", desc="Set parameters using expert file" | ||
| | @@ -927,6 +942,29 @@ class ReconAllInputSpec(CommandLineInputSpec): | |
| ) | ||
| flags = InputMultiPath(traits.Str, argstr="%s", desc="additional parameters") | ||
| | ||
| # Longitudinal runs | ||
| Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might need to introduce additional constraints to avoid using cross-sectional specific arguments with base and long modes. I am thinking of the Contributor Author There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That didn't cross my mind but excellent thought. I've add some requires and will test some variations next week to make sure that works as expected. Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks good to me 👍 | ||
| base_template_id = traits.Str( | ||
| argstr="-base %s", | ||
| desc="base template id", | ||
| xor=["subject_id", "longitudinal_timepoint_id"], | ||
| requires=["base_timepoint_ids"], | ||
| ) | ||
| base_timepoint_ids = InputMultiObject( | ||
| traits.Str(), | ||
| argstr="-base-tp %s...", | ||
| desc="processed timepoint to use in template", | ||
| ) | ||
| longitudinal_timepoint_id = traits.Str( | ||
| argstr="-long %s", | ||
| desc="longitudinal session/timepoint id", | ||
| xor=["subject_id", "base_template_id"], | ||
| requires=["longitudinal_template_id"], | ||
| position=1, | ||
| ) | ||
| longitudinal_template_id = traits.Str( | ||
| argstr="%s", desc="longitudinal base tempalte id", position=2 | ||
| ) | ||
| | ||
| # Expert options | ||
| talairach = traits.Str(desc="Flags to pass to talairach commands", xor=["expert"]) | ||
| mri_normalize = traits.Str( | ||
| | @@ -1019,7 +1057,7 @@ class ReconAll(CommandLine): | |
| >>> reconall.inputs.subject_id = 'foo' | ||
| >>> reconall.inputs.directive = 'all' | ||
| >>> reconall.inputs.subjects_dir = '.' | ||
| >>> reconall.inputs.T1_files = 'structural.nii' | ||
| >>> reconall.inputs.T1_files = ['structural.nii'] | ||
| >>> reconall.cmdline | ||
| 'recon-all -all -i structural.nii -subjid foo -sd .' | ||
| >>> reconall.inputs.flags = "-qcache" | ||
| | @@ -1049,7 +1087,7 @@ class ReconAll(CommandLine): | |
| >>> reconall_subfields.inputs.subject_id = 'foo' | ||
| >>> reconall_subfields.inputs.directive = 'all' | ||
| >>> reconall_subfields.inputs.subjects_dir = '.' | ||
| >>> reconall_subfields.inputs.T1_files = 'structural.nii' | ||
| >>> reconall_subfields.inputs.T1_files = ['structural.nii'] | ||
| >>> reconall_subfields.inputs.hippocampal_subfields_T1 = True | ||
| >>> reconall_subfields.cmdline | ||
| 'recon-all -all -i structural.nii -hippocampal-subfields-T1 -subjid foo -sd .' | ||
| | @@ -1060,6 +1098,24 @@ class ReconAll(CommandLine): | |
| >>> reconall_subfields.inputs.hippocampal_subfields_T1 = False | ||
| >>> reconall_subfields.cmdline | ||
| 'recon-all -all -i structural.nii -hippocampal-subfields-T2 structural.nii test -subjid foo -sd .' | ||
| | ||
| Base template creation for longitudinal pipeline: | ||
| >>> baserecon = ReconAll() | ||
| >>> baserecon.inputs.base_template_id = 'sub-template' | ||
| >>> baserecon.inputs.base_timepoint_ids = ['ses-1','ses-2'] | ||
| >>> baserecon.inputs.directive = 'all' | ||
| >>> baserecon.inputs.subjects_dir = '.' | ||
| >>> baserecon.cmdline | ||
| 'recon-all -all -base sub-template -base-tp ses-1 -base-tp ses-2 -sd .' | ||
| | ||
| Longitudinal timepoint run: | ||
| >>> longrecon = ReconAll() | ||
| >>> longrecon.inputs.longitudinal_timepoint_id = 'ses-1' | ||
| >>> longrecon.inputs.longitudinal_template_id = 'sub-template' | ||
| >>> longrecon.inputs.directive = 'all' | ||
| >>> longrecon.inputs.subjects_dir = '.' | ||
| >>> longrecon.cmdline | ||
| 'recon-all -all -long ses-1 sub-template -sd .' | ||
| """ | ||
| | ||
| _cmd = "recon-all" | ||
| | @@ -1523,21 +1579,62 @@ def _list_outputs(self): | |
| | ||
| outputs = self._outputs().get() | ||
| | ||
| outputs.update( | ||
| FreeSurferSource( | ||
| subject_id=self.inputs.subject_id, subjects_dir=subjects_dir, hemi=hemi | ||
| )._list_outputs() | ||
| ) | ||
| outputs["subject_id"] = self.inputs.subject_id | ||
| # If using longitudinal pipeline, update subject id accordingly, | ||
| # otherwise use original/default subject_id | ||
| if isdefined(self.inputs.base_template_id): | ||
| outputs.update( | ||
| FreeSurferSource( | ||
| subject_id=self.inputs.base_template_id, | ||
| subjects_dir=subjects_dir, | ||
| hemi=hemi, | ||
| )._list_outputs() | ||
| ) | ||
| outputs["subject_id"] = self.inputs.base_template_id | ||
| elif isdefined(self.inputs.longitudinal_timepoint_id): | ||
| subject_id = f"{self.inputs.longitudinal_timepoint_id}.long.{self.inputs.longitudinal_template_id}" | ||
| outputs.update( | ||
| FreeSurferSource( | ||
| subject_id=subject_id, subjects_dir=subjects_dir, hemi=hemi | ||
| )._list_outputs() | ||
| ) | ||
| outputs["subject_id"] = subject_id | ||
| else: | ||
| outputs.update( | ||
| FreeSurferSource( | ||
| subject_id=self.inputs.subject_id, | ||
| subjects_dir=subjects_dir, | ||
| hemi=hemi, | ||
| )._list_outputs() | ||
| ) | ||
| outputs["subject_id"] = self.inputs.subject_id | ||
| | ||
| outputs["subjects_dir"] = subjects_dir | ||
| return outputs | ||
| | ||
| def _is_resuming(self): | ||
| subjects_dir = self.inputs.subjects_dir | ||
| if not isdefined(subjects_dir): | ||
| subjects_dir = self._gen_subjects_dir() | ||
| if os.path.isdir(os.path.join(subjects_dir, self.inputs.subject_id, "mri")): | ||
| return True | ||
| | ||
| # Check for longitudinal pipeline | ||
| if not isdefined(self.inputs.subject_id): | ||
| if isdefined(self.inputs.base_template_id): | ||
| if os.path.isdir( | ||
| os.path.join(subjects_dir, self.inputs.base_template_id, "mri") | ||
| ): | ||
| return True | ||
| elif isdefined(self.inputs.longitudinal_template_id): | ||
| if os.path.isdir( | ||
| os.path.join( | ||
| subjects_dir, | ||
| f"{self.inputs.longitudinal_timepoint_id}.long.{self.inputs.longitudinal_template_id}", | ||
| "mri", | ||
| ) | ||
| ): | ||
| return True | ||
| else: | ||
| if os.path.isdir(os.path.join(subjects_dir, self.inputs.subject_id, "mri")): | ||
| return True | ||
| return False | ||
| | ||
| def _format_arg(self, name, trait_spec, value): | ||
| | ||
Uh oh!
There was an error while loading. Please reload this page.