-
Notifications
You must be signed in to change notification settings - Fork 532
[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.
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 | ||
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 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. 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.