Skip to content

Commit 6a04f6b

Browse files
committed
including optional fieldmap unwarping in rsfmri example workflow
1 parent 48eb213 commit 6a04f6b

File tree

1 file changed

+157
-1
lines changed

1 file changed

+157
-1
lines changed

examples/rsfmri_vol_surface_preprocessing_nipy.py

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,103 @@ def create_reg_workflow(name='registration'):
558558
return register
559559

560560

561+
def create_unwarp_workflow(name='unwarping'):
562+
"""Creates a workflow for unwarping the EPI data prior to
563+
coregistration with Siemens fieldmaps.
564+
565+
Parameters
566+
----------
567+
568+
::
569+
570+
name : name of workflow (default: 'unwarping')
571+
572+
Inputs::
573+
574+
inputspec.realigned_file : realigned time series to unwarp
575+
inputspec.mean_image : mean or median of time series
576+
inputspec.fmap_mag : fieldmap magnitude image
577+
inputspec.fmap_phase : fieldmap phase image
578+
inputspec.delta_te : echo time difference of the fieldmap sequence [ms]
579+
inputspec.pedir : phase encoding direction [x/y/z/x-/y-/z-]
580+
inputspec.echospacing : effective EPI echo spacing (dwell time) [sec]
581+
582+
Outputs::
583+
outputspec.unwarped_mean
584+
outputspec.unwarped_file : unwarped time series
585+
586+
"""
587+
unwarp = Workflow(name=name)
588+
589+
inputnode = Node(interface=IdentityInterface(fields=['realigned_file',
590+
'mean_image',
591+
'fmap_mag',
592+
'fmap_phase',
593+
'delta_te',
594+
'pedir',
595+
'echospacing']),
596+
name='inputspec')
597+
598+
outputnode = Node(interface=IdentityInterface(fields=['unwarped_file',
599+
'unwarped_mean']),
600+
name='outputspec')
601+
602+
# split first magnitude image from mag input file
603+
split = Node(fsl.ExtractROI(t_min=0,
604+
t_size=1),
605+
name='split')
606+
unwarp.connect(inputnode, 'fmap_mag', split, 'in_file')
607+
608+
# skullstrip magnitude image and erode further to get rid of noisy edge voxels
609+
bet = Node(fsl.BET(frac=0.5),
610+
name='bet')
611+
unwarp.connect(split,'roi_file', bet,'in_file')
612+
613+
erode = Node(fsl.maths.ErodeImage(kernel_shape='sphere',
614+
kernel_size=5),
615+
name='erode')
616+
unwarp.connect(bet,'out_file', erode, 'in_file')
617+
618+
# prepare fieldmap
619+
prep_fmap = Node(fsl.epi.PrepareFieldmap(),
620+
name='prepare_fieldmap')
621+
unwarp.connect([(erode, prep_fmap, [('out_file', 'in_magnitude')]),
622+
(inputnode, prep_fmap, [('fmap_phase', 'in_phase'),
623+
('delta_te', 'delta_TE')])
624+
])
625+
626+
# unmask fieldmap to avoid edge effects
627+
fmap_mask = Node(fsl.maths.MathsCommand(args='-abs -bin -fillh'),
628+
name='fmap_mask')
629+
630+
unmask = Node(fsl.FUGUE(save_unmasked_fmap=True),
631+
name='unmask')
632+
633+
unwarp.connect([(prep_fmap, fmap_mask, [('out_fieldmap', 'in_file')]),
634+
(fmap_mask, unmask, [('out_file', 'mask_file')]),
635+
(prep_fmap, unmask,[('out_fieldmap','fmap_in_file')]),
636+
(inputnode, unmask, [('pedir', 'unwarp_direction')])
637+
])
638+
639+
# unwarp epi mean and time series with fieldmap
640+
unwarp_mean = Node(fsl.FUGUE(save_shift=True),name='unwarp_mean')
641+
unwarp_ts = Node(fsl.FUGUE(save_shift=True),name='unwarp_ts')
642+
643+
unwarp.connect([(inputnode, unwarp_mean, [('mean_image', 'in_file'),
644+
('echospacing', 'dwell_time'),
645+
('pedir', 'unwarp_direction')]),
646+
(unmask, unwarp_mean, [('fmap_out_file', 'fmap_in_file')]),
647+
(fmap_mask, unwarp_mean, [('out_file','mask_file')]),
648+
(inputnode, unwarp_ts, [('realigned_file', 'in_file'),
649+
('echospacing', 'dwell_time'),
650+
('pedir', 'unwarp_direction')]),
651+
(unmask, unwarp_ts, [('fmap_out_file', 'fmap_in_file')]),
652+
(fmap_mask, unwarp_ts, [('out_file','mask_file')]),
653+
(unwarp_mean, outputnode, [('unwarped_file', 'unwarped_mean')]),
654+
(unwarp_ts, outputnode, [('unwarped_file', 'unwarped_file')])])
655+
656+
return unwarp
657+
561658
"""
562659
Creates the main preprocessing workflow
563660
"""
@@ -577,6 +674,12 @@ def create_workflow(files,
577674
subjects_dir=None,
578675
sink_directory=os.getcwd(),
579676
target_subject=['fsaverage3', 'fsaverage4'],
677+
do_unwarp=False,
678+
fmap_mag=None,
679+
fmap_phase=None,
680+
delta_te=None,
681+
pedir=None,
682+
echospacing=None,
580683
name='resting'):
581684

582685
wf = Workflow(name=name)
@@ -767,7 +870,33 @@ def merge_files(in1, in2):
767870
maskts = MapNode(fsl.ApplyMask(), iterfield=['in_file'], name='ts_masker')
768871
wf.connect(warpall, 'output_image', maskts, 'in_file')
769872
wf.connect(mask_target, 'out_file', maskts, 'mask_file')
770-
873+
874+
875+
"""
876+
Rewire for optional fieldmap unwarping
877+
"""
878+
if do_unwarp:
879+
unwarping = create_unwarp_workflow(name='unwarping')
880+
unwarping.inputs.inputspec.fmap_mag=fmap_mag
881+
unwarping.inputs.inputspec.fmap_phase=fmap_phase
882+
unwarping.inputs.inputspec.pedir=pedir
883+
unwarping.inputs.inputspec.delta_te=delta_te
884+
unwarping.inputs.inputspec.echospacing=echospacing
885+
886+
wf.disconnect([(calc_median, registration, [('median_file', 'inputspec.mean_image')]),
887+
(realign, art, [('out_file', 'realigned_files')]),
888+
(realign, filter1, [('out_file', 'in_file'),
889+
(('out_file', rename, '_filtermotart'),
890+
'out_res_name')])])
891+
wf.connect([(realign, unwarping, [('out_file', 'inputspec.realigned_file')]),
892+
(calc_median, unwarping, [('median_file', 'inputspec.mean_image')]),
893+
(unwarping, registration, [('outputspec.unwarped_mean', 'inputspec.mean_image')]),
894+
(unwarping, art, [('outputspec.unwarped_file', 'realigned_files')]),
895+
(unwarping, filter1, [('outputspec.unwarped_file', 'in_file'),
896+
(('outputspec.unwarped_file', rename, '_filtermotart'),
897+
'out_res_name')])])
898+
899+
771900
# map to surface
772901
# extract aparc+aseg ROIs
773902
# extract subcortical ROIs
@@ -945,6 +1074,12 @@ def create_resting_workflow(args, name=None):
9451074
if args.dicom_file:
9461075
TR, slice_times, slice_thickness = get_info(args.dicom_file)
9471076
slice_times = (np.array(slice_times)/1000.).tolist()
1077+
if args.do_unwarp:
1078+
fmap_mag=os.path.abspath(args.fmap_mag)
1079+
fmap_phase=os.path.abspath(args.fmap_phase)
1080+
else:
1081+
fmap_mag=None
1082+
fmap_phase=None
9481083

9491084
if name is None:
9501085
name = 'resting_' + args.subject_id
@@ -961,6 +1096,12 @@ def create_resting_workflow(args, name=None):
9611096
lowpass_freq=args.lowpass_freq,
9621097
highpass_freq=args.highpass_freq,
9631098
sink_directory=os.path.abspath(args.sink),
1099+
do_unwarp=args.do_unwarp,
1100+
fmap_mag=fmap_mag,
1101+
fmap_phase=fmap_phase,
1102+
delta_te=args.delta_te,
1103+
pedir=args.pedir,
1104+
echospacing=args.echospacing,
9641105
name=name)
9651106
wf = create_workflow(**kwargs)
9661107
return wf
@@ -1010,6 +1151,21 @@ def create_resting_workflow(args, name=None):
10101151
help="Plugin to use")
10111152
parser.add_argument("--plugin_args", dest="plugin_args",
10121153
help="Plugin arguments")
1154+
parser.add_argument("--do_unwarp", dest="do_unwarp",
1155+
default=False,
1156+
help="Whether fieldmap unwarping should be used")
1157+
parser.add_argument("--fmap_mag", dest="fmap_mag", default=None,
1158+
help="fieldmap magnitude image")
1159+
parser.add_argument("--fmap_phase", dest="fmap_phase", default=None,
1160+
help="fieldmap phase image")
1161+
parser.add_argument("--delta_te", dest="delta_te",
1162+
default=None, type=float,
1163+
help="echo time difference of the fieldmap sequence [ms]")
1164+
parser.add_argument("--pedir", dest="pedir", default=None,
1165+
help="phase encoding direction [x/y/z/x-/y-/z-]")
1166+
parser.add_argument("--echospacing", dest="echospacing",
1167+
default=None, type=float,
1168+
help="echo time difference of the fieldmap sequence [ms]")
10131169
args = parser.parse_args()
10141170

10151171
wf = create_resting_workflow(args)

0 commit comments

Comments
 (0)