2525 CommandLine ,
2626 CommandLineInputSpec ,
2727 isdefined ,
28+ InputMultiObject ,
2829)
2930from .base import FSCommand , FSTraitedSpec , FSTraitedSpecOpenMP , FSCommandOpenMP , Info
3031from .utils import copy2subjdir
@@ -816,7 +817,10 @@ def _gen_filename(self, name):
816817
817818class ReconAllInputSpec (CommandLineInputSpec ):
818819 subject_id = traits .Str (
819- "recon_all" , argstr = "-subjid %s" , desc = "subject name" , usedefault = True
820+ "recon_all" ,
821+ argstr = "-subjid %s" ,
822+ desc = "subject name" ,
823+ xor = ["base_template_id" , "longitudinal_timepoint_id" ],
820824 )
821825 directive = traits .Enum (
822826 "all" ,
@@ -842,21 +846,32 @@ class ReconAllInputSpec(CommandLineInputSpec):
842846 usedefault = True ,
843847 position = 0 ,
844848 )
845- hemi = traits .Enum ("lh" , "rh" , desc = "hemisphere to process" , argstr = "-hemi %s" )
849+ hemi = traits .Enum (
850+ "lh" ,
851+ "rh" ,
852+ desc = "hemisphere to process" ,
853+ argstr = "-hemi %s" ,
854+ requires = ["subject_id" ],
855+ )
846856 T1_files = InputMultiPath (
847- File (exists = True ), argstr = "-i %s..." , desc = "name of T1 file to process"
857+ File (exists = True ),
858+ argstr = "-i %s..." ,
859+ desc = "name of T1 file to process" ,
860+ requires = ["subject_id" ],
848861 )
849862 T2_file = File (
850863 exists = True ,
851864 argstr = "-T2 %s" ,
852865 min_ver = "5.3.0" ,
853866 desc = "Convert T2 image to orig directory" ,
867+ requires = ["subject_id" ],
854868 )
855869 FLAIR_file = File (
856870 exists = True ,
857871 argstr = "-FLAIR %s" ,
858872 min_ver = "5.3.0" ,
859873 desc = "Convert FLAIR image to orig directory" ,
874+ requires = ["subject_id" ],
860875 )
861876 use_T2 = traits .Bool (
862877 argstr = "-T2pial" ,
@@ -885,18 +900,22 @@ class ReconAllInputSpec(CommandLineInputSpec):
885900 "Assume scan parameters are MGH MP-RAGE "
886901 "protocol, which produces darker gray matter"
887902 ),
903+ requires = ["subject_id" ],
888904 )
889905 big_ventricles = traits .Bool (
890906 argstr = "-bigventricles" ,
891907 desc = ("For use in subjects with enlarged ventricles" ),
892908 )
893909 brainstem = traits .Bool (
894- argstr = "-brainstem-structures" , desc = "Segment brainstem structures"
910+ argstr = "-brainstem-structures" ,
911+ desc = "Segment brainstem structures" ,
912+ requires = ["subject_id" ],
895913 )
896914 hippocampal_subfields_T1 = traits .Bool (
897915 argstr = "-hippocampal-subfields-T1" ,
898916 min_ver = "6.0.0" ,
899917 desc = "segment hippocampal subfields using input T1 scan" ,
918+ requires = ["subject_id" ],
900919 )
901920 hippocampal_subfields_T2 = traits .Tuple (
902921 File (exists = True ),
@@ -907,6 +926,7 @@ class ReconAllInputSpec(CommandLineInputSpec):
907926 "segment hippocampal subfields using T2 scan, identified by "
908927 "ID (may be combined with hippocampal_subfields_T1)"
909928 ),
929+ requires = ["subject_id" ],
910930 )
911931 expert = File (
912932 exists = True , argstr = "-expert %s" , desc = "Set parameters using expert file"
@@ -927,6 +947,29 @@ class ReconAllInputSpec(CommandLineInputSpec):
927947 )
928948 flags = InputMultiPath (traits .Str , argstr = "%s" , desc = "additional parameters" )
929949
950+ # Longitudinal runs
951+ base_template_id = traits .Str (
952+ argstr = "-base %s" ,
953+ desc = "base template id" ,
954+ xor = ["subject_id" , "longitudinal_timepoint_id" ],
955+ requires = ["base_timepoint_ids" ],
956+ )
957+ base_timepoint_ids = InputMultiObject (
958+ traits .Str (),
959+ argstr = "-base-tp %s..." ,
960+ desc = "processed timepoint to use in template" ,
961+ )
962+ longitudinal_timepoint_id = traits .Str (
963+ argstr = "-long %s" ,
964+ desc = "longitudinal session/timepoint id" ,
965+ xor = ["subject_id" , "base_template_id" ],
966+ requires = ["longitudinal_template_id" ],
967+ position = 1 ,
968+ )
969+ longitudinal_template_id = traits .Str (
970+ argstr = "%s" , desc = "longitudinal base template id" , position = 2
971+ )
972+
930973 # Expert options
931974 talairach = traits .Str (desc = "Flags to pass to talairach commands" , xor = ["expert" ])
932975 mri_normalize = traits .Str (
@@ -1019,7 +1062,7 @@ class ReconAll(CommandLine):
10191062 >>> reconall.inputs.subject_id = 'foo'
10201063 >>> reconall.inputs.directive = 'all'
10211064 >>> reconall.inputs.subjects_dir = '.'
1022- >>> reconall.inputs.T1_files = 'structural.nii'
1065+ >>> reconall.inputs.T1_files = [ 'structural.nii']
10231066 >>> reconall.cmdline
10241067 'recon-all -all -i structural.nii -subjid foo -sd .'
10251068 >>> reconall.inputs.flags = "-qcache"
@@ -1049,7 +1092,7 @@ class ReconAll(CommandLine):
10491092 >>> reconall_subfields.inputs.subject_id = 'foo'
10501093 >>> reconall_subfields.inputs.directive = 'all'
10511094 >>> reconall_subfields.inputs.subjects_dir = '.'
1052- >>> reconall_subfields.inputs.T1_files = 'structural.nii'
1095+ >>> reconall_subfields.inputs.T1_files = [ 'structural.nii']
10531096 >>> reconall_subfields.inputs.hippocampal_subfields_T1 = True
10541097 >>> reconall_subfields.cmdline
10551098 'recon-all -all -i structural.nii -hippocampal-subfields-T1 -subjid foo -sd .'
@@ -1060,6 +1103,24 @@ class ReconAll(CommandLine):
10601103 >>> reconall_subfields.inputs.hippocampal_subfields_T1 = False
10611104 >>> reconall_subfields.cmdline
10621105 'recon-all -all -i structural.nii -hippocampal-subfields-T2 structural.nii test -subjid foo -sd .'
1106+
1107+ Base template creation for longitudinal pipeline:
1108+ >>> baserecon = ReconAll()
1109+ >>> baserecon.inputs.base_template_id = 'sub-template'
1110+ >>> baserecon.inputs.base_timepoint_ids = ['ses-1','ses-2']
1111+ >>> baserecon.inputs.directive = 'all'
1112+ >>> baserecon.inputs.subjects_dir = '.'
1113+ >>> baserecon.cmdline
1114+ 'recon-all -all -base sub-template -base-tp ses-1 -base-tp ses-2 -sd .'
1115+
1116+ Longitudinal timepoint run:
1117+ >>> longrecon = ReconAll()
1118+ >>> longrecon.inputs.longitudinal_timepoint_id = 'ses-1'
1119+ >>> longrecon.inputs.longitudinal_template_id = 'sub-template'
1120+ >>> longrecon.inputs.directive = 'all'
1121+ >>> longrecon.inputs.subjects_dir = '.'
1122+ >>> longrecon.cmdline
1123+ 'recon-all -all -long ses-1 sub-template -sd .'
10631124 """
10641125
10651126 _cmd = "recon-all"
@@ -1523,21 +1584,62 @@ def _list_outputs(self):
15231584
15241585 outputs = self ._outputs ().get ()
15251586
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
1587+ # If using longitudinal pipeline, update subject id accordingly,
1588+ # otherwise use original/default subject_id
1589+ if isdefined (self .inputs .base_template_id ):
1590+ outputs .update (
1591+ FreeSurferSource (
1592+ subject_id = self .inputs .base_template_id ,
1593+ subjects_dir = subjects_dir ,
1594+ hemi = hemi ,
1595+ )._list_outputs ()
1596+ )
1597+ outputs ["subject_id" ] = self .inputs .base_template_id
1598+ elif isdefined (self .inputs .longitudinal_timepoint_id ):
1599+ subject_id = f"{ self .inputs .longitudinal_timepoint_id } .long.{ self .inputs .longitudinal_template_id } "
1600+ outputs .update (
1601+ FreeSurferSource (
1602+ subject_id = subject_id , subjects_dir = subjects_dir , hemi = hemi
1603+ )._list_outputs ()
1604+ )
1605+ outputs ["subject_id" ] = subject_id
1606+ else :
1607+ outputs .update (
1608+ FreeSurferSource (
1609+ subject_id = self .inputs .subject_id ,
1610+ subjects_dir = subjects_dir ,
1611+ hemi = hemi ,
1612+ )._list_outputs ()
1613+ )
1614+ outputs ["subject_id" ] = self .inputs .subject_id
1615+
15321616 outputs ["subjects_dir" ] = subjects_dir
15331617 return outputs
15341618
15351619 def _is_resuming (self ):
15361620 subjects_dir = self .inputs .subjects_dir
15371621 if not isdefined (subjects_dir ):
15381622 subjects_dir = self ._gen_subjects_dir ()
1539- if os .path .isdir (os .path .join (subjects_dir , self .inputs .subject_id , "mri" )):
1540- return True
1623+
1624+ # Check for longitudinal pipeline
1625+ if not isdefined (self .inputs .subject_id ):
1626+ if isdefined (self .inputs .base_template_id ):
1627+ if os .path .isdir (
1628+ os .path .join (subjects_dir , self .inputs .base_template_id , "mri" )
1629+ ):
1630+ return True
1631+ elif isdefined (self .inputs .longitudinal_template_id ):
1632+ if os .path .isdir (
1633+ os .path .join (
1634+ subjects_dir ,
1635+ f"{ self .inputs .longitudinal_timepoint_id } .long.{ self .inputs .longitudinal_template_id } " ,
1636+ "mri" ,
1637+ )
1638+ ):
1639+ return True
1640+ else :
1641+ if os .path .isdir (os .path .join (subjects_dir , self .inputs .subject_id , "mri" )):
1642+ return True
15411643 return False
15421644
15431645 def _format_arg (self , name , trait_spec , value ):
0 commit comments