Skip to content

Commit 78a5feb

Browse files
committed
Refactor longitudinal pipeline to live within ReconAll interface.
1 parent bdc39fc commit 78a5feb

File tree

5 files changed

+105
-882
lines changed

5 files changed

+105
-882
lines changed

nipype/interfaces/freesurfer/longitudinal.py

Lines changed: 0 additions & 208 deletions
Original file line numberDiff line numberDiff line change
@@ -253,211 +253,3 @@ def _list_outputs(self):
253253
outputs = self.output_spec().get()
254254
outputs["out_file"] = os.path.abspath(self.inputs.out_file)
255255
return outputs
256-
257-
258-
class BaseReconAllInputSpec(ReconAllInputSpec):
259-
base_template_id = traits.Str(
260-
argstr="-base %s",
261-
desc="base template name",
262-
mandatory=True,
263-
)
264-
base_timepoint_ids = InputMultiObject(
265-
traits.Str(),
266-
argstr="-base-tp %s...",
267-
desc="processed time point to use in template",
268-
mandatory=True,
269-
)
270-
271-
class BaseReconAllOutputSpec(FreeSurferSource.output_spec):
272-
subjects_dir = Directory(exists=True, desc="FreeSurfer subjects directory")
273-
subject_id = traits.Str(desc="Subject template name")
274-
275-
class BaseReconAll(CommandLine):
276-
"""Uses the longitudinal pipeline of recon-all to create a template for a given number of subject's sessions.
277-
278-
Examples
279-
--------
280-
>>> from nipype.interfaces.freesurfer.longitudinal import BaseReconAll
281-
>>> baserecon = BaseReconAll()
282-
>>> baserecon.inputs.base_id = 'sub-template'
283-
>>> baserecon.inputs.timepoints = ['ses-1','ses-2']
284-
>>> baserecon.inputs.directive = 'all'
285-
>>> baserecon.inputs.subjects_dir = '.'
286-
>>> baserecon.cmdline
287-
'recon-all -all -base sub-template -sd . -tp ses-1 -tp ses-2'
288-
"""
289-
290-
_cmd = "recon-all"
291-
input_spec = BaseReconAllInputSpec
292-
output_spec = BaseReconAllOutputSpec
293-
_can_resume = True
294-
force_run = False
295-
296-
def _gen_subjects_dir(self):
297-
return os.getcwd()
298-
299-
300-
def _gen_filename(self, name):
301-
if name == "subjects_dir":
302-
return self._gen_subjects_dir()
303-
return None
304-
305-
306-
def _list_outputs(self):
307-
if isdefined(self.inputs.subjects_dir):
308-
subjects_dir = self.inputs.subjects_dir
309-
else:
310-
subjects_dir = self._gen_subjects_dir()
311-
312-
if isdefined(self.inputs.hemi):
313-
hemi = self.inputs.hemi
314-
else:
315-
hemi = "both"
316-
317-
outputs = self._outputs().get()
318-
319-
outputs.update(
320-
FreeSurferSource(subject_id=self.inputs.base_id,
321-
subjects_dir=subjects_dir, hemi=hemi)._list_outputs()
322-
)
323-
outputs["subject_id"] = self.inputs.base_id
324-
outputs["subjects_dir"] = subjects_dir
325-
return outputs
326-
327-
328-
def _is_resuming(self):
329-
subjects_dir = self.inputs.subjects_dir
330-
if not isdefined(subjects_dir):
331-
subjects_dir = self._gen_subjects_dir()
332-
if os.path.isdir(os.path.join(subjects_dir, self.inputs.base_id, "mri")):
333-
return True
334-
return False
335-
336-
337-
def _format_arg(self, name, trait_spec, value):
338-
return super(BaseReconAll, self)._format_arg(name, trait_spec, value)
339-
340-
341-
@property
342-
def cmdline(self):
343-
cmd = super(BaseReconAll, self).cmdline
344-
345-
if not self._is_resuming():
346-
return cmd
347-
348-
subjects_dir = self.inputs.subjects_dir
349-
if not isdefined(subjects_dir):
350-
subjects_dir = self._gen_subjects_dir()
351-
352-
directive = self.inputs.directive
353-
if not isdefined(directive):
354-
steps = []
355-
356-
iflogger.info(f"recon-all: {cmd}")
357-
return cmd
358-
359-
360-
class LongReconAllInputSpec(ReconAllInputSpec):
361-
longitudinal_timepoint_id = traits.Str(
362-
argstr="-long %s",
363-
desc="longitudinal session/timepoint id",
364-
mandatory=True
365-
position=1
366-
)
367-
longitudinal_template_id = traits.Str(
368-
argstr="%s",
369-
desc="longitudinal base template id",
370-
mandatory=True,
371-
position=2
372-
)
373-
374-
375-
class LongReconAllOutputSpec(FreeSurferSource.output_spec):
376-
subjects_dir = Directory(exists=True, desc="FreeSurfer subjects directory")
377-
subject_id = traits.Str(desc="Subject template name")
378-
379-
380-
class LongReconAll(CommandLine):
381-
"""Uses FreeSurfer's longitudinal recon-all to process a subject given
382-
the previously processed base template.
383-
384-
Examples
385-
---------
386-
387-
>>> from nipype.interfaces.freesurfer.longitudinal import LongReconAll
388-
>>> longrecon = LongReconAll()
389-
>>> longrecon.inputs.long_id = "ses-1"
390-
>>> longrecon.inputs.base_id = "sub-template"
391-
>>> longrecon.inputs.directive = "all"
392-
>>> longrecon.inputs.subjects_dir = "."
393-
>>> longrecon.cmdline
394-
'recon-all -all -long ses-1 sub-template -sd .'
395-
"""
396-
397-
_cmd = "recon-all"
398-
input_spec = LongReconAllInputSpec
399-
output_spec = LongReconAllOutputSpec
400-
_can_resume = True
401-
force_run = False
402-
403-
def _gen_subjects_dir(self):
404-
return os.getcwd()
405-
406-
def _gen_filename(self, name):
407-
if name == "subjects_dir":
408-
return self._gen_subjects_dir()
409-
return None
410-
411-
def _list_outputs(self):
412-
subject_id = f"{self.inputs.long_id}.long.{self.inputs.base_id}"
413-
414-
if isdefined(self.inputs.subjects_dir):
415-
subjects_dir = self.inputs.subjects_dir
416-
else:
417-
subjects_dir = self._gen_subjects_dir()
418-
419-
if isdefined(self.inputs.hemi):
420-
hemi = self.inputs.hemi
421-
else:
422-
hemi = "both"
423-
424-
outputs = self._outputs().get()
425-
426-
outputs.update(
427-
FreeSurferSource(
428-
subject_id=subject_id, subjects_dir=subjects_dir, hemi=hemi
429-
)._list_outputs()
430-
)
431-
outputs["subject_id"] = subject_id
432-
outputs["subjects_dir"] = subjects_dir
433-
return outputs
434-
435-
def _is_resuming(self):
436-
subjects_dir = self.inputs.subjects_dir
437-
subject_id = f"{self.inputs.long_id}.long.{self.inputs.base_id}"
438-
if not isdefined(subjects_dir):
439-
subjects_dir = self._gen_subjects_dir()
440-
if os.path.isdir(os.path.join(subjects_dir, subject_id, "mri")):
441-
return True
442-
return False
443-
444-
def _format_arg(self, name, trait_spec, value):
445-
return super(LongReconAll, self)._format_arg(name, trait_spec, value)
446-
447-
@property
448-
def cmdline(self):
449-
cmd = super(LongReconAll, self).cmdline
450-
451-
if not self._is_resuming():
452-
return cmd
453-
454-
subjects_dir = self.inputs.subjects_dir
455-
if not isdefined(subjects_dir):
456-
subjects_dir = self._gen_subjects_dir()
457-
458-
directive = self.inputs.directive
459-
if not isdefined(directive):
460-
steps = []
461-
462-
iflogger.info(f"recon-all: {cmd}")
463-
return cmd

nipype/interfaces/freesurfer/preprocess.py

Lines changed: 87 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
CommandLine,
2626
CommandLineInputSpec,
2727
isdefined,
28+
InputMultiObject,
2829
)
2930
from .base import FSCommand, FSTraitedSpec, FSTraitedSpecOpenMP, FSCommandOpenMP, Info
3031
from .utils import copy2subjdir
@@ -816,7 +817,7 @@ def _gen_filename(self, name):
816817

817818
class ReconAllInputSpec(CommandLineInputSpec):
818819
subject_id = traits.Str(
819-
"recon_all", argstr="-subjid %s", desc="subject name", usedefault=True
820+
"recon_all", argstr="-subjid %s", desc="subject name",
820821
)
821822
directive = traits.Enum(
822823
"all",
@@ -927,6 +928,31 @@ class ReconAllInputSpec(CommandLineInputSpec):
927928
)
928929
flags = InputMultiPath(traits.Str, argstr="%s", desc="additional parameters")
929930

931+
# Longitudinal runs
932+
base_template_id = traits.Str(
933+
argstr="-base %s",
934+
desc="base template id",
935+
xor=["subject_id","longitudinal_timepoint_id"],
936+
requires=["base_timepoint_ids"],
937+
)
938+
base_timepoint_ids = InputMultiObject(
939+
traits.Str(),
940+
argstr="-base-tp %s...",
941+
desc="processed timepoint to use in template",
942+
)
943+
longitudinal_timepoint_id = traits.Str(
944+
argstr="-long %s",
945+
desc="longitudinal session/timepoint id",
946+
xor=["subject_id","base_template_id"],
947+
requires=["longitudinal_template_id"],
948+
position=1
949+
)
950+
longitudinal_template_id = traits.Str(
951+
argstr="%s",
952+
desc="longitudinal base tempalte id",
953+
position=2
954+
)
955+
930956
# Expert options
931957
talairach = traits.Str(desc="Flags to pass to talairach commands", xor=["expert"])
932958
mri_normalize = traits.Str(
@@ -1019,7 +1045,7 @@ class ReconAll(CommandLine):
10191045
>>> reconall.inputs.subject_id = 'foo'
10201046
>>> reconall.inputs.directive = 'all'
10211047
>>> reconall.inputs.subjects_dir = '.'
1022-
>>> reconall.inputs.T1_files = 'structural.nii'
1048+
>>> reconall.inputs.T1_files = ['structural.nii']
10231049
>>> reconall.cmdline
10241050
'recon-all -all -i structural.nii -subjid foo -sd .'
10251051
>>> reconall.inputs.flags = "-qcache"
@@ -1049,7 +1075,7 @@ class ReconAll(CommandLine):
10491075
>>> reconall_subfields.inputs.subject_id = 'foo'
10501076
>>> reconall_subfields.inputs.directive = 'all'
10511077
>>> reconall_subfields.inputs.subjects_dir = '.'
1052-
>>> reconall_subfields.inputs.T1_files = 'structural.nii'
1078+
>>> reconall_subfields.inputs.T1_files = ['structural.nii']
10531079
>>> reconall_subfields.inputs.hippocampal_subfields_T1 = True
10541080
>>> reconall_subfields.cmdline
10551081
'recon-all -all -i structural.nii -hippocampal-subfields-T1 -subjid foo -sd .'
@@ -1060,6 +1086,24 @@ class ReconAll(CommandLine):
10601086
>>> reconall_subfields.inputs.hippocampal_subfields_T1 = False
10611087
>>> reconall_subfields.cmdline
10621088
'recon-all -all -i structural.nii -hippocampal-subfields-T2 structural.nii test -subjid foo -sd .'
1089+
1090+
Base template creation for longitudinal pipeline:
1091+
>>> baserecon = ReconAll()
1092+
>>> baserecon.inputs.base_template_id = 'sub-template'
1093+
>>> baserecon.inputs.base_timepoint_ids = ['ses-1','ses-2']
1094+
>>> baserecon.inputs.directive = 'all'
1095+
>>> baserecon.inputs.subjects_dir = '.'
1096+
>>> baserecon.cmdline
1097+
'recon-all -all -base sub-template -base-tp ses-1 -base-tp ses-2 -sd .'
1098+
1099+
Longitudinal timepoint run:
1100+
>>> longrecon = ReconAll()
1101+
>>> longrecon.inputs.longitudinal_timepoint_id = 'ses-1'
1102+
>>> longrecon.inputs.longitudinal_template_id = 'sub-template'
1103+
>>> longrecon.inputs.directive = 'all'
1104+
>>> longrecon.inputs.subjects_dir = '.'
1105+
>>> longrecon.cmdline
1106+
'recon-all -all -long ses-1 sub-template -sd .'
10631107
"""
10641108

10651109
_cmd = "recon-all"
@@ -1523,21 +1567,53 @@ def _list_outputs(self):
15231567

15241568
outputs = self._outputs().get()
15251569

1526-
outputs.update(
1527-
FreeSurferSource(
1528-
subject_id=self.inputs.subject_id, subjects_dir=subjects_dir, hemi=hemi
1529-
)._list_outputs()
1530-
)
1531-
outputs["subject_id"] = self.inputs.subject_id
1570+
# If using longitudinal pipeline, update subject id accordingly,
1571+
# otherwise use original/default subject_id
1572+
if isdefined(self.inputs.base_template_id):
1573+
outputs.update(
1574+
FreeSurferSource(
1575+
subject_id=self.inputs.base_template_id, subjects_dir=subjects_dir,
1576+
hemi=hemi
1577+
)._list_outputs()
1578+
)
1579+
outputs["subject_id"] = self.inputs.base_template_id
1580+
elif isdefined(self.inputs.longitudinal_timepoint_id):
1581+
subject_id=f"{self.inputs.longitudinal_timepoint_id}.long.{self.inputs.longitudinal_template_id}"
1582+
outputs.update(
1583+
FreeSurferSource(
1584+
subject_id=subject_id, subjects_id=subjects_dir, hemi=hemi
1585+
)._list_outputs()
1586+
)
1587+
outputs["subject_id"] = subject_id
1588+
else:
1589+
outputs.update(
1590+
FreeSurferSource(
1591+
subject_id=self.inputs.subject_id, subjects_dir=subjects_dir, hemi=hemi
1592+
)._list_outputs()
1593+
)
1594+
outputs["subject_id"] = self.inputs.subject_id
1595+
15321596
outputs["subjects_dir"] = subjects_dir
15331597
return outputs
15341598

15351599
def _is_resuming(self):
15361600
subjects_dir = self.inputs.subjects_dir
15371601
if not isdefined(subjects_dir):
15381602
subjects_dir = self._gen_subjects_dir()
1539-
if os.path.isdir(os.path.join(subjects_dir, self.inputs.subject_id, "mri")):
1540-
return True
1603+
1604+
# Check for longitudinal pipeline
1605+
if not isdefined(self.inputs.subject_id):
1606+
if isdefined(self.inputs.base_template_id):
1607+
if os.path.isdir(os.path.join(subjects_dir, self.inputs.base_template_id, "mri")):
1608+
return True
1609+
elif isdefined(self.inputs.longitudinal_template_id):
1610+
if os.path.isdir(os.path.join(subjects_dir,
1611+
f"{self.inputs.longitudinal_timepoint_id}.long.{self.inputs.longitudinal_template_id}",
1612+
"mri")):
1613+
return True
1614+
else:
1615+
if os.path.isdir(os.path.join(subjects_dir, self.inputs.subject_id, "mri")):
1616+
return True
15411617
return False
15421618

15431619
def _format_arg(self, name, trait_spec, value):

0 commit comments

Comments
 (0)