Skip to content

Commit ee871ba

Browse files
committed
Merge pull request #1326 from BRAINSia/FreeSurferBreakout
ENH: FreeSurfer Wrappings for Recon-All Workflow
2 parents 355591b + 4b850c8 commit ee871ba

File tree

74 files changed

+10044
-149
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+10044
-149
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Next release
2525
* ENH: Interfaces for MINC tools (https://github.com/nipy/nipype/pull/1304)
2626
* FIX: Use realpath to determine hard link source (https://github.com/nipy/nipype/pull/1388)
2727
* FIX: Correct linking/copying fallback behavior (https://github.com/nipy/nipype/pull/1391)
28+
* ENH: Nipype workflow and interfaces for FreeSurfer's recon-all (https://github.com/nipy/nipype/pull/1326)
2829

2930
Release 0.11.0 (September 15, 2015)
3031
============

CHANGES.orig

Lines changed: 538 additions & 0 deletions
Large diffs are not rendered by default.

examples/smri_fsreconall.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/usr/bin/env python
2+
"""
3+
================
4+
sMRI: FSReconAll
5+
================
6+
7+
This script, smri_fsreconall.py, demonstrates the ability to use the
8+
reconall nipype workflow with a set of subjects and then make an average
9+
subject::
10+
11+
python smri_fsreconall.py
12+
13+
Import necessary modules from nipype.
14+
"""
15+
16+
import os
17+
18+
import nipype.pipeline.engine as pe
19+
import nipype.interfaces.io as nio
20+
from nipype.workflows.smri.freesurfer import create_reconall_workflow
21+
from nipype.interfaces.freesurfer.utils import MakeAverageSubject
22+
from nipype.interfaces.utility import IdentityInterface
23+
24+
"""
25+
Assign the tutorial directory
26+
"""
27+
28+
tutorial_dir = os.path.abspath('smri_fsreconall_tutorial')
29+
if not os.path.isdir(tutorial_dir):
30+
os.mkdir(tutorial_dir)
31+
32+
"""
33+
Define the workflow directories
34+
"""
35+
36+
subject_list = ['s1', 's3']
37+
data_dir = os.path.abspath('data')
38+
subjects_dir = os.path.join(tutorial_dir, 'subjects_dir')
39+
if not os.path.exists(subjects_dir):
40+
os.mkdir(subjects_dir)
41+
42+
wf = pe.Workflow(name="l1workflow")
43+
wf.base_dir = os.path.join(tutorial_dir, 'workdir')
44+
45+
"""
46+
Create inputspec
47+
"""
48+
49+
inputspec = pe.Node(interface=IdentityInterface(['subject_id']),
50+
name="inputspec")
51+
inputspec.iterables = ("subject_id", subject_list)
52+
53+
"""
54+
Grab data
55+
"""
56+
57+
datasource = pe.Node(interface=nio.DataGrabber(infields=['subject_id'],
58+
outfields=['struct']),
59+
name='datasource')
60+
datasource.inputs.base_directory = data_dir
61+
datasource.inputs.template = '%s/%s.nii'
62+
datasource.inputs.template_args = dict(struct=[['subject_id', 'struct']])
63+
datasource.inputs.subject_id = subject_list
64+
datasource.inputs.sort_filelist = True
65+
66+
wf.connect(inputspec, 'subject_id', datasource, 'subject_id')
67+
68+
"""
69+
Run recon-all
70+
"""
71+
72+
recon_all = create_reconall_workflow()
73+
recon_all.inputs.inputspec.subjects_dir = subjects_dir
74+
75+
wf.connect(datasource, 'struct', recon_all, 'inputspec.T1_files')
76+
wf.connect(inputspec, 'subject_id', recon_all, 'inputspec.subject_id')
77+
78+
"""
79+
Make average subject
80+
"""
81+
82+
average = pe.JoinNode(interface=MakeAverageSubject(),
83+
joinsource="inputspec",
84+
joinfield="subjects_ids",
85+
name="average")
86+
average.inputs.subjects_dir = subjects_dir
87+
88+
wf.connect(recon_all, 'postdatasink_outputspec.subject_id', average, 'subjects_ids')
89+
90+
wf.run("MultiProc", plugin_args={'n_procs': 4})

nipype/algorithms/tests/test_auto_Overlap.py

Lines changed: 0 additions & 47 deletions
This file was deleted.

nipype/interfaces/freesurfer/__init__.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,21 @@
66
from .preprocess import (ParseDICOMDir, UnpackSDICOMDir, MRIConvert, Resample,
77
ReconAll, BBRegister, ApplyVolTransform, Smooth,
88
DICOMConvert, RobustRegister, FitMSParams,
9-
SynthesizeFLASH)
10-
from .model import (MRISPreproc, GLMFit, OneSampleTTest, Binarize, Concatenate,
11-
SegStats, Label2Vol, MS_LDA)
9+
SynthesizeFLASH, MNIBiasCorrection, WatershedSkullStrip,
10+
Normalize, CANormalize, CARegister, CALabel, MRIsCALabel,
11+
SegmentCC, SegmentWM, EditWMwithAseg, ConcatenateLTA)
12+
from .model import (MRISPreproc, MRISPreprocReconAll, GLMFit, OneSampleTTest, Binarize,
13+
Concatenate, SegStats, SegStatsReconAll, Label2Vol, MS_LDA,
14+
Label2Label, Label2Annot, SphericalAverage)
1215
from .utils import (SampleToSurface, SurfaceSmooth, SurfaceTransform, Surface2VolTransform,
1316
SurfaceSnapshots, ApplyMask, MRIsConvert, MRITessellate, MRIPretess,
1417
MRIMarchingCubes, SmoothTessellation, MakeAverageSubject,
15-
ExtractMainComponent, Tkregister2)
18+
ExtractMainComponent, Tkregister2, AddXFormToHeader,
19+
CheckTalairachAlignment, TalairachAVI, TalairachQC, RemoveNeck,
20+
MRIFill, MRIsInflate, Sphere, FixTopology, EulerNumber,
21+
RemoveIntersection, MakeSurfaces, Curvature, CurvatureStats,
22+
Jacobian, MRIsCalc, VolumeMask, ParcellationStats, Contrast,
23+
RelabelHypointensities, Aparc2Aseg, Apas2Aseg)
24+
from .longitudinal import (RobustTemplate, FuseSegmentations)
25+
from .registration import (MPRtoMNI305, RegisterAVItoTalairach, EMRegister, Register,
26+
Paint)

nipype/interfaces/freesurfer/base.py

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
import os
2121

2222
from ..base import (CommandLine, Directory,
23-
CommandLineInputSpec, isdefined)
23+
CommandLineInputSpec, isdefined,
24+
traits, TraitedSpec, File)
2425
from ...utils.filemanip import fname_presuffix
2526

2627

@@ -161,3 +162,61 @@ def version(self):
161162
return ver.rstrip().split('-')[-1] + '.dev'
162163
else:
163164
return ver.rstrip().split('-v')[-1]
165+
166+
167+
class FSScriptCommand(FSCommand):
168+
""" Support for Freesurfer script commands with log inputs.terminal_output """
169+
_terminal_output = 'file'
170+
_always_run = False
171+
172+
def __init__(self, **inputs):
173+
super(FSScriptCommand, self).__init__(**inputs)
174+
self.set_default_terminal_output(self._terminal_output)
175+
176+
def _list_outputs(self):
177+
outputs = self._outputs().get()
178+
outputs['log_file'] = os.path.abspath('stdout.nipype')
179+
return outputs
180+
181+
182+
class FSScriptOutputSpec(TraitedSpec):
183+
log_file = File('stdout.nipype', usedefault=True,
184+
exists=True, desc="The output log")
185+
186+
187+
class FSTraitedSpecOpenMP(FSTraitedSpec):
188+
num_threads = traits.Int(desc='allows for specifying more threads')
189+
190+
191+
class FSCommandOpenMP(FSCommand):
192+
"""Support for FS commands that utilize OpenMP
193+
194+
Sets the environment variable 'OMP_NUM_THREADS' to the number
195+
of threads specified by the input num_threads.
196+
"""
197+
198+
input_spec = FSTraitedSpecOpenMP
199+
200+
_num_threads = None
201+
202+
def __init__(self, **inputs):
203+
super(FSCommandOpenMP, self).__init__(**inputs)
204+
self.inputs.on_trait_change(self._num_threads_update, 'num_threads')
205+
if not self._num_threads:
206+
self._num_threads = os.environ.get('OMP_NUM_THREADS', None)
207+
if not self._num_threads:
208+
self._num_threads = os.environ.get('NSLOTS', None)
209+
if not isdefined(self.inputs.num_threads) and self._num_threads:
210+
self.inputs.num_threads = int(self._num_threads)
211+
self._num_threads_update()
212+
213+
def _num_threads_update(self):
214+
if self.inputs.num_threads:
215+
self.inputs.environ.update(
216+
{'OMP_NUM_THREADS': str(self.inputs.num_threads)})
217+
218+
def run(self, **inputs):
219+
if 'num_threads' in inputs:
220+
self.inputs.num_threads = inputs['num_threads']
221+
self._num_threads_update()
222+
return super(FSCommandOpenMP, self).run(**inputs)

0 commit comments

Comments
 (0)