Skip to content

Commit 7fc0fd1

Browse files
author
David Ellis
committed
ENH: Adds example of freesurfer recon-all workflow in nipype.
1 parent 6329f3e commit 7fc0fd1

File tree

7 files changed

+121
-107
lines changed

7 files changed

+121
-107
lines changed

examples/smri_fsreconall.py

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,50 +19,68 @@
1919
import nipype.interfaces.io as nio
2020
from nipype.workflows.smri.freesurfer import create_reconall_workflow
2121
from nipype.interfaces.freesurfer.utils import MakeAverageSubject
22+
from nipype.interfaces.utility import IdentityInterface
2223

24+
"""
25+
Assign the tutorial directory
26+
"""
27+
tutorial_dir = os.path.abspath('smri_fsreconall_tutorial')
28+
if not os.path.isdir(tutorial_dir):
29+
os.mkdir(tutorial_dir)
30+
31+
"""
32+
Define the workflow directories
33+
"""
2334

2435
subject_list = ['s1', 's3']
2536
data_dir = os.path.abspath('data')
26-
subjects_dir = os.path.abspath('amri_freesurfer_tutorial/subjects_dir')
37+
subjects_dir = os.path.join(tutorial_dir, 'subjects_dir')
38+
if not os.path.exists(subjects_dir):
39+
os.mkdir(subjects_dir)
2740

2841
wf = pe.Workflow(name="l1workflow")
29-
wf.base_dir = os.path.abspath('amri_freesurfer_tutorial/workdir')
42+
wf.base_dir = os.path.join(tutorial_dir, 'workdir')
43+
44+
"""
45+
Create inputspec
46+
"""
47+
48+
inputspec = pe.Node(interface=IdentityInterface(['subject_id']),
49+
name="inputspec")
50+
inputspec.iterables = ("subject_id", subject_list)
3051

3152
"""
3253
Grab data
3354
"""
3455

35-
datasource = pe.MapNode(interface=nio.DataGrabber(infields=['subject_id'],
36-
outfields=['struct']),
37-
name='datasource',
38-
iterfield=['subject_id'])
56+
datasource = pe.Node(interface=nio.DataGrabber(infields=['subject_id'],
57+
outfields=['struct']),
58+
name='datasource')
3959
datasource.inputs.base_directory = data_dir
4060
datasource.inputs.template = '%s/%s.nii'
4161
datasource.inputs.template_args = dict(struct=[['subject_id', 'struct']])
4262
datasource.inputs.subject_id = subject_list
4363
datasource.inputs.sort_filelist = True
4464

65+
wf.connect(inputspec, 'subject_id', datasource, 'subject_id')
66+
4567
"""
4668
Run recon-all
4769
"""
4870

49-
recon_all = create_reconall_workflow(subjects_dir=subjects_dir)
50-
pe.MapNode(interface=ReconAllWF(), name='recon_all',
51-
iterfield=['subject_id', 'T1_files'])
52-
recon_all.inputs.subject_id = subject_list
53-
if not os.path.exists(subjects_dir):
54-
os.mkdir(subjects_dir)
55-
recon_all.inputs.subjects_dir = subjects_dir
71+
recon_all = create_reconall_workflow()
72+
recon_all.inputs.inputspec.subjects_dir = subjects_dir
5673

57-
wf.connect(datasource, 'struct', recon_all, 'T1_files')
74+
wf.connect(datasource, 'struct', recon_all, 'inputspec.T1_files')
75+
wf.connect(inputspec, 'subject_id', recon_all, 'inputspec.subject_id')
5876

5977
"""
6078
Make average subject
6179
"""
6280

63-
average = pe.Node(interface=MakeAverageSubject(), name="average")
64-
average.inputs.subjects_dir = subjects_dir
81+
#average = pe.Node(interface=MakeAverageSubject(), name="average")
82+
#average.inputs.subjects_dir = subjects_dir
6583

66-
wf.connect(recon_all, 'subject_id', average, 'subjects_ids')
84+
#wf.connect(recon_all, 'outputspec.subject_id', average, 'subjects_ids')
6785

6886
wf.run("MultiProc", plugin_args={'n_procs': 4})

nipype/workflows/smri/freesurfer/autorecon1.py

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
from nipype.interfaces.utility import Function,IdentityInterface
55
import nipype.pipeline.engine as pe # pypeline engine
66
from nipype.interfaces.freesurfer import *
7-
from utils import copy_file, copy_files, awkfile
7+
from utils import copy_file, copy_files
88

99
def checkT1s(T1_files, cw256=False):
1010
"""Verifying size of inputs and setting workflow parameters"""
1111
import SimpleITK as sitk
1212
import os
1313
import sys
14+
# check that the files are in a list
15+
if not type(T1_files) == list:
16+
T1_files = [T1_files]
1417
if len(T1_files) == 0:
1518
print("ERROR: No T1's Given")
1619
sys.exit(-1)
@@ -66,20 +69,20 @@ def create_AutoRecon1(name="AutoRecon1", longitudinal=False, field_strength='1.5
6669
ar1_wf = pe.Workflow(name=name)
6770
inputspec = pe.Node(interface=IdentityInterface(fields=['T1_files',
6871
'T2_file',
69-
'in_flair',
72+
'FLAIR_file',
7073
'cw256',
7174
'num_threads',
72-
'skulltemplate']),
75+
'reg_template_withskull']),
7376
run_without_submitting=True,
7477
name='inputspec')
7578

7679
if not longitudinal:
7780
# single session processing
78-
verify_inputs = pe.Node(Function(infields=["T1_files", "cw256"],
79-
outfields=["T1_files", "cw256", "resample_type", "origvol_names"],
80-
checkT1s)
81-
name="Check_T1s"),
82-
ar1_wf.conncet([(inputspec, verify_inputs, [('T1_files', 'T1_files'),
81+
verify_inputs = pe.Node(Function(["T1_files", "cw256"],
82+
["T1_files", "cw256", "resample_type", "origvol_names"],
83+
checkT1s),
84+
name="Check_T1s")
85+
ar1_wf.connect([(inputspec, verify_inputs, [('T1_files', 'T1_files'),
8386
('cw256', 'cw256')])])
8487

8588

@@ -93,11 +96,11 @@ def create_AutoRecon1(name="AutoRecon1", longitudinal=False, field_strength='1.5
9396
('origvol_names', 'out_file')]),
9497
])
9598

96-
def convert_modalities(in_file, out_file):
99+
def convert_modalities(in_file=None, out_file=None):
97100
"""Returns an undefined output if the in_file is not defined"""
98-
from nipype.interfaces.base import isdefined
99101
from nipype.interfaces.freesurfer import MRIConvert
100-
if isdefined(in_file):
102+
import os
103+
if in_file:
101104
convert = MRIConvert()
102105
convert.inputs.in_file = in_file
103106
convert.inputs.out_file = out_file
@@ -116,9 +119,9 @@ def convert_modalities(in_file, out_file):
116119
FLAIR_convert = pe.Node(Function(['in_file', 'out_file'],
117120
['out_file'],
118121
convert_modalities),
119-
name="T2_Convert")
122+
name="FLAIR_Convert")
120123
FLAIR_convert.inputs.out_file = 'FLAIRraw.mgz'
121-
ar1_wf.connect([(inputspec, FLAIR_convert, [('in_flair', 'in_file')])])
124+
ar1_wf.connect([(inputspec, FLAIR_convert, [('FLAIR_file', 'in_file')])])
122125
else:
123126
# longitudinal inputs
124127
inputspec = pe.Node(interface=IdentityInterface(fields=['T1_files',
@@ -144,7 +147,7 @@ def output_names(T1_files):
144147
return iscale_names, lta_names
145148

146149
filenames = pe.Node(Function(['T1_files'],
147-
['iscale_names', 'lta_names']
150+
['iscale_names', 'lta_names'],
148151
output_names),
149152
name="Longitudinal_Filenames")
150153
ar1_wf.connect([(inputspec, filenames, [('T1_files', 'T1_files')])])
@@ -180,11 +183,13 @@ def output_names(T1_files):
180183
"""
181184

182185
def createTemplate(in_files, out_file):
183-
if len(in_files) == 1 and not longitudinal:
184-
print("WARNING: only one run found. This is OK, but motion correction" +
185-
"cannot be performed on one run, so I'll copy the run to rawavg" +
186+
import os
187+
import shutil
188+
if len(in_files) == 1:
189+
print("WARNING: only one run found. This is OK, but motion correction " +
190+
"cannot be performed on one run, so I'll copy the run to rawavg " +
186191
"and continue.")
187-
copy_file(in_files[0], out_file)
192+
shutil.copyfile(in_files[0], out_file)
188193
intensity_scales = None
189194
transforms = None
190195
else:
@@ -193,6 +198,7 @@ def createTemplate(in_files, out_file):
193198
intensity_scales = [os.path.basename(f.replace('.mgz', '-iscale.txt')) for f in in_files]
194199
transforms = [os.path.basename(f.replace('.mgz', '.lta')) for f in in_files]
195200
robtemp = RobustTemplate()
201+
robtemp.inputs.in_files = in_files
196202
robtemp.inputs.average_metric = 'median'
197203
robtemp.inputs.out_file = out_file
198204
robtemp.inputs.no_iteration = True
@@ -385,15 +391,15 @@ def awkfile(in_file, log_file):
385391

386392
ar1_wf.connect([(add_xform_to_orig_nu, mri_em_register, [('out_file', 'in_file')]),
387393
(inputspec, mri_em_register, [('num_threads', 'num_threads'),
388-
('skulltemplate', 'template')])])
394+
('reg_template_withskull', 'template')])])
389395

390396
brainmask = pe.Node(WatershedSkullStrip(),
391397
name='Watershed_Skull_Strip')
392398
brainmask.inputs.t1 = True
393399
brainmask.inputs.out_file = 'brainmask.auto.mgz'
394400
ar1_wf.connect([(mri_normalize, brainmask, [('out_file', 'in_file')]),
395401
(mri_em_register, brainmask, [('out_file', 'transform')]),
396-
(inputspec, brainmask, [('skulltemplate', 'brain_atlas')])])
402+
(inputspec, brainmask, [('reg_template_withskull', 'brain_atlas')])])
397403
else:
398404
copy_template_brainmask = pe.Node(Function(['in_file', 'out_file'],
399405
['out_file'],

nipype/workflows/smri/freesurfer/autorecon2.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def create_AutoRecon2(name="AutoRecon2", longitudinal=False,
128128

129129
ar2_wf.connect([(align_transform, ca_normalize, [('out_file', 'transform')]),
130130
(inputspec, ca_normalize, [('brainmask', 'mask'),
131-
('reg_template', 'template')]),
131+
('reg_template', 'atlas')]),
132132
(add_to_header_nu, ca_normalize, [('out_file', 'in_file')])])
133133

134134
# CA Register
@@ -160,7 +160,7 @@ def create_AutoRecon2(name="AutoRecon2", longitudinal=False,
160160
remove_neck.inputs.out_file = 'nu_noneck.mgz'
161161
ar2_wf.connect([(ca_register, remove_neck, [('out_file', 'transform')]),
162162
(add_to_header_nu, remove_neck, [('out_file', 'in_file')]),
163-
(inputspec, remove_neck, [('reg_template', 'template')]))])
163+
(inputspec, remove_neck, [('reg_template', 'template')])])
164164

165165
# SkullLTA (EM Registration, with Skull)
166166
# Computes transform to align volume mri/nu_noneck.mgz with GCA volume
@@ -339,7 +339,8 @@ def create_AutoRecon2(name="AutoRecon2", longitudinal=False,
339339
'aseg',
340340
't1',
341341
'wm',
342-
'brain']),
342+
'brain',
343+
'num_threads']),
343344
name="inputspec")
344345

345346
if longitudinal:
@@ -475,7 +476,7 @@ def create_AutoRecon2(name="AutoRecon2", longitudinal=False,
475476
if plugin_args:
476477
qsphere.plugin_args = plugin_args
477478
hemi_wf.connect([(inflate1, qsphere, [('out_file', 'in_file')]),
478-
(inputspec, qsphere, [('num_threads', 'num_threads')])])
479+
(hemi_inputspec, qsphere, [('num_threads', 'num_threads')])])
479480

480481
# Automatic Topology Fixer
481482
"""
@@ -607,7 +608,8 @@ def create_AutoRecon2(name="AutoRecon2", longitudinal=False,
607608
(copy_cc, hemi_wf, [('out_file', 'inputspec.aseg')]),
608609
(mri_mask, hemi_wf, [('out_file', 'inputspec.t1')]),
609610
(pretess, hemi_wf, [('out_file', 'inputspec.wm')]),
610-
(normalization2, hemi_wf, [('out_file', 'inputspec.brain')])])
611+
(normalization2, hemi_wf, [('out_file', 'inputspec.brain')]),
612+
(inputspec, hemi_wf, [('num_threads', 'inputspec.num_threads')])])
611613

612614
# Outputs for hemisphere workflow
613615
hemi_outputs=['orig_nofix',

nipype/workflows/smri/freesurfer/autorecon3.py

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ def create_AutoRecon3(name="AutoRecon3", qcache=False, plugin_args=None):
5757
'wm_lookup_table',
5858
'src_subject_id',
5959
'src_subject_dir',
60-
'color_table']),
60+
'color_table',
61+
'num_threads']),
6162
name='inputspec')
6263

6364

@@ -78,7 +79,8 @@ def create_AutoRecon3(name="AutoRecon3", qcache=False, plugin_args=None):
7879
'area',
7980
'curv',
8081
'classifier',
81-
'atlas']),
82+
'atlas',
83+
'num_threads']),
8284
name="inputspec")
8385

8486
# Spherical Inflation
@@ -207,24 +209,25 @@ def create_AutoRecon3(name="AutoRecon3", qcache=False, plugin_args=None):
207209

208210
# Connect the inputs
209211
ar3_wf.connect([(inputspec, hemi_wf, [('{0}_inflated'.format(hemisphere), 'inputspec.inflated'),
210-
('{0}_smoothwm'.format(hemisphere),
211-
'inputspec.smoothwm'),
212-
('{0}_white'.format(hemisphere), 'inputspec.white'),
213-
('{0}_cortex_label'.format(hemisphere),
214-
'inputspec.cortex_label'),
215-
('{0}_orig'.format(hemisphere), 'inputspec.orig'),
216-
('{0}_sulc'.format(hemisphere), 'inputspec.sulc'),
217-
('{0}_area'.format(hemisphere), 'inputspec.area'),
218-
('{0}_curv'.format(hemisphere), 'inputspec.curv'),
219-
('aseg_presurf',
220-
'inputspec.aseg_presurf'),
221-
('brain_finalsurfs',
222-
'inputspec.brain_finalsurfs'),
223-
('wm', 'inputspec.wm'),
224-
('filled', 'inputspec.filled'),
225-
('{0}_atlas'.format(hemisphere), 'inputspec.atlas'),
226-
('{0}_classifier1'.format(hemisphere),
227-
'inputspec.classifier')
212+
('{0}_smoothwm'.format(hemisphere),
213+
'inputspec.smoothwm'),
214+
('{0}_white'.format(hemisphere), 'inputspec.white'),
215+
('{0}_cortex_label'.format(hemisphere),
216+
'inputspec.cortex_label'),
217+
('{0}_orig'.format(hemisphere), 'inputspec.orig'),
218+
('{0}_sulc'.format(hemisphere), 'inputspec.sulc'),
219+
('{0}_area'.format(hemisphere), 'inputspec.area'),
220+
('{0}_curv'.format(hemisphere), 'inputspec.curv'),
221+
('aseg_presurf',
222+
'inputspec.aseg_presurf'),
223+
('brain_finalsurfs',
224+
'inputspec.brain_finalsurfs'),
225+
('wm', 'inputspec.wm'),
226+
('filled', 'inputspec.filled'),
227+
('{0}_atlas'.format(hemisphere), 'inputspec.atlas'),
228+
('{0}_classifier1'.format(hemisphere),
229+
'inputspec.classifier'),
230+
('num_threads', 'inputspec.num_threads')
228231
])
229232
])
230233

@@ -512,8 +515,8 @@ def create_AutoRecon3(name="AutoRecon3", qcache=False, plugin_args=None):
512515
('rawavg', 'inputspec.rawavg'),
513516
('{0}_curv'.format(hemisphere), 'inputspec.curv'),
514517
('{0}_sulc'.format(hemisphere), 'inputspec.sulc'),
515-
('{0}_classifier2'.format(hemisphere), 'classifier2'),
516-
('{0}_classifier3'.format(hemisphere), 'classifier3'),
518+
('{0}_classifier2'.format(hemisphere), 'inputspec.classifier2'),
519+
('{0}_classifier3'.format(hemisphere), 'inputspec.classifier3'),
517520
]),
518521
(ar3_lh_wf1, hemiwf2, [('outputspec.pial', 'inputspec.lh_pial')]),
519522
(ar3_rh_wf1, hemiwf2, [('outputspec.pial', 'inputspec.rh_pial')]),
@@ -709,7 +712,7 @@ def create_AutoRecon3(name="AutoRecon3", qcache=False, plugin_args=None):
709712
('rh_orig', 'inputspec.rh_orig'),
710713
('src_subject_dir', 'inputspec.src_subject_dir'),
711714
('src_subject_id', 'inputspec.src_subject_id'),
712-
('color_table', 'color_table'),
715+
('color_table', 'inputspec.color_table'),
713716
]),
714717
(volume_mask, ba_WF, [('out_ribbon', 'inputspec.ribbon')])
715718
])

0 commit comments

Comments
 (0)