Skip to content

Commit

Permalink
Merge pull request nipy#1434 from BRAINSia/FreeSurfer53
Browse files Browse the repository at this point in the history
ENH: Makes ReconAll workflow backwards compatible with FreeSurfer 5.3.0.
  • Loading branch information
satra committed Apr 17, 2016
2 parents 252a2dd + 9faf6f8 commit f6ae4e4
Show file tree
Hide file tree
Showing 14 changed files with 320 additions and 147 deletions.
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Next release
* FIX: Correct linking/copying fallback behavior (https://github.com/nipy/nipype/pull/1391)
* ENH: Nipype workflow and interfaces for FreeSurfer's recon-all (https://github.com/nipy/nipype/pull/1326)
* FIX: Permit relative path for concatenated_file input to Concatenate() (https://github.com/nipy/nipype/pull/1411)
* ENH: Makes ReconAll workflow backwards compatible with FreeSurfer 5.3.0 (https://github.com/nipy/nipype/pull/1434)

Release 0.11.0 (September 15, 2015)
============
Expand Down
11 changes: 7 additions & 4 deletions nipype/interfaces/freesurfer/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,7 @@ class SegStatsReconAllInputSpec(SegStatsInputSpec):
# implicit
ribbon = traits.File(mandatory=True, exists=True,
desc="Input file mri/ribbon.mgz")
presurf_seg = File(mandatory=True, exists=True,
presurf_seg = File(exists=True,
desc="Input segmentation volume")
transform = File(mandatory=True, exists=True,
desc="Input transform file")
Expand All @@ -796,6 +796,8 @@ class SegStatsReconAllInputSpec(SegStatsInputSpec):
desc="Input file must be <subject_id>/surf/lh.pial")
rh_pial = File(mandatory=True, exists=True,
desc="Input file must be <subject_id>/surf/rh.pial")
aseg = File(exists=True,
desc="Mandatory implicit input in 5.3")
copy_inputs = traits.Bool(desc="If running as a node, set this to True " +
"otherwise, this will copy the implicit inputs " +
"to the node directory.")
Expand Down Expand Up @@ -859,7 +861,7 @@ def run(self, **inputs):
copy2subjdir(self, self.inputs.lh_white,
'surf', 'lh.white')
copy2subjdir(self, self.inputs.rh_white,
'Surf', 'Rh.White')
'surf', 'rh.white')
copy2subjdir(self, self.inputs.lh_pial,
'surf', 'lh.pial')
copy2subjdir(self, self.inputs.rh_pial,
Expand All @@ -868,12 +870,13 @@ def run(self, **inputs):
'mri', 'ribbon.mgz')
copy2subjdir(self, self.inputs.presurf_seg,
'mri', 'aseg.presurf.mgz')
copy2subjdir(self, self.inputs.aseg,
'mri', 'aseg.mgz')
copy2subjdir(self, self.inputs.transform,
os.path.join('mri', 'transforms'),
'talairach.xfm')
copy2subjdir(self, self.inputs.in_intensity, 'mri')
if isdefined(self.inputs.brainmask_file):
copy2subjdir(self, self.inputs.brainmask_file, 'mri')
copy2subjdir(self, self.inputs.brainmask_file, 'mri')
return super(SegStatsReconAll, self).run(**inputs)


Expand Down
1 change: 1 addition & 0 deletions nipype/interfaces/freesurfer/tests/test_auto_Aparc2Aseg.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def test_Aparc2Aseg_inputs():
environ=dict(nohash=True,
usedefault=True,
),
filled=dict(),
hypo_wm=dict(argstr='--hypo-as-wm',
mandatory=False,
),
Expand Down
1 change: 1 addition & 0 deletions nipype/interfaces/freesurfer/tests/test_auto_Curvature.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def test_Curvature_inputs():
usedefault=True,
),
in_file=dict(argstr='%s',
copyfile=True,
mandatory=True,
position=-2,
),
Expand Down
3 changes: 3 additions & 0 deletions nipype/interfaces/freesurfer/tests/test_auto_MakeSurfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def test_MakeSurfaces_inputs():
in_orig=dict(argstr='-orig %s',
mandatory=True,
),
in_white=dict(),
in_wm=dict(mandatory=True,
),
longitudinal=dict(argstr='-long',
Expand Down Expand Up @@ -66,6 +67,8 @@ def test_MakeSurfaces_inputs():
subjects_dir=dict(),
terminal_output=dict(nohash=True,
),
white=dict(argstr='-white %s',
),
white_only=dict(argstr='-whiteonly',
mandatory=False,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def test_SegStatsReconAll_inputs():
),
args=dict(argstr='%s',
),
aseg=dict(),
avgwf_file=dict(argstr='--avgwfvol %s',
),
avgwf_txt_file=dict(argstr='--avgwf %s',
Expand Down Expand Up @@ -85,8 +86,7 @@ def test_SegStatsReconAll_inputs():
),
partial_volume_file=dict(argstr='--pv %s',
),
presurf_seg=dict(mandatory=True,
),
presurf_seg=dict(),
rh_orig_nofix=dict(mandatory=True,
),
rh_pial=dict(mandatory=True,
Expand Down
3 changes: 3 additions & 0 deletions nipype/interfaces/freesurfer/tests/test_auto_VolumeMask.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
def test_VolumeMask_inputs():
input_map = dict(args=dict(argstr='%s',
),
aseg=dict(xor=['in_aseg'],
),
copy_inputs=dict(mandatory=False,
),
environ=dict(nohash=True,
Expand All @@ -16,6 +18,7 @@ def test_VolumeMask_inputs():
),
in_aseg=dict(argstr='--aseg_name %s',
mandatory=False,
xor=['aseg'],
),
left_ribbonlabel=dict(argstr='--label_left_ribbon %d',
mandatory=True,
Expand Down
52 changes: 38 additions & 14 deletions nipype/interfaces/freesurfer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,30 @@


def copy2subjdir(cls, in_file, folder=None, basename=None, subject_id=None):
"""Method to copy an input to the subjects directory"""
# check that the input is defined
if not isdefined(in_file):
return in_file
# check that subjects_dir is defined
if isdefined(cls.inputs.subjects_dir):
subjects_dir = cls.inputs.subjects_dir
else:
subjects_dir = os.getcwd()
subjects_dir = os.getcwd() #if not use cwd
# check for subject_id
if not subject_id:
if isdefined(cls.inputs.subject_id):
subject_id = cls.inputs.subject_id
else:
subject_id = 'subject_id'
subject_id = 'subject_id' #default
# check for basename
if basename == None:
basename = os.path.basename(in_file)
# check which folder to put the file in
if folder != None:
out_dir = os.path.join(subjects_dir, subject_id, folder)
else:
out_dir = os.path.join(subjects_dir, subject_id)
# make the output folder if it does not exist
if not os.path.isdir(out_dir):
os.makedirs(out_dir)
out_file = os.path.join(out_dir, basename)
Expand All @@ -58,7 +67,7 @@ def copy2subjdir(cls, in_file, folder=None, basename=None, subject_id=None):
return out_file

def createoutputdirs(outputs):
"""create an output directories. If not created, some freesurfer interfaces fail"""
"""create all output directories. If not created, some freesurfer interfaces fail"""
for output in outputs.itervalues():
dirname = os.path.dirname(output)
if not os.path.isdir(dirname):
Expand Down Expand Up @@ -1867,6 +1876,7 @@ class MakeSurfacesInputSpec(FSTraitedSpec):
in_filled = File(exists=True, mandatory=True,
desc="Implicit input file filled.mgz")
# optional
in_white = File(exists=True, desc="Implicit input that is sometimes used")
in_label = File(exists=True, mandatory=False, xor=['noaparc'],
desc="Implicit input label/<hemisphere>.aparc.annot")
orig_white = File(argstr="-orig_white %s", exists=True, mandatory=False,
Expand All @@ -1893,6 +1903,8 @@ class MakeSurfacesInputSpec(FSTraitedSpec):
argstr="-max %.1f", desc="No documentation (used for longitudinal processing)")
longitudinal = traits.Bool(
argstr="-long", desc="No documentation (used for longitudinal processing)")
white = traits.String(argstr="-white %s",
desc="White surface name")
copy_inputs = traits.Bool(mandatory=False,
desc="If running as a node, set this to True." +
"This will copy the input files to the node " +
Expand Down Expand Up @@ -1947,15 +1959,15 @@ def run(self, **inputs):
folder='mri', basename='wm.mgz')
copy2subjdir(self, self.inputs.in_filled,
folder='mri', basename='filled.mgz')
copy2subjdir(self, self.inputs.in_white,
'surf', '{0}.white'.format(self.inputs.hemisphere))
for originalfile in [self.inputs.in_aseg,
self.inputs.in_T1]:
if isdefined(originalfile):
copy2subjdir(self, originalfile, folder='mri')
copy2subjdir(self, originalfile, folder='mri')
for originalfile in [self.inputs.orig_white,
self.inputs.orig_pial,
self.inputs.in_orig]:
if isdefined(originalfile):
copy2subjdir(self, originalfile, folder='surf')
copy2subjdir(self, originalfile, folder='surf')
if isdefined(self.inputs.in_label):
copy2subjdir(self, self.inputs.in_label, 'label',
'{0}.aparc.annot'.format(self.inputs.hemisphere))
Expand All @@ -1972,9 +1984,11 @@ def _format_arg(self, name, spec, value):
basename = os.path.basename(value)
# whent the -mgz flag is specified, it assumes the mgz extension
if self.inputs.mgz:
prefix = basename.rstrip('.mgz')
prefix = os.path.splitext(basename)[0]
else:
prefix = basename
if prefix == 'aseg':
return # aseg is already the default
return spec.argstr % prefix
elif name in ['orig_white', 'orig_pial']:
# these inputs do take full file paths or even basenames
Expand Down Expand Up @@ -2011,8 +2025,8 @@ def _list_outputs(self):
dest_dir, str(self.inputs.hemisphere) + '.area')
# Something determines when a pial surface and thickness file is generated
# but documentation doesn't say what.
# The orig_pial flag is just a guess
if isdefined(self.inputs.orig_pial):
# The orig_pial input is just a guess
if isdefined(self.inputs.orig_pial) or self.inputs.white == 'NOWRITE':
outputs["out_curv"] = outputs["out_curv"] + ".pial"
outputs["out_area"] = outputs["out_area"] + ".pial"
outputs["out_pial"] = os.path.join(
Expand All @@ -2029,7 +2043,7 @@ def _list_outputs(self):

class CurvatureInputSpec(FSTraitedSpec):
in_file = File(argstr="%s", position=-2, mandatory=True, exists=True,
desc="Input file for Curvature")
copyfile=True, desc="Input file for Curvature")
# optional
threshold = traits.Float(
argstr="-thresh %.3f", mandatory=False, desc="Undocumented input threshold")
Expand Down Expand Up @@ -2073,7 +2087,6 @@ def _format_arg(self, name, spec, value):
if self.inputs.copy_input:
if name == 'in_file':
basename = os.path.basename(value)
shutil.copy(value, basename)
return spec.argstr % basename
return super(Curvature, self)._format_arg(name, spec, value)

Expand Down Expand Up @@ -2301,11 +2314,16 @@ class VolumeMaskInputSpec(FSTraitedSpec):
desc="Implicit input left white matter surface")
rh_white = File(mandatory=True, exists=True,
desc="Implicit input right white matter surface")
aseg = File(exists=True,
xor=['in_aseg'],
desc="Implicit aseg.mgz segmentation. " +
"Specify a different aseg by using the 'in_aseg' input.")
subject_id = traits.String('subject_id', usedefault=True,
position=-1, argstr="%s", mandatory=True,
desc="Subject being processed")
# optional
in_aseg = File(argstr="--aseg_name %s", mandatory=False, exists=True,
in_aseg = File(argstr="--aseg_name %s", mandatory=False,
exists=True, xor=['aseg'],
desc="Input aseg file for VolumeMask")
save_ribbon = traits.Bool(argstr="--save_ribbon", mandatory=False,
desc="option to save just the ribbon for the " +
Expand Down Expand Up @@ -2364,6 +2382,8 @@ def run(self, **inputs):
copy2subjdir(self, self.inputs.lh_white, 'surf', 'lh.white')
copy2subjdir(self, self.inputs.rh_white, 'surf', 'rh.white')
copy2subjdir(self, self.inputs.in_aseg, 'mri')
copy2subjdir(self, self.inputs.aseg, 'mri', 'aseg.mgz')

return super(VolumeMask, self).run(**inputs)

def _format_arg(self, name, spec, value):
Expand Down Expand Up @@ -2726,6 +2746,8 @@ class Aparc2AsegInputSpec(FSTraitedSpec):
rh_annotation = File(mandatory=True, exists=True,
desc="Input file must be <subject_id>/label/rh.aparc.annot")
# optional
filled = File(exists=True,
desc="Implicit input filled file. Only required with FS v5.3.")
aseg = File(argstr="--aseg %s", mandatory=False, exists=True,
desc="Input aseg file")
volmask = traits.Bool(argstr="--volmask", mandatory=False,
Expand Down Expand Up @@ -2808,6 +2830,7 @@ def run(self, **inputs):
copy2subjdir(self, self.inputs.rh_ribbon, 'mri', 'rh.ribbon.mgz')
copy2subjdir(self, self.inputs.ribbon, 'mri', 'ribbon.mgz')
copy2subjdir(self, self.inputs.aseg, 'mri')
copy2subjdir(self, self.inputs.filled, 'mri', 'filled.mgz')
copy2subjdir(self, self.inputs.lh_annotation, 'label')
copy2subjdir(self, self.inputs.rh_annotation, 'label')

Expand All @@ -2816,7 +2839,8 @@ def run(self, **inputs):
def _format_arg(self, name, spec, value):
if name == 'aseg':
# aseg does not take a full filename
return spec.argstr % os.path.basename(value).replace('.mgz', '')
basename = os.path.basename(value).replace('.mgz', '')
return spec.argstr % basename
elif name == 'out_file':
return spec.argstr % os.path.abspath(value)

Expand Down
Loading

0 comments on commit f6ae4e4

Please sign in to comment.