Skip to content

Commit da35d17

Browse files
committed
Initial addition of FreeSurfer longitudinal interfaces.
1 parent 5d436ba commit da35d17

File tree

1 file changed

+201
-2
lines changed

1 file changed

+201
-2
lines changed

nipype/interfaces/freesurfer/longitudinal.py

Lines changed: 201 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
import os
88

99
from ... import logging
10-
from ..base import TraitedSpec, File, traits, InputMultiPath, OutputMultiPath, isdefined
11-
from .base import FSCommand, FSTraitedSpec, FSCommandOpenMP, FSTraitedSpecOpenMP
10+
from ..base import TraitedSpec, File, traits, InputMultiPath, OutputMultiPath, isdefined, InputMultiObject, Directory
11+
from .base import FSCommand, FSTraitedSpec, FSCommandOpenMP, FSTraitedSpecOpenMP, CommandLine
12+
from .preprocess import ReconAllInputSpec
13+
from ..io import FreeSurferSource
1214

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

0 commit comments

Comments
 (0)