def init_phdiff_wf(omp_nthreads, name='phdiff_wf'): r""" Distortion correction of EPI sequences using phase-difference maps. Estimates the fieldmap using a phase-difference image and one or more magnitude images corresponding to two or more :abbr:`GRE (Gradient Echo sequence)` acquisitions. The most delicate bit of this workflow is the phase-unwrapping process: phase maps are clipped in the range :math:`[0 \dotsb 2\pi )`. To find the integer number of offsets that make a region continously smooth with its neighbour, FSL PRELUDE is run [Jenkinson2003]_. FSL PRELUDE takes wrapped maps in the range 0 to 6.28, `as per the user guide <https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FUGUE/Guide#Step_2_-_Getting_.28wrapped.29_phase_in_radians>`__. For the phase-difference maps, recentering back to :math:`[-\pi \dotsb \pi )` is necessary. After some massaging and the application of the effective echo spacing parameter, the phase-difference maps can be converted into a *B0 field map* in Hz units. The `original code was taken from nipype <https://github.com/nipy/nipype/blob/0.12.1/nipype/workflows/dmri/fsl/artifacts.py#L514>`_. Workflow Graph .. workflow :: :graph2use: orig :simple_form: yes from sdcflows.workflows.phdiff import init_phdiff_wf wf = init_phdiff_wf(omp_nthreads=1) Parameters ---------- omp_nthreads : int Maximum number of threads an individual process may use Inputs ------ magnitude : list of os.pathlike List of path(s) the GRE magnitude maps. phasediff : list of tuple(os.pathlike, dict) List containing one GRE phase-difference map with its corresponding metadata (requires ``EchoTime1`` and ``EchoTime2``), or the phase maps for the two subsequent echoes, with their metadata (requires ``EchoTime``). Outputs ------- fmap_ref : pathlike The average magnitude image, skull-stripped fmap_mask : pathlike The brain mask applied to the fieldmap fmap : pathlike The estimated fieldmap in Hz References ---------- .. [Jenkinson2003] Jenkinson, M. (2003) Fast, automated, N-dimensional phase-unwrapping algorithm. MRM 49(1):193-197. doi:`10.1002/mrm.10354 <10.1002/mrm.10354>`__. """ workflow = Workflow(name=name) workflow.__desc__ = """\ A B0-nonuniformity map (or *fieldmap*) was estimated based on a phase-difference map calculated with a dual-echo GRE (gradient-recall echo) sequence, processed with a custom workflow of *SDCFlows* inspired by the [`epidewarp.fsl` script](http://www.nmr.mgh.harvard.edu/~greve/fbirn/b0/epidewarp.fsl) and further improvements in HCP Pipelines [@hcppipelines]. """ inputnode = pe.Node(niu.IdentityInterface(fields=['magnitude', 'phasediff']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['fmap', 'fmap_ref', 'fmap_mask']), name='outputnode') split = pe.MapNode(niu.Function(function=_split, output_names=['map_file', 'meta']), iterfield=['phasediff'], run_without_submitting=True, name='split') magnitude_wf = init_magnitude_wf(omp_nthreads=omp_nthreads) # phase diff -> radians phmap2rads = pe.MapNode(PhaseMap2rads(), name='phmap2rads', iterfield=['in_file'], run_without_submitting=True) # FSL PRELUDE will perform phase-unwrapping prelude = pe.Node(fsl.PRELUDE(), name='prelude') calc_phdiff = pe.Node(SubtractPhases(), name='calc_phdiff', run_without_submitting=True) fmap_postproc_wf = init_fmap_postproc_wf(omp_nthreads=omp_nthreads, fmap_bspline=False) compfmap = pe.Node(Phasediff2Fieldmap(), name='compfmap') workflow.connect([ (inputnode, split, [('phasediff', 'phasediff')]), (inputnode, magnitude_wf, [('magnitude', 'inputnode.magnitude')]), (magnitude_wf, prelude, [('outputnode.fmap_ref', 'magnitude_file'), ('outputnode.fmap_mask', 'mask_file')]), (split, phmap2rads, [('map_file', 'in_file')]), (phmap2rads, calc_phdiff, [('out_file', 'in_phases')]), (split, calc_phdiff, [('meta', 'in_meta')]), (calc_phdiff, prelude, [('phase_diff', 'phase_file')]), (prelude, fmap_postproc_wf, [('unwrapped_phase_file', 'inputnode.fmap')]), (calc_phdiff, fmap_postproc_wf, [('metadata', 'inputnode.metadata')]), (magnitude_wf, fmap_postproc_wf, [ ('outputnode.fmap_mask', 'inputnode.fmap_mask'), ('outputnode.fmap_ref', 'inputnode.fmap_ref')]), (fmap_postproc_wf, compfmap, [('outputnode.out_fmap', 'in_file'), ('outputnode.metadata', 'metadata')]), (compfmap, outputnode, [('out_file', 'fmap')]), (magnitude_wf, outputnode, [('outputnode.fmap_ref', 'fmap_ref'), ('outputnode.fmap_mask', 'fmap_mask')]), ]) return workflow
def init_fmap_wf(omp_nthreads, fmap_bspline, name='fmap_wf'): """ Fieldmap workflow - when we have a sequence that directly measures the fieldmap we just need to mask it (using the corresponding magnitude image) to remove the noise in the surrounding air region, and ensure that units are Hz. .. workflow :: :graph2use: orig :simple_form: yes from fmriprep.workflows.fieldmap.fmap import init_fmap_wf wf = init_fmap_wf(omp_nthreads=6, fmap_bspline=False) """ workflow = pe.Workflow(name=name) inputnode = pe.Node( niu.IdentityInterface(fields=['magnitude', 'fieldmap']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['fmap', 'fmap_ref', 'fmap_mask']), name='outputnode') # Merge input magnitude images magmrg = pe.Node(IntraModalMerge(), name='magmrg') # Merge input fieldmap images fmapmrg = pe.Node(IntraModalMerge(zero_based_avg=False, hmc=False), name='fmapmrg') # de-gradient the fields ("bias/illumination artifact") n4_correct = pe.Node(ants.N4BiasFieldCorrection(dimension=3, copy_header=True), name='n4_correct', n_procs=omp_nthreads) bet = pe.Node(BETRPT(generate_report=True, frac=0.6, mask=True), name='bet') ds_fmap_mask = pe.Node(DerivativesDataSink(suffix='fmap_mask'), name='ds_report_fmap_mask', run_without_submitting=True) workflow.connect([ (inputnode, magmrg, [('magnitude', 'in_files')]), (inputnode, fmapmrg, [('fieldmap', 'in_files')]), (magmrg, n4_correct, [('out_file', 'input_image')]), (n4_correct, bet, [('output_image', 'in_file')]), (bet, outputnode, [('mask_file', 'fmap_mask'), ('out_file', 'fmap_ref')]), (inputnode, ds_fmap_mask, [('fieldmap', 'source_file')]), (bet, ds_fmap_mask, [('out_report', 'in_file')]), ]) if fmap_bspline: # despike_threshold=1.0, mask_erode=1), fmapenh = pe.Node(FieldEnhance(unwrap=False, despike=False), name='fmapenh', mem_gb=4, n_procs=omp_nthreads) workflow.connect([ (bet, fmapenh, [('mask_file', 'in_mask'), ('out_file', 'in_magnitude')]), (fmapmrg, fmapenh, [('out_file', 'in_file')]), (fmapenh, outputnode, [('out_file', 'fmap')]), ]) else: torads = pe.Node(FieldToRadS(), name='torads') prelude = pe.Node(fsl.PRELUDE(), name='prelude') tohz = pe.Node(FieldToHz(), name='tohz') denoise = pe.Node(fsl.SpatialFilter(operation='median', kernel_shape='sphere', kernel_size=3), name='denoise') demean = pe.Node(niu.Function(function=demean_image), name='demean') cleanup_wf = cleanup_edge_pipeline(name='cleanup_wf') applymsk = pe.Node(fsl.ApplyMask(), name='applymsk') workflow.connect([ (bet, prelude, [('mask_file', 'mask_file'), ('out_file', 'magnitude_file')]), (fmapmrg, torads, [('out_file', 'in_file')]), (torads, tohz, [('fmap_range', 'range_hz')]), (torads, prelude, [('out_file', 'phase_file')]), (prelude, tohz, [('unwrapped_phase_file', 'in_file')]), (tohz, denoise, [('out_file', 'in_file')]), (denoise, demean, [('out_file', 'in_file')]), (demean, cleanup_wf, [('out', 'inputnode.in_file')]), (bet, cleanup_wf, [('mask_file', 'inputnode.in_mask')]), (cleanup_wf, applymsk, [('outputnode.out_file', 'in_file')]), (bet, applymsk, [('mask_file', 'mask_file')]), (applymsk, outputnode, [('out_file', 'fmap')]), ]) return workflow
def sdc_fmb(name='fmb_correction', interp='Linear', fugue_params=dict(smooth3d=2.0)): """ SDC stands for susceptibility distortion correction. FMB stands for fieldmap-based. The fieldmap based (FMB) method implements SDC by using a mapping of the B0 field as proposed by [Jezzard95]_. This workflow uses the implementation of FSL (`FUGUE <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FUGUE>`_). Phase unwrapping is performed using `PRELUDE <http://fsl.fmrib.ox.ac.uk/fsl/fsl-4.1.9/fugue/prelude.html>`_ [Jenkinson03]_. Preparation of the fieldmap is performed reproducing the script in FSL `fsl_prepare_fieldmap <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FUGUE/Guide#SIEMENS_data>`_. Example ------- >>> from nipype.workflows.dmri.fsl.artifacts import sdc_fmb >>> fmb = sdc_fmb() >>> fmb.inputs.inputnode.in_file = 'diffusion.nii' >>> fmb.inputs.inputnode.in_ref = list(range(0, 30, 6)) >>> fmb.inputs.inputnode.in_mask = 'mask.nii' >>> fmb.inputs.inputnode.bmap_mag = 'magnitude.nii' >>> fmb.inputs.inputnode.bmap_pha = 'phase.nii' >>> fmb.inputs.inputnode.settings = 'epi_param.txt' >>> fmb.run() # doctest: +SKIP .. warning:: Only SIEMENS format fieldmaps are supported. .. admonition:: References .. [Jezzard95] Jezzard P, and Balaban RS, `Correction for geometric distortion in echo planar images from B0 field variations <https://doi.org/10.1002/mrm.1910340111>`_, MRM 34(1):65-73. (1995). doi: 10.1002/mrm.1910340111. .. [Jenkinson03] Jenkinson M., `Fast, automated, N-dimensional phase-unwrapping algorithm <https://doi.org/10.1002/mrm.10354>`_, MRM 49(1):193-197, 2003, doi: 10.1002/mrm.10354. """ epi_defaults = { 'delta_te': 2.46e-3, 'echospacing': 0.77e-3, 'acc_factor': 2, 'enc_dir': u'AP' } inputnode = pe.Node(niu.IdentityInterface(fields=[ 'in_file', 'in_ref', 'in_mask', 'bmap_pha', 'bmap_mag', 'settings' ]), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['out_file', 'out_vsm', 'out_warp']), name='outputnode') r_params = pe.Node(JSONFileGrabber(defaults=epi_defaults), name='SettingsGrabber') eff_echo = pe.Node(niu.Function(function=_eff_t_echo, input_names=['echospacing', 'acc_factor'], output_names=['eff_echo']), name='EffEcho') firstmag = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name='GetFirst') n4 = pe.Node(ants.N4BiasFieldCorrection(dimension=3), name='Bias') bet = pe.Node(fsl.BET(frac=0.4, mask=True), name='BrainExtraction') dilate = pe.Node(fsl.maths.MathsCommand(nan2zeros=True, args='-kernel sphere 5 -dilM'), name='MskDilate') pha2rads = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_file'], function=siemens2rads), name='PreparePhase') prelude = pe.Node(fsl.PRELUDE(process3d=True), name='PhaseUnwrap') rad2rsec = pe.Node(niu.Function(input_names=['in_file', 'delta_te'], output_names=['out_file'], function=rads2radsec), name='ToRadSec') baseline = pe.Node(niu.Function(input_names=['in_file', 'index'], output_names=['out_file'], function=time_avg), name='Baseline') fmm2b0 = pe.Node(ants.Registration(output_warped_image=True), name="FMm_to_B0") fmm2b0.inputs.transforms = ['Rigid'] * 2 fmm2b0.inputs.transform_parameters = [(1.0, )] * 2 fmm2b0.inputs.number_of_iterations = [[50], [20]] fmm2b0.inputs.dimension = 3 fmm2b0.inputs.metric = ['Mattes', 'Mattes'] fmm2b0.inputs.metric_weight = [1.0] * 2 fmm2b0.inputs.radius_or_number_of_bins = [64, 64] fmm2b0.inputs.sampling_strategy = ['Regular', 'Random'] fmm2b0.inputs.sampling_percentage = [None, 0.2] fmm2b0.inputs.convergence_threshold = [1.e-5, 1.e-8] fmm2b0.inputs.convergence_window_size = [20, 10] fmm2b0.inputs.smoothing_sigmas = [[6.0], [2.0]] fmm2b0.inputs.sigma_units = ['vox'] * 2 fmm2b0.inputs.shrink_factors = [[6], [1]] # ,[1] ] fmm2b0.inputs.use_estimate_learning_rate_once = [True] * 2 fmm2b0.inputs.use_histogram_matching = [True] * 2 fmm2b0.inputs.initial_moving_transform_com = 0 fmm2b0.inputs.collapse_output_transforms = True fmm2b0.inputs.winsorize_upper_quantile = 0.995 applyxfm = pe.Node(ants.ApplyTransforms(dimension=3, interpolation=interp), name='FMp_to_B0') pre_fugue = pe.Node(fsl.FUGUE(save_fmap=True), name='PreliminaryFugue') demean = pe.Node(niu.Function(input_names=['in_file', 'in_mask'], output_names=['out_file'], function=demean_image), name='DemeanFmap') cleanup = cleanup_edge_pipeline() addvol = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_file'], function=add_empty_vol), name='AddEmptyVol') vsm = pe.Node(fsl.FUGUE(save_shift=True, **fugue_params), name="ComputeVSM") split = pe.Node(fsl.Split(dimension='t'), name='SplitDWIs') merge = pe.Node(fsl.Merge(dimension='t'), name='MergeDWIs') unwarp = pe.MapNode(fsl.FUGUE(icorr=True, forward_warping=False), iterfield=['in_file'], name='UnwarpDWIs') thres = pe.MapNode(fsl.Threshold(thresh=0.0), iterfield=['in_file'], name='RemoveNegative') vsm2dfm = vsm2warp() vsm2dfm.inputs.inputnode.scaling = 1.0 wf = pe.Workflow(name=name) wf.connect([(inputnode, r_params, [('settings', 'in_file')]), (r_params, eff_echo, [('echospacing', 'echospacing'), ('acc_factor', 'acc_factor')]), (inputnode, pha2rads, [('bmap_pha', 'in_file')]), (inputnode, firstmag, [('bmap_mag', 'in_file')]), (inputnode, baseline, [('in_file', 'in_file'), ('in_ref', 'index')]), (firstmag, n4, [('roi_file', 'input_image')]), (n4, bet, [('output_image', 'in_file')]), (bet, dilate, [('mask_file', 'in_file')]), (pha2rads, prelude, [('out_file', 'phase_file')]), (n4, prelude, [('output_image', 'magnitude_file')]), (dilate, prelude, [('out_file', 'mask_file')]), (r_params, rad2rsec, [('delta_te', 'delta_te')]), (prelude, rad2rsec, [('unwrapped_phase_file', 'in_file')]), (baseline, fmm2b0, [('out_file', 'fixed_image')]), (n4, fmm2b0, [('output_image', 'moving_image')]), (inputnode, fmm2b0, [('in_mask', 'fixed_image_mask')]), (dilate, fmm2b0, [('out_file', 'moving_image_mask')]), (baseline, applyxfm, [('out_file', 'reference_image')]), (rad2rsec, applyxfm, [('out_file', 'input_image')]), (fmm2b0, applyxfm, [('forward_transforms', 'transforms'), ('forward_invert_flags', 'invert_transform_flags')]), (applyxfm, pre_fugue, [('output_image', 'fmap_in_file')]), (inputnode, pre_fugue, [('in_mask', 'mask_file')]), (pre_fugue, demean, [('fmap_out_file', 'in_file')]), (inputnode, demean, [('in_mask', 'in_mask')]), (demean, cleanup, [('out_file', 'inputnode.in_file')]), (inputnode, cleanup, [('in_mask', 'inputnode.in_mask')]), (cleanup, addvol, [('outputnode.out_file', 'in_file')]), (inputnode, vsm, [('in_mask', 'mask_file')]), (addvol, vsm, [('out_file', 'fmap_in_file')]), (r_params, vsm, [('delta_te', 'asym_se_time')]), (eff_echo, vsm, [('eff_echo', 'dwell_time')]), (inputnode, split, [('in_file', 'in_file')]), (split, unwarp, [('out_files', 'in_file')]), (vsm, unwarp, [('shift_out_file', 'shift_in_file')]), (r_params, unwarp, [(('enc_dir', _fix_enc_dir), 'unwarp_direction')]), (unwarp, thres, [('unwarped_file', 'in_file')]), (thres, merge, [('out_file', 'in_files')]), (r_params, vsm2dfm, [(('enc_dir', _fix_enc_dir), 'inputnode.enc_dir')]), (merge, vsm2dfm, [('merged_file', 'inputnode.in_ref')]), (vsm, vsm2dfm, [('shift_out_file', 'inputnode.in_vsm')]), (merge, outputnode, [('merged_file', 'out_file')]), (vsm, outputnode, [('shift_out_file', 'out_vsm')]), (vsm2dfm, outputnode, [('outputnode.out_warp', 'out_warp')])]) return wf
# compute median of realigned timeseries for preperation for fieldmap median1 = Node(util.Function(input_names=['in_files'], output_names=['median_file'], function=median), name='median1') #median = Node(SpatialFilter(operation='median'), # name='median') preproc.connect([(tsnr, median1, [('detrended_file', 'in_files')])]) #prelude phase unwrapping x 2 prelude1 = Node(fsl.PRELUDE(terminal_output='none'), name='prelude1') preproc.connect([(selectfiles, prelude1, [('phase1', 'phase_file')]), (brain_extract_mag1, prelude1, [('out_file', 'magnitude_file') ])]) prelude2 = Node(fsl.PRELUDE(terminal_output='none'), name='prelude2') preproc.connect([(selectfiles, prelude2, [('phase2', 'phase_file')]), (brain_extract_mag1, prelude2, [('out_file', 'magnitude_file') ])]) # Getting the fieldmap in rad/s ---- fusing phase images -sub -mul 1000 -div 5,10 phase_maths_sub = Node(fsl.BinaryMaths(operation='sub'), name='phase_maths_sub')
def susceptibility_distortion_correction_using_phasediff_fmap( fugue_params=dict(smooth3d=2.0), register_fmap_on_b0=True, name='susceptibility_distortion_correction_using_phasediff_fmap'): """ The fieldmap based method implements susceptibility distortion correction by using a mapping of the B0 field as proposed by [Jezzard95]_. This workflow uses the implementation of FSL (`FUGUE <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FUGUE>`_). Phase unwrapping is performed using `PRELUDE <http://fsl.fmrib.ox.ac.uk/fsl/fsl-4.1.9/fugue/prelude.html>`_ [Jenkinson03]_. Preparation of the fieldmap is performed reproducing the script in FSL `fsl_prepare_fieldmap <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FUGUE/Guide#SIEMENS_data>`_. Args: fugue_params: Regularisation to apply to the fieldmap with a 3D Gaussian smoothing (default: 2.0) register_fmap_on_b0: Register the fmap on the b0 or not. If the fmap is already well aligned and name: Inputnode in_file (str): DWI dataset. in_bval (str): Bval file. in_mask (str): Mask file. in_fmap_magnitude (str): Magnitude fieldmap. Chose the fmap with the best contrast. in_fmap_phasediff (str): Phase difference fieldmap. delta_echo_time (float): Difference of echo time of the effective_echo_spacing (float): phase_encoding_direction (str): Outputnode: out_file (str): Output. out_vsm (str): The set of DWI volumes. out_warp (str): The bvalues corresponding to the out_dwi. .. warning:: Only SIEMENS format fieldmaps are supported. .. admonition:: References < .. [Jezzard95] Jezzard P, and Balaban RS, `Correction for geometric distortion in echo planar images from B0 field variations <http://dx.doi.org/10.1002/mrm.1910340111>`_, MRM 34(1):65-73. (1995). doi: 10.1002/mrm.1910340111. .. [Jenkinson03] Jenkinson M., `Fast, automated, N-dimensional phase-unwrapping algorithm <http://dx.doi.org/10.1002/mrm.10354>`_, MRM 49(1):193-197, 2003, doi: 10.1002/mrm.10354. """ import nipype.pipeline.engine as pe import nipype.interfaces.utility as niu import nipype.interfaces.fsl as fsl import nipype.interfaces.ants as ants from clinica.utils.epi import bids_dir_to_fsl_dir from clinica.utils.fmap import resample_fmap_to_b0 from nipype.workflows.dmri.fsl.utils import (rads2radsec, siemens2rads, vsm2warp, add_empty_vol, cleanup_edge_pipeline, demean_image) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'in_dwi', 'in_mask', 'in_fmap_phasediff', 'in_fmap_magnitude', 'delta_echo_time', 'effective_echo_spacing', 'phase_encoding_direction' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['out_file', 'out_vsm', 'out_warp', 'out_prepared_fmap']), name='outputnode') fsl_dir = pe.Node(niu.Function(input_names=['bids_dir'], output_names=['fsl_dir'], function=bids_dir_to_fsl_dir), name='ConvertToFslDir') get_b0 = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name='GetB0') first_mag = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name='GetFirst') n4 = pe.Node(ants.N4BiasFieldCorrection(dimension=3), name='N4MagnitudeFmap') bet = pe.Node(fsl.BET(frac=0.4, mask=True), name='BetN4MagnitudeFmap') dilate = pe.Node(fsl.maths.MathsCommand(nan2zeros=True, args='-kernel sphere 5 -dilM'), name='DilateBet') pha2rads = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_file'], function=siemens2rads), name='PreparePhase') prelude = pe.Node(fsl.PRELUDE(process3d=True), name='PhaseUnwrap') rad2rsec = pe.Node(niu.Function(input_names=['in_file', 'delta_te'], output_names=['out_file'], function=rads2radsec), name='ToRadSec') # Node when we register the fmap onto the b0 fmm2b0 = pe.Node(ants.Registration(output_warped_image=True), name="FmapMagnitudeToB0") fmm2b0.inputs.transforms = ['Rigid'] * 2 fmm2b0.inputs.transform_parameters = [(1.0, )] * 2 fmm2b0.inputs.number_of_iterations = [[50], [20]] fmm2b0.inputs.dimension = 3 fmm2b0.inputs.metric = ['Mattes', 'Mattes'] fmm2b0.inputs.metric_weight = [1.0] * 2 fmm2b0.inputs.radius_or_number_of_bins = [64, 64] fmm2b0.inputs.sampling_strategy = ['Regular', 'Random'] fmm2b0.inputs.sampling_percentage = [None, 0.2] fmm2b0.inputs.convergence_threshold = [1.e-5, 1.e-8] fmm2b0.inputs.convergence_window_size = [20, 10] fmm2b0.inputs.smoothing_sigmas = [[6.0], [2.0]] fmm2b0.inputs.sigma_units = ['vox'] * 2 fmm2b0.inputs.shrink_factors = [[6], [1]] # ,[1] ] fmm2b0.inputs.use_estimate_learning_rate_once = [True] * 2 fmm2b0.inputs.use_histogram_matching = [True] * 2 fmm2b0.inputs.initial_moving_transform_com = 0 fmm2b0.inputs.collapse_output_transforms = True fmm2b0.inputs.winsorize_upper_quantile = 0.995 # Node when we resample the fmap onto the b0 res_fmap = pe.Node(niu.Function( input_names=['in_fmap', 'in_b0', 'out_file'], output_names=['out_resampled_fmap'], function=resample_fmap_to_b0), name='ResampleFmap') apply_xfm = pe.Node(ants.ApplyTransforms(dimension=3, interpolation='BSpline'), name='FmapPhaseToB0') pre_fugue = pe.Node(fsl.FUGUE(save_fmap=True), name='PreliminaryFugue') demean = pe.Node(niu.Function(input_names=['in_file', 'in_mask'], output_names=['out_file'], function=demean_image), name='DemeanFmap') cleanup = cleanup_edge_pipeline() add_vol = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_file'], function=add_empty_vol), name='AddEmptyVol') vsm = pe.Node(fsl.FUGUE(save_shift=True, **fugue_params), name="ComputeVSM") split = pe.Node(fsl.Split(dimension='t'), name='SplitDWIs') merge = pe.Node(fsl.Merge(dimension='t'), name='MergeDWIs') unwarp = pe.MapNode(fsl.FUGUE(icorr=True, forward_warping=False), iterfield=['in_file'], name='UnwarpDWIs') thres = pe.MapNode(fsl.Threshold(thresh=0.0), iterfield=['in_file'], name='RemoveNegative') vsm2dfm = vsm2warp() vsm2dfm.inputs.inputnode.scaling = 1.0 wf = pe.Workflow(name=name) wf.connect([ (inputnode, fsl_dir, [('phase_encoding_direction', 'bids_dir') ]), # noqa (inputnode, pha2rads, [('in_fmap_phasediff', 'in_file')]), # noqa (inputnode, get_b0, [('in_dwi', 'in_file')]), # noqa (inputnode, first_mag, [('in_fmap_magnitude', 'in_file')]), # noqa (first_mag, n4, [('roi_file', 'input_image')]), # noqa (n4, bet, [('output_image', 'in_file')]), # noqa (bet, dilate, [('mask_file', 'in_file')]), # noqa (pha2rads, prelude, [('out_file', 'phase_file')]), # noqa (n4, prelude, [('output_image', 'magnitude_file')]), # noqa (dilate, prelude, [('out_file', 'mask_file')]), # noqa (prelude, rad2rsec, [('unwrapped_phase_file', 'in_file')]), # noqa (inputnode, rad2rsec, [('delta_echo_time', 'delta_te')]), # noqa ]) if register_fmap_on_b0: wf.connect([ (get_b0, fmm2b0, [('roi_file', 'fixed_image')]), # noqa (n4, fmm2b0, [('output_image', 'moving_image')]), # noqa (inputnode, fmm2b0, [('in_mask', 'fixed_image_mask')]), # noqa (dilate, fmm2b0, [('out_file', 'moving_image_mask')]), # noqa (get_b0, apply_xfm, [('roi_file', 'reference_image')]), # noqa (rad2rsec, apply_xfm, [('out_file', 'input_image')]), # noqa ( fmm2b0, apply_xfm, [ ('forward_transforms', 'transforms'), # noqa ('forward_invert_flags', 'invert_transform_flags') ]), # noqa (apply_xfm, pre_fugue, [('output_image', 'fmap_in_file')]), # noqa (inputnode, pre_fugue, [('in_mask', 'mask_file')]), # noqa (apply_xfm, outputnode, [('output_image', 'out_prepared_fmap')] ) # noqa ]) else: wf.connect([ (get_b0, res_fmap, [('roi_file', 'in_b0')]), # noqa (rad2rsec, res_fmap, [('out_file', 'in_fmap')]), # noqa (res_fmap, pre_fugue, [('out_resampled_fmap', 'fmap_in_file') ]), # noqa (inputnode, pre_fugue, [('in_mask', 'mask_file')]), # noqa (res_fmap, outputnode, [('out_resampled_fmap', 'out_prepared_fmap') ]) # noqa ]) wf.connect([ (pre_fugue, demean, [('fmap_out_file', 'in_file')]), # noqa (inputnode, demean, [('in_mask', 'in_mask')]), # noqa (demean, cleanup, [('out_file', 'inputnode.in_file')]), # noqa (inputnode, cleanup, [('in_mask', 'inputnode.in_mask')]), # noqa (cleanup, add_vol, [('outputnode.out_file', 'in_file')]), # noqa (inputnode, vsm, [('in_mask', 'mask_file')]), # noqa (add_vol, vsm, [('out_file', 'fmap_in_file')]), # noqa (inputnode, vsm, [('delta_echo_time', 'asym_se_time')]), # noqa (inputnode, vsm, [('effective_echo_spacing', 'dwell_time')]), # noqa (inputnode, split, [('in_dwi', 'in_file')]), # noqa (split, unwarp, [('out_files', 'in_file')]), # noqa (vsm, unwarp, [('shift_out_file', 'shift_in_file')]), # noqa (fsl_dir, unwarp, [('fsl_dir', 'unwarp_direction')]), # noqa (unwarp, thres, [('unwarped_file', 'in_file')]), # noqa (thres, merge, [('out_file', 'in_files')]), # noqa (merge, vsm2dfm, [('merged_file', 'inputnode.in_ref')]), # noqa (vsm, vsm2dfm, [('shift_out_file', 'inputnode.in_vsm')]), # noqa (fsl_dir, vsm2dfm, [('fsl_dir', 'inputnode.enc_dir')]), # noqa (rad2rsec, outputnode, [('out_file', 'out_native_fmap')]), # noqa (merge, outputnode, [('merged_file', 'out_file')]), # noqa (vsm, outputnode, [('shift_out_file', 'out_vsm')]), # noqa (vsm2dfm, outputnode, [('outputnode.out_warp', 'out_warp')]), # noqa ]) return wf
def init_phdiff_wf(omp_nthreads, name='phdiff_wf'): """ Distortion correction of EPI sequences using phase-difference maps. Estimates the fieldmap using a phase-difference image and one or more magnitude images corresponding to two or more :abbr:`GRE (Gradient Echo sequence)` acquisitions. The `original code was taken from nipype <https://github.com/nipy/nipype/blob/master/nipype/workflows/dmri/fsl/artifacts.py#L514>`_. .. workflow :: :graph2use: orig :simple_form: yes from sdcflows.workflows.phdiff import init_phdiff_wf wf = init_phdiff_wf(omp_nthreads=1) **Parameters**: omp_nthreads : int Maximum number of threads an individual process may use **Inputs**: magnitude : pathlike Path to the corresponding magnitude path(s). phasediff : pathlike Path to the corresponding phase-difference file. metadata : dict Metadata dictionary corresponding to the phasediff input **Outputs**: fmap_ref : pathlike The average magnitude image, skull-stripped fmap_mask : pathlike The brain mask applied to the fieldmap fmap : pathlike The estimated fieldmap in Hz """ workflow = Workflow(name=name) workflow.__desc__ = """\ A deformation field to correct for susceptibility distortions was estimated based on a field map that was co-registered to the BOLD reference, using a custom workflow of *fMRIPrep* derived from D. Greve's `epidewarp.fsl` [script](http://www.nmr.mgh.harvard.edu/~greve/fbirn/b0/epidewarp.fsl) and further improvements of HCP Pipelines [@hcppipelines]. """ inputnode = pe.Node( niu.IdentityInterface(fields=['magnitude', 'phasediff', 'metadata']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['fmap', 'fmap_ref', 'fmap_mask']), name='outputnode') # Merge input magnitude images magmrg = pe.Node(IntraModalMerge(), name='magmrg') # de-gradient the fields ("bias/illumination artifact") n4 = pe.Node(ants.N4BiasFieldCorrection(dimension=3, copy_header=True), name='n4', n_procs=omp_nthreads) bet = pe.Node(BETRPT(generate_report=True, frac=0.6, mask=True), name='bet') # uses mask from bet; outputs a mask # dilate = pe.Node(fsl.maths.MathsCommand( # nan2zeros=True, args='-kernel sphere 5 -dilM'), name='MskDilate') # phase diff -> radians pha2rads = pe.Node(niu.Function(function=siemens2rads), name='pha2rads') # FSL PRELUDE will perform phase-unwrapping prelude = pe.Node(fsl.PRELUDE(), name='prelude') denoise = pe.Node(fsl.SpatialFilter(operation='median', kernel_shape='sphere', kernel_size=3), name='denoise') demean = pe.Node(niu.Function(function=demean_image), name='demean') cleanup_wf = cleanup_edge_pipeline(name="cleanup_wf") compfmap = pe.Node(Phasediff2Fieldmap(), name='compfmap') # The phdiff2fmap interface is equivalent to: # rad2rsec (using rads2radsec from nipype.workflows.dmri.fsl.utils) # pre_fugue = pe.Node(fsl.FUGUE(save_fmap=True), name='ComputeFieldmapFUGUE') # rsec2hz (divide by 2pi) workflow.connect([ (inputnode, compfmap, [('metadata', 'metadata')]), (inputnode, magmrg, [('magnitude', 'in_files')]), (magmrg, n4, [('out_avg', 'input_image')]), (n4, prelude, [('output_image', 'magnitude_file')]), (n4, bet, [('output_image', 'in_file')]), (bet, prelude, [('mask_file', 'mask_file')]), (inputnode, pha2rads, [('phasediff', 'in_file')]), (pha2rads, prelude, [('out', 'phase_file')]), (prelude, denoise, [('unwrapped_phase_file', 'in_file')]), (denoise, demean, [('out_file', 'in_file')]), (demean, cleanup_wf, [('out', 'inputnode.in_file')]), (bet, cleanup_wf, [('mask_file', 'inputnode.in_mask')]), (cleanup_wf, compfmap, [('outputnode.out_file', 'in_file')]), (compfmap, outputnode, [('out_file', 'fmap')]), (bet, outputnode, [('mask_file', 'fmap_mask'), ('out_file', 'fmap_ref')]), ]) return workflow
def make_t1_fieldmap(name='make_t1_fieldmap'): inputnode = pe.Node(utility.IdentityInterface(fields=[ 'complex_image', 'magnitude_image', 't1_mask', 't1_mag', 'delta_TE' ]), run_without_submitting=True, name='inputspec') outputnode = pe.Node( utility.IdentityInterface(fields=['fieldmap', 'fieldmap_mask']), run_without_submitting=True, name='outputspec') n_fieldmap_to_t1 = pe.Node(fsl.FLIRT(cost='mutualinfo', dof=6, no_search=True, uses_qform=True), name='fieldmap_to_t1') n_flirt_complex = pe.Node(utility.Function( input_names=['mat_file', 'cplx_file', 'ref_file'], output_names=['out_file'], function=flirt_complex), name='flirt_complex') n_resample_complex = pe.Node(afni.Resample(resample_mode='Cu', out_file='%s_int1sp.nii.gz'), name='resample_complex') n_phase_mag = pe.Node(utility.Function( input_names=['complex_in_file', 'mask_file'], output_names=['magnitude_out_file', 'phase_out_file'], function=complex_to_mag_phase), name='phase_mag') n_unwrap_phases = pe.Node(fsl.PRELUDE(process3d=True), name='unwrap_phases') n_make_fieldmap = pe.Node( fsl.FUGUE(smooth3d=3), iterfield=['fmap_out_file', 'mask_file', 'fmap_in_file'], name='make_fieldmap') w = pe.Workflow(name=name) w.connect([ # (inputnode, n_resample_complex,[('complex_image','in_file'), # ('t1_mask','master')]), (inputnode, n_fieldmap_to_t1, [('magnitude_image', 'in_file'), ('t1_mag', 'reference')]), (n_fieldmap_to_t1, n_flirt_complex, [ ('out_matrix_file', 'mat_file'), ]), (inputnode, n_flirt_complex, [('complex_image', 'cplx_file'), ('t1_mag', 'ref_file')]), (n_flirt_complex, n_phase_mag, [('out_file', 'complex_in_file')]), (inputnode, n_phase_mag, [('t1_mask', 'mask_file')]), (n_phase_mag, n_unwrap_phases, [('phase_out_file', 'phase_file')]), (n_phase_mag, n_unwrap_phases, [('magnitude_out_file', 'magnitude_file')]), (inputnode, n_unwrap_phases, [('t1_mask', 'mask_file')]), (n_unwrap_phases, n_make_fieldmap, [('unwrapped_phase_file', 'phasemap_file')]), (inputnode, n_make_fieldmap, [('delta_TE', 'asym_se_time'), ('t1_mask', 'mask_file'), (('complex_image', fname_presuffix_basename, '', '_fieldmap'), 'fmap_out_file')]), ]) return w
def vsm_fmb(name='VSM_FMB', phase_unwrap=True, fmb_params={}): """ Workflow that uses `FUGUE <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FUGUE>`_ to compute the voxel-shift map (VSM) from the corresponding B-fieldmap in EPI data. """ wf = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'in_bmap', 'in_mask', 'echospacing', 'delta_te', 'acc_factor', 'enc_dir' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['vsm', 'dfm', 'dfm_inv', 'jac', 'jac_inv']), name='outputnode') selmag = pe.Node(niu.Select(index=0), name='SelectMag') selpha = pe.Node(niu.Select(index=1), name='SelectPha') magmsk = pe.Node(fs.ApplyMask(), name='mask_mag') eff_es = pe.Node(niu.Function(input_names=['echospacing', 'acc_factor'], function=_eff_es_seg, output_names=['echospacing']), name='GetEffEcho') phcache = pe.Node(niu.IdentityInterface(fields=['pha_unwrapped']), name='PhCache') rad2rsec = pe.Node(niu.Function(input_names=['in_file', 'delta_te'], output_names=['out_file'], function=nwu.rads2radsec), name='ToRadSec') pre_fugue = pe.Node(fsl.FUGUE(save_unmasked_fmap=True), name='PreliminaryFugue') demean = pe.Node(niu.Function(function=nwu.demean_image, input_names=['in_file', 'in_mask'], output_names=['out_file']), name='DemeanFmap') cleanup = nwu.cleanup_edge_pipeline() addvol = pe.Node(niu.Function(function=nwu.add_empty_vol, input_names=['in_file'], output_names=['out_file']), name='AddEmptyVol') vsm = pe.Node(fsl.FUGUE(save_unmasked_shift=True, **fmb_params), name="Compute_VSM") dfm = process_vsm() dfm.inputs.inputnode.scaling = 1.0 wf.connect([(inputnode, selmag, [('in_bmap', 'inlist')]), (inputnode, selpha, [('in_bmap', 'inlist')]), (inputnode, magmsk, [('in_mask', 'mask_file')]), (inputnode, eff_es, [('echospacing', 'echospacing'), ('acc_factor', 'acc_factor')]), (selmag, magmsk, [('out', 'in_file')]), (phcache, rad2rsec, [('pha_unwrapped', 'in_file')]), (inputnode, rad2rsec, [('delta_te', 'delta_te')]), (rad2rsec, pre_fugue, [('out_file', 'fmap_in_file')]), (inputnode, pre_fugue, [('in_mask', 'mask_file')]), (pre_fugue, demean, [('fmap_out_file', 'in_file')]), (inputnode, demean, [('in_mask', 'in_mask')]), (demean, cleanup, [('out_file', 'inputnode.in_file')]), (inputnode, cleanup, [('in_mask', 'inputnode.in_mask')]), (cleanup, addvol, [('outputnode.out_file', 'in_file')]), (addvol, vsm, [('out_file', 'fmap_in_file')]), (eff_es, vsm, [('echospacing', 'dwell_time')]), (inputnode, vsm, [('in_mask', 'mask_file'), ('delta_te', 'asym_se_time')]), (vsm, outputnode, [('shift_out_file', 'vsm')]), (vsm, dfm, [('shift_out_file', 'inputnode.vsm')]), (inputnode, dfm, [('in_mask', 'inputnode.reference'), ('enc_dir', 'inputnode.enc_dir')]), (dfm, outputnode, [('outputnode.dfm', 'dfm'), ('outputnode.jacobian', 'jac'), ('outputnode.inv_dfm', 'dfm_inv'), ('outputnode.inv_jacobian', 'jac_inv')])]) if phase_unwrap: prelude = pe.Node(fsl.PRELUDE(process3d=True), name='PhaseUnwrap') wf.connect([(selmag, prelude, [('out', 'magnitude_file')]), (selpha, prelude, [('out', 'phase_file')]), (magmsk, prelude, [('out_file', 'mask_file')]), (prelude, phcache, [('unwrapped_phase_file', 'pha_unwrapped')])]) else: wf.connect(selpha, 'out', phcache, 'pha_unwrapped') return wf
def init_phdiff_wf(omp_nthreads, name='phdiff_wf'): """ Estimates the fieldmap using a phase-difference image and one or more magnitude images corresponding to two or more :abbr:`GRE (Gradient Echo sequence)` acquisitions. The `original code was taken from nipype <https://github.com/nipy/nipype/blob/master/nipype/workflows/dmri/fsl/artifacts.py#L514>`_. .. workflow :: :graph2use: orig :simple_form: yes from fmriprep.workflows.fieldmap.phdiff import init_phdiff_wf wf = init_phdiff_wf(omp_nthreads=1) Outputs:: outputnode.fmap_ref - The average magnitude image, skull-stripped outputnode.fmap_mask - The brain mask applied to the fieldmap outputnode.fmap - The estimated fieldmap in Hz """ inputnode = pe.Node( niu.IdentityInterface(fields=['magnitude', 'phasediff']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['fmap', 'fmap_ref', 'fmap_mask']), name='outputnode') def _pick1st(inlist): return inlist[0] # Read phasediff echo times meta = pe.Node(ReadSidecarJSON(), name='meta', mem_gb=0.01, run_without_submitting=True) # Merge input magnitude images magmrg = pe.Node(IntraModalMerge(), name='magmrg') # de-gradient the fields ("bias/illumination artifact") n4 = pe.Node(ants.N4BiasFieldCorrection(dimension=3, copy_header=True), name='n4', n_procs=omp_nthreads) bet = pe.Node(BETRPT(generate_report=True, frac=0.6, mask=True), name='bet') ds_fmap_mask = pe.Node(DerivativesDataSink(suffix='fmap_mask'), name='ds_report_fmap_mask', mem_gb=0.01, run_without_submitting=True) # uses mask from bet; outputs a mask # dilate = pe.Node(fsl.maths.MathsCommand( # nan2zeros=True, args='-kernel sphere 5 -dilM'), name='MskDilate') # phase diff -> radians pha2rads = pe.Node(niu.Function(function=siemens2rads), name='pha2rads') # FSL PRELUDE will perform phase-unwrapping prelude = pe.Node(fsl.PRELUDE(), name='prelude') denoise = pe.Node(fsl.SpatialFilter(operation='median', kernel_shape='sphere', kernel_size=3), name='denoise') demean = pe.Node(niu.Function(function=demean_image), name='demean') cleanup_wf = cleanup_edge_pipeline(name="cleanup_wf") compfmap = pe.Node(Phasediff2Fieldmap(), name='compfmap') # The phdiff2fmap interface is equivalent to: # rad2rsec (using rads2radsec from nipype.workflows.dmri.fsl.utils) # pre_fugue = pe.Node(fsl.FUGUE(save_fmap=True), name='ComputeFieldmapFUGUE') # rsec2hz (divide by 2pi) workflow = pe.Workflow(name=name) workflow.connect([ (inputnode, meta, [('phasediff', 'in_file')]), (inputnode, magmrg, [('magnitude', 'in_files')]), (magmrg, n4, [('out_avg', 'input_image')]), (n4, prelude, [('output_image', 'magnitude_file')]), (n4, bet, [('output_image', 'in_file')]), (bet, prelude, [('mask_file', 'mask_file')]), (inputnode, pha2rads, [('phasediff', 'in_file')]), (pha2rads, prelude, [('out', 'phase_file')]), (meta, compfmap, [('out_dict', 'metadata')]), (prelude, denoise, [('unwrapped_phase_file', 'in_file')]), (denoise, demean, [('out_file', 'in_file')]), (demean, cleanup_wf, [('out', 'inputnode.in_file')]), (bet, cleanup_wf, [('mask_file', 'inputnode.in_mask')]), (cleanup_wf, compfmap, [('outputnode.out_file', 'in_file')]), (compfmap, outputnode, [('out_file', 'fmap')]), (bet, outputnode, [('mask_file', 'fmap_mask'), ('out_file', 'fmap_ref')]), (inputnode, ds_fmap_mask, [('phasediff', 'source_file')]), (bet, ds_fmap_mask, [('out_report', 'in_file')]), ]) return workflow
def make_fmap_wkfl_old(name='make_fieldmap'): inputnode = pe.Node(utility.IdentityInterface(fields=[ 'phase_difference', 'magnitude', 't1_mask', 't1_mag', 'delta_TE' ]), name='inputspec') outputnode = pe.Node(utility.IdentityInterface(fields=[ 'fieldmap', 'fieldmap_reg', 'fieldmap_magnitude', 'fieldmap_mask' ]), name='outputspec') n_fieldmap2t1_warp = pe.Node( fsl.FLIRT( out_matrix_file='fieldmap2t1.mat', cost='normmi', dof=6, searchr_x=[-5, 5], # restrict search as they are acquired searchr_y=[-5, 5], # in the same sequence searchr_z=[-5, 5], cost_func='normmi'), name='fieldmap2t1_warp') n_invert_fieldmap2t1_warp = pe.Node(fsl.ConvertXFM(invert_xfm=True), name='invert_fieldmap2t1_warp') n_warp_t1_mask = pe.Node(fsl.ApplyXfm(apply_xfm=True, interp='nearestneighbour'), name='warp_t1_mask') n_mask_mag = pe.Node(interface=fsl.ImageMaths(op_string='-mul', suffix='_brain', output_type='NIFTI'), name='mask_mag') n_unwrap_phasediff = pe.Node(fsl.PRELUDE(process3d=True), name='unwrap_phasediff') n_make_fieldmap = pe.Node(fsl.FUGUE(fmap_out_file='fieldmap.nii.gz', smooth3d=2), name='make_fieldmap') w = pe.Workflow(name=name) w.connect([ (inputnode, n_fieldmap2t1_warp, [('t1_mag', 'reference'), ('magnitude', 'in_file')]), (n_fieldmap2t1_warp, n_invert_fieldmap2t1_warp, [('out_matrix_file', 'in_file')]), (n_invert_fieldmap2t1_warp, n_warp_t1_mask, [('out_file', 'in_matrix_file')]), (inputnode, n_warp_t1_mask, [('t1_mask', 'in_file')]), (inputnode, n_warp_t1_mask, [('magnitude', 'reference')]), (inputnode, n_mask_mag, [('magnitude', 'in_file')]), (n_warp_t1_mask, n_mask_mag, [('out_file', 'in_file2')]), (inputnode, n_unwrap_phasediff, [('phase_difference', 'phase_file')]), (inputnode, n_unwrap_phasediff, [('magnitude', 'magnitude_file')]), (n_warp_t1_mask, n_unwrap_phasediff, [('out_file', 'mask_file')]), (inputnode, n_make_fieldmap, [(('phase_difference', fname_presuffix_basename, 'fieldmap_', '', './'), 'fmap_out_file')]), (n_warp_t1_mask, n_make_fieldmap, [('out_file', 'mask_file')]), (n_unwrap_phasediff, n_make_fieldmap, [('unwrapped_phase_file', 'phasemap_file')]), (inputnode, n_make_fieldmap, [('delta_TE', 'asym_se_time')]), (n_warp_t1_mask, outputnode, [('out_file', 'fieldmap_mask')]), (n_mask_mag, outputnode, [('out_file', 'fieldmap_magnitude')]), (n_make_fieldmap, outputnode, [('fmap_out_file', 'fieldmap')]), (n_make_fieldmap, outputnode, [('fmap_out_file', 'fieldmap_reg')]), ]) return w
def prepare_phasediff_fmap(name="prepare_phasediff_fmap"): """This workflow adapts the fsl_prepare_fieldmap script from FSL for the FSL eddy command. Please note that the step 3 converts the fieldmap into Hz instead of rad/s in the initial script because FSL eddy --field expects the fieldmap to be in Hz. Input node: fmap_mask (str): Binary mask of the fieldmap. fmap_phasediff (str): Phase difference fieldmap. fmap_magnitude (str): Brain extracted magnitude fieldmap. Chose the fieldmap with the best contrast. delta_echo_time (float): DeltaEchoTime from BIDS specifications. Output node: calibrated_fmap (str): Calibrated fieldmap for eddy --field command. Warnings: This workflow can not be used for PRELUDE. You would need to change the step 3 (conversion into rad/s instead of Hz). """ import nipype.interfaces.fsl as fsl import nipype.interfaces.utility as nutil import nipype.pipeline.engine as npe from niflow.nipype1.workflows.dmri.fsl.utils import ( cleanup_edge_pipeline, demean_image, siemens2rads, ) from .dwi_preprocessing_using_phasediff_fmap_utils import rads2hz input_node = npe.Node( nutil.IdentityInterface(fields=[ "fmap_mask", "fmap_phasediff", "fmap_magnitude", "delta_echo_time" ]), name="input_node", ) output_node = npe.Node(nutil.IdentityInterface(fields=["calibrated_fmap"]), name="output_node") # Step 1 - Convert the fmap into radians pha2rads = npe.Node( nutil.Function(input_names=["in_file"], output_names=["out_file"], function=siemens2rads), name="1-ConvertFMapToRads", ) # Step 2 - Unwrap the fmap with PRELUDE prelude = npe.Node(fsl.PRELUDE(process3d=True), name="2-PhaseUnwrap") # Step 3 - Convert the fmap to Hz rads2hz = npe.Node( nutil.Function( input_names=["in_file", "delta_te"], output_names=["out_file"], function=rads2hz, ), name="3-ConvertFMapToHz", ) # Step 4 - Call FUGUE to extrapolate from mask (fill holes, etc) pre_fugue = npe.Node(fsl.FUGUE(save_fmap=True), name="4-FugueExtrapolationFromMask") # Step 5 - Demean fmap to avoid gross shifting demean = npe.Node( nutil.Function( input_names=["in_file", "in_mask"], output_names=["out_file"], function=demean_image, ), name="5-DemeanFMap", ) # Step 6 - Clean up edge voxels cleanup = cleanup_edge_pipeline(name="6-CleanUpEdgeVoxels") wf = npe.Workflow(name=name) # fmt: off wf.connect([ # Step 1 - Convert the fmap into radians (input_node, pha2rads, [("fmap_phasediff", "in_file")]), # Step 2 - Unwrap the fmap with PRELUDE (pha2rads, prelude, [("out_file", "phase_file")]), (input_node, prelude, [("fmap_magnitude", "magnitude_file")]), (input_node, prelude, [("fmap_mask", "mask_file")]), # Step 3 - Convert the fmap to Hz (prelude, rads2hz, [("unwrapped_phase_file", "in_file")]), (input_node, rads2hz, [("delta_echo_time", "delta_te")]), # Step 4 - Call FUGUE to extrapolate from mask (fill holes, etc) (rads2hz, pre_fugue, [("out_file", "fmap_in_file")]), (input_node, pre_fugue, [("fmap_mask", "mask_file")]), # Step 5 - Demean fmap to avoid gross shifting (pre_fugue, demean, [("fmap_out_file", "in_file")]), (input_node, demean, [("fmap_mask", "in_mask")]), # Step 6 - Clean up edge voxels (demean, cleanup, [("out_file", "inputnode.in_file")]), (input_node, cleanup, [("fmap_mask", "inputnode.in_mask")]), # Output node (cleanup, output_node, [("outputnode.out_file", "calibrated_fmap")]), ]) # fmt: on return wf
def phase_diff_and_magnitudes(name='phase_diff_and_magnitudes'): """ Estimates the fieldmap using a phase-difference image and one or more magnitude images corresponding to two or more :abbr:`GRE (Gradient Echo sequence)` acquisitions. The `original code was taken from nipype <https://github.com/nipy/nipype/blob/master/nipype/workflows/dmri/fsl/artifacts.py#L514>`_. Outputs:: outputnode.fmap_ref - The average magnitude image, skull-stripped outputnode.fmap_mask - The brain mask applied to the fieldmap outputnode.fmap - The estimated fieldmap in Hz """ inputnode = pe.Node(niu.IdentityInterface(fields=['input_images']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['fmap_ref', 'fmap_mask', 'fmap']), name='outputnode') sortfmaps = pe.Node(niu.Function(function=_sort_fmaps, input_names=['input_images'], output_names=['magnitude', 'phasediff']), name='SortFmaps') def _pick1st(inlist): return inlist[0] # Read phasediff echo times meta = pe.Node(ReadSidecarJSON(fields=['EchoTime1', 'EchoTime2']), name='metadata') dte = pe.Node(niu.Function(input_names=['in_values'], output_names=['delta_te'], function=_delta_te), name='ComputeDeltaTE') # Merge input magnitude images magmrg = pe.Node(IntraModalMerge(), name='MagnitudeFuse') # de-gradient the fields ("bias/illumination artifact") n4 = pe.Node(ants.N4BiasFieldCorrection(dimension=3), name='MagnitudeBias') bet = pe.Node(fsl.BET(frac=0.6, mask=True), name='MagnitudeBET') # uses mask from bet; outputs a mask # dilate = pe.Node(fsl.maths.MathsCommand( # nan2zeros=True, args='-kernel sphere 5 -dilM'), name='MskDilate') # phase diff -> radians pha2rads = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_file'], function=siemens2rads), name='PreparePhase') # FSL PRELUDE will perform phase-unwrapping prelude = pe.Node(fsl.PRELUDE(process3d=True), name='PhaseUnwrap') denoise = pe.Node(fsl.SpatialFilter(operation='median', kernel_shape='sphere', kernel_size=3), name='PhaseDenoise') demean = pe.Node(niu.Function(input_names=['in_file', 'in_mask'], output_names=['out_file'], function=demean_image), name='DemeanFmap') cleanup = cleanup_edge_pipeline() compfmap = pe.Node(niu.Function(input_names=['in_file', 'delta_te'], output_names=['out_file'], function=phdiff2fmap), name='ComputeFieldmap') # The phdiff2fmap interface is equivalent to: # rad2rsec (using rads2radsec from nipype.workflows.dmri.fsl.utils) # pre_fugue = pe.Node(fsl.FUGUE(save_fmap=True), name='ComputeFieldmapFUGUE') # rsec2hz (divide by 2pi) workflow = pe.Workflow(name=name) workflow.connect([ (inputnode, sortfmaps, [('input_images', 'input_images')]), (sortfmaps, meta, [(('phasediff', _pick1st), 'in_file')]), (sortfmaps, magmrg, [('magnitude', 'in_files')]), (magmrg, n4, [('out_avg', 'input_image')]), (n4, prelude, [('output_image', 'magnitude_file')]), (n4, bet, [('output_image', 'in_file')]), (bet, prelude, [('mask_file', 'mask_file')]), (sortfmaps, pha2rads, [(('phasediff', _pick1st), 'in_file')]), (pha2rads, prelude, [('out_file', 'phase_file')]), (meta, dte, [('out_dict', 'in_values')]), (dte, compfmap, [('delta_te', 'delta_te')]), (prelude, denoise, [('unwrapped_phase_file', 'in_file')]), (denoise, demean, [('out_file', 'in_file')]), (demean, cleanup, [('out_file', 'inputnode.in_file')]), (bet, cleanup, [('mask_file', 'inputnode.in_mask')]), (cleanup, compfmap, [('outputnode.out_file', 'in_file')]), (compfmap, outputnode, [('out_file', 'fmap')]), (bet, outputnode, [('mask_file', 'fmap_mask'), ('out_file', 'fmap_ref')]) ]) return workflow
moving=mag_brain, output_warped=magbrain_warped, transform_prefix=outdir + '/trans_') applytransform(in_file=phaseon, reference=ref, out_file=phase_warped, transformfile=outdir + '/trans_Composite.h5', interpolation='LanczosWindowedSinc') au2rads(phase_warped, outdir) phasediff = glob.glob(outdir + '/*rads.nii.gz') maskdata(magbrain_warped, mag_mask) #unwarp withe predule unwrapped = outdir + '/unwrapped.nii.gz' prefsl = fsl.PRELUDE() prefsl.inputs.magnitude_file = magbrain_warped prefsl.inputs.phase_file = phasediff[0] prefsl.inputs.mask_file = mag_mask prefsl.inputs.unwrapped_phase_file = unwrapped prefsl.run() #denoise demean recenter the fieldmap and #recentre recentered = _recenter(unwrapped, newpath=outdir + '/') # denoise with fsl spatial filter denoised = outdir + '/unwrapped_denoise.nii.gz' denoise = fsl.SpatialFilter() denoise.inputs.in_file = recentered denoise.inputs.kernel_shape = 'sphere' denoise.inputs.kernel_size = 3 denoise.inputs.operation = 'median'
def init_phdiff_wf(omp_nthreads, phasetype='phasediff', name='phdiff_wf'): """ Estimates the fieldmap using a phase-difference image and one or more magnitude images corresponding to two or more :abbr:`GRE (Gradient Echo sequence)` acquisitions. The `original code was taken from nipype <https://github.com/nipy/nipype/blob/master/nipype/workflows/dmri/fsl/artifacts.py#L514>`_. .. workflow :: :graph2use: orig :simple_form: yes from qsiprep.workflows.fieldmap.phdiff import init_phdiff_wf wf = init_phdiff_wf(omp_nthreads=1) Outputs:: outputnode.fmap_ref - The average magnitude image, skull-stripped outputnode.fmap_mask - The brain mask applied to the fieldmap outputnode.fmap - The estimated fieldmap in Hz """ workflow = Workflow(name=name) workflow.__desc__ = """\ A deformation field to correct for susceptibility distortions was estimated based on a field map that was co-registered to the BOLD reference, using a custom workflow of *fMRIPrep* derived from D. Greve's `epidewarp.fsl` [script](http://www.nmr.mgh.harvard.edu/~greve/fbirn/b0/epidewarp.fsl) and further improvements of HCP Pipelines [@hcppipelines]. """ inputnode = pe.Node( niu.IdentityInterface(fields=['magnitude', 'phasediff']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['fmap', 'fmap_ref', 'fmap_mask']), name='outputnode') # Merge input magnitude images magmrg = pe.Node(IntraModalMerge(), name='magmrg') # de-gradient the fields ("bias/illumination artifact") n4 = pe.Node(ants.N4BiasFieldCorrection(dimension=3, copy_header=True), name='n4', n_procs=omp_nthreads) bet = pe.Node(BETRPT(generate_report=True, frac=0.6, mask=True), name='bet') ds_report_fmap_mask = pe.Node(DerivativesDataSink(desc='brain', suffix='mask'), name='ds_report_fmap_mask', mem_gb=0.01, run_without_submitting=True) # uses mask from bet; outputs a mask # dilate = pe.Node(fsl.maths.MathsCommand( # nan2zeros=True, args='-kernel sphere 5 -dilM'), name='MskDilate') # FSL PRELUDE will perform phase-unwrapping prelude = pe.Node(fsl.PRELUDE(), name='prelude') denoise = pe.Node(fsl.SpatialFilter(operation='median', kernel_shape='sphere', kernel_size=5), name='denoise') demean = pe.Node(niu.Function(function=demean_image), name='demean') cleanup_wf = cleanup_edge_pipeline(name="cleanup_wf") compfmap = pe.Node(Phasediff2Fieldmap(), name='compfmap') # The phdiff2fmap interface is equivalent to: # rad2rsec (using rads2radsec from nipype.workflows.dmri.fsl.utils) # pre_fugue = pe.Node(fsl.FUGUE(save_fmap=True), name='ComputeFieldmapFUGUE') # rsec2hz (divide by 2pi) if phasetype == "phasediff": # Read phasediff echo times meta = pe.Node(ReadSidecarJSON(), name='meta', mem_gb=0.01) # phase diff -> radians pha2rads = pe.Node(niu.Function(function=siemens2rads), name='pha2rads') # Read phasediff echo times meta = pe.Node(ReadSidecarJSON(), name='meta', mem_gb=0.01, run_without_submitting=True) workflow.connect([ (meta, compfmap, [('out_dict', 'metadata')]), (inputnode, pha2rads, [('phasediff', 'in_file')]), (pha2rads, prelude, [('out', 'phase_file')]), (inputnode, ds_report_fmap_mask, [('phasediff', 'source_file')]), ]) elif phasetype == "phase": workflow.__desc__ += """\ The phase difference used for unwarping was calculated using two separate phase measurements [@pncprocessing]. """ # Special case for phase1, phase2 images meta = pe.MapNode(ReadSidecarJSON(), name='meta', mem_gb=0.01, run_without_submitting=True, iterfield=['in_file']) phases2fmap = pe.Node(Phases2Fieldmap(), name='phases2fmap') workflow.connect([ (meta, phases2fmap, [('out_dict', 'metadatas')]), (inputnode, phases2fmap, [('phasediff', 'phase_files')]), (phases2fmap, prelude, [('out_file', 'phase_file')]), (phases2fmap, compfmap, [('phasediff_metadata', 'metadata')]), (phases2fmap, ds_report_fmap_mask, [('out_file', 'source_file')]) ]) workflow.connect([ (inputnode, meta, [('phasediff', 'in_file')]), (inputnode, magmrg, [('magnitude', 'in_files')]), (magmrg, n4, [('out_avg', 'input_image')]), (n4, prelude, [('output_image', 'magnitude_file')]), (n4, bet, [('output_image', 'in_file')]), (bet, prelude, [('mask_file', 'mask_file')]), (prelude, denoise, [('unwrapped_phase_file', 'in_file')]), (denoise, demean, [('out_file', 'in_file')]), (demean, cleanup_wf, [('out', 'inputnode.in_file')]), (bet, cleanup_wf, [('mask_file', 'inputnode.in_mask')]), (cleanup_wf, compfmap, [('outputnode.out_file', 'in_file')]), (compfmap, outputnode, [('out_file', 'fmap')]), (bet, outputnode, [('mask_file', 'fmap_mask'), ('out_file', 'fmap_ref')]), (bet, ds_report_fmap_mask, [('out_report', 'in_file')]), ]) return workflow
def make_fs_fieldmap(name='make_fs_fieldmap'): inputnode = pe.Node(utility.IdentityInterface(fields=[ 'complex_image', 'magnitude_image', 'subject_id', 'freesurfer_subject_dir', 't1_mask', 'delta_TE' ]), run_without_submitting=True, name='inputspec') outputnode = pe.Node( utility.IdentityInterface(fields=['fieldmap', 'fieldmap_mask']), run_without_submitting=True, name='outputspec') n_fieldmap_to_t1_bbr = pe.Node(freesurfer.BBRegister(init='header', reg_frame=0, out_fsl_file=True, registered_file=True, contrast_type='t1'), name='fieldmap_to_t1_bbr') """ n_fieldmap_to_t1 = pe.Node( fsl.FLIRT(cost='mutualinfo', dof=6, no_search=True, uses_qform=True), name='fieldmap_to_t1') """ n_reg_to_mni = pe.Node(freesurfer.preprocess.Tkregister( no_edit=True, xfm_out='%s_xfm.mat'), run_without_submitting=True, name='reg_to_mni') n_resamp_complex = pe.Node(utility.Function( input_names=['mat_file', 'cplx_file', 'ref_file'], output_names=['out_file'], function=fs_resamp_complex), name='resamp_complex') n_phase_mag = pe.Node(utility.Function( input_names=['complex_in_file', 'mask_file'], output_names=['magnitude_out_file', 'phase_out_file'], function=complex_to_mag_phase), name='phase_mag') n_unwrap_phases = pe.Node(fsl.PRELUDE(process3d=True), name='unwrap_phases') n_make_fieldmap = pe.Node( fsl.FUGUE(smooth3d=3, unwarp_direction='z'), iterfield=['fmap_out_file', 'mask_file', 'fmap_in_file'], name='make_fieldmap') w = pe.Workflow(name=name) w.connect([ # (inputnode, n_fieldmap_to_t1, [('magnitude_image','in_file'), # ('t1_mag','reference')]), #(n_fieldmap_to_t1, n_flirt_complex, [('out_matrix_file','mat_file'),]), (inputnode, n_fieldmap_to_t1_bbr, [('magnitude_image', 'source_file'), ('freesurfer_subject_dir', 'subjects_dir'), ('subject_id', 'subject_id')]), (n_fieldmap_to_t1_bbr, n_reg_to_mni, [('out_reg_file', 'reg_file')]), (inputnode, n_reg_to_mni, [ ('magnitude_image', 'mov'), ('freesurfer_subject_dir', 'subjects_dir'), ]), (n_reg_to_mni, n_resamp_complex, [ ('xfm_out', 'mat_file'), ]), (inputnode, n_resamp_complex, [('complex_image', 'cplx_file'), ('t1_mask', 'ref_file')]), (n_resamp_complex, n_phase_mag, [('out_file', 'complex_in_file')]), (inputnode, n_phase_mag, [('t1_mask', 'mask_file')]), (n_phase_mag, n_unwrap_phases, [('phase_out_file', 'phase_file')]), (n_phase_mag, n_unwrap_phases, [('magnitude_out_file', 'magnitude_file')]), (inputnode, n_unwrap_phases, [('t1_mask', 'mask_file')]), (n_unwrap_phases, n_make_fieldmap, [('unwrapped_phase_file', 'phasemap_file')]), (inputnode, n_make_fieldmap, [('delta_TE', 'asym_se_time'), ('t1_mask', 'mask_file'), (('complex_image', fname_presuffix_basename, '', '_fieldmap'), 'fmap_out_file')]), ]) return w
def create_epidewarp_pipeline(name='epidewarp', fieldmap_registration=False): """ Replaces the epidewarp.fsl script (http://www.nmr.mgh.harvard.edu/~greve/fbirn/b0/epidewarp.fsl) for susceptibility distortion correction of dMRI & fMRI acquired with EPI sequences and the fieldmap information (Jezzard et al., 1995) using FSL's FUGUE. The registration to the (warped) fieldmap (strictly following the original script) is available using fieldmap_registration=True. .. warning:: This workflow makes use of ``epidewarp.fsl`` a script of FSL deprecated long time ago. The use of this workflow is not recommended, use :func:`nipype.workflows.dmri.preprocess.epi.sdc_fmb` instead. Example ------- >>> nipype_epicorrect = create_epidewarp_pipeline('nipype_epidewarp', fieldmap_registration=False) >>> nipype_epicorrect.inputs.inputnode.in_file = 'diffusion.nii' >>> nipype_epicorrect.inputs.inputnode.fieldmap_mag = 'magnitude.nii' >>> nipype_epicorrect.inputs.inputnode.fieldmap_pha = 'phase.nii' >>> nipype_epicorrect.inputs.inputnode.te_diff = 2.46 >>> nipype_epicorrect.inputs.inputnode.epi_echospacing = 0.77 >>> nipype_epicorrect.inputs.inputnode.epi_rev_encoding = False >>> nipype_epicorrect.inputs.inputnode.ref_num = 0 >>> nipype_epicorrect.inputs.inputnode.pi_accel_factor = 1.0 >>> nipype_epicorrect.run() # doctest: +SKIP Inputs:: inputnode.in_file - The volume acquired with EPI sequence inputnode.fieldmap_mag - The magnitude of the fieldmap inputnode.fieldmap_pha - The phase difference of the fieldmap inputnode.te_diff - Time difference between TE in ms. inputnode.epi_echospacing - The echo spacing (aka dwell time) in the EPI sequence inputnode.epi_ph_encoding_dir - The phase encoding direction in EPI acquisition (default y) inputnode.epi_rev_encoding - True if it is acquired with reverse encoding inputnode.pi_accel_factor - Acceleration factor used for EPI parallel imaging (GRAPPA) inputnode.vsm_sigma - Sigma value of the gaussian smoothing filter applied to the vsm (voxel shift map) inputnode.ref_num - The reference volume (B=0 in dMRI or a central frame in fMRI) Outputs:: outputnode.epi_corrected Optional arguments:: fieldmap_registration - True if registration to fieldmap should be done (default False) """ warnings.warn(('This workflow reproduces a deprecated FSL script.'), DeprecationWarning) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'in_file', 'fieldmap_mag', 'fieldmap_pha', 'te_diff', 'epi_echospacing', 'epi_ph_encoding_dir', 'epi_rev_encoding', 'pi_accel_factor', 'vsm_sigma', 'ref_num', 'unwarp_direction' ]), name='inputnode') pipeline = pe.Workflow(name=name) # Keep first frame from magnitude select_mag = pe.Node(fsl.utils.ExtractROI(t_size=1, t_min=0), name='select_magnitude') # mask_brain mask_mag = pe.Node(fsl.BET(mask=True), name='mask_magnitude') mask_mag_dil = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_file'], function=_dilate_mask), name='mask_dilate') # Compute dwell time dwell_time = pe.Node(niu.Function( input_names=['dwell_time', 'pi_factor', 'is_reverse_encoding'], output_names=['dwell_time'], function=_compute_dwelltime), name='dwell_time') # Normalize phase diff to be [-pi, pi) norm_pha = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_file'], function=_prepare_phasediff), name='normalize_phasediff') # Execute FSL PRELUDE: prelude -p %s -a %s -o %s -f -v -m %s prelude = pe.Node(fsl.PRELUDE(process3d=True), name='phase_unwrap') fill_phase = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_file'], function=_fill_phase), name='fill_phasediff') # to assure that vsm is same dimension as mag. The input only affects the output dimension. # The content of the input has no effect on the vsm. The de-warped mag volume is # meaningless and will be thrown away # fugue -i %s -u %s -p %s --dwell=%s --asym=%s --mask=%s --saveshift=%s % # ( mag_name, magdw_name, ph_name, esp, tediff, mask_name, vsmmag_name) vsm = pe.Node(fsl.FUGUE(save_shift=True), name='generate_vsm') vsm_mean = pe.Node(niu.Function( input_names=['in_file', 'mask_file', 'in_unwarped'], output_names=['out_file'], function=_vsm_remove_mean), name='vsm_mean_shift') # fugue_epi dwi_split = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_files'], function=_split_dwi), name='dwi_split') # 'fugue -i %s -u %s --loadshift=%s --mask=%s' % ( vol_name, out_vol_name, vsm_name, mask_name ) dwi_applyxfm = pe.MapNode(fsl.FUGUE(icorr=True, save_shift=False), iterfield=['in_file'], name='dwi_fugue') # Merge back all volumes dwi_merge = pe.Node(fsl.utils.Merge(dimension='t'), name='dwi_merge') outputnode = pe.Node(niu.IdentityInterface(fields=['epi_corrected']), name='outputnode') pipeline.connect([ (inputnode, dwell_time, [('epi_echospacing', 'dwell_time'), ('pi_accel_factor', 'pi_factor'), ('epi_rev_encoding', 'is_reverse_encoding')]), (inputnode, select_mag, [('fieldmap_mag', 'in_file')]), (inputnode, norm_pha, [('fieldmap_pha', 'in_file')]), (select_mag, mask_mag, [('roi_file', 'in_file')]), (mask_mag, mask_mag_dil, [('mask_file', 'in_file')]), (select_mag, prelude, [('roi_file', 'magnitude_file')]), (norm_pha, prelude, [('out_file', 'phase_file')]), (mask_mag_dil, prelude, [('out_file', 'mask_file')]), (prelude, fill_phase, [('unwrapped_phase_file', 'in_file')]), (inputnode, vsm, [('fieldmap_mag', 'in_file')]), (fill_phase, vsm, [('out_file', 'phasemap_in_file')]), (inputnode, vsm, [(('te_diff', _ms2sec), 'asym_se_time'), ('vsm_sigma', 'smooth2d')]), (dwell_time, vsm, [(('dwell_time', _ms2sec), 'dwell_time')]), (mask_mag_dil, vsm, [('out_file', 'mask_file')]), (mask_mag_dil, vsm_mean, [('out_file', 'mask_file')]), (vsm, vsm_mean, [('unwarped_file', 'in_unwarped'), ('shift_out_file', 'in_file')]), (inputnode, dwi_split, [('in_file', 'in_file')]), (dwi_split, dwi_applyxfm, [('out_files', 'in_file')]), (dwi_applyxfm, dwi_merge, [('unwarped_file', 'in_files')]), (dwi_merge, outputnode, [('merged_file', 'epi_corrected')]) ]) if fieldmap_registration: """ Register magfw to example epi. There are some parameters here that may need to be tweaked. Should probably strip the mag Pre-condition: forward warp the mag in order to reg with func. What does mask do here? """ # Select reference volume from EPI (B0 in dMRI and a middle frame in # fMRI) select_epi = pe.Node(fsl.utils.ExtractROI(t_size=1), name='select_epi') # fugue -i %s -w %s --loadshift=%s --mask=%s % ( mag_name, magfw_name, # vsmmag_name, mask_name ), log ) # Forward Map vsm_fwd = pe.Node(fsl.FUGUE(forward_warping=True), name='vsm_fwd') vsm_reg = pe.Node(fsl.FLIRT(bins=256, cost='corratio', dof=6, interp='spline', searchr_x=[-10, 10], searchr_y=[-10, 10], searchr_z=[-10, 10]), name='vsm_registration') # 'flirt -in %s -ref %s -out %s -init %s -applyxfm' % ( vsmmag_name, ref_epi, vsmmag_name, magfw_mat_out ) vsm_applyxfm = pe.Node(fsl.ApplyXfm(interp='spline'), name='vsm_apply_xfm') # 'flirt -in %s -ref %s -out %s -init %s -applyxfm' % ( mask_name, ref_epi, mask_name, magfw_mat_out ) msk_applyxfm = pe.Node(fsl.ApplyXfm(interp='nearestneighbour'), name='msk_apply_xfm') pipeline.connect([ (inputnode, select_epi, [('in_file', 'in_file'), ('ref_num', 't_min')]), (select_epi, vsm_reg, [('roi_file', 'reference')]), (vsm, vsm_fwd, [('shift_out_file', 'shift_in_file')]), (mask_mag_dil, vsm_fwd, [('out_file', 'mask_file')]), (inputnode, vsm_fwd, [('fieldmap_mag', 'in_file')]), (vsm_fwd, vsm_reg, [('warped_file', 'in_file')]), (vsm_reg, msk_applyxfm, [('out_matrix_file', 'in_matrix_file')]), (select_epi, msk_applyxfm, [('roi_file', 'reference')]), (mask_mag_dil, msk_applyxfm, [('out_file', 'in_file')]), (vsm_reg, vsm_applyxfm, [('out_matrix_file', 'in_matrix_file')]), (select_epi, vsm_applyxfm, [('roi_file', 'reference')]), (vsm_mean, vsm_applyxfm, [('out_file', 'in_file')]), (msk_applyxfm, dwi_applyxfm, [('out_file', 'mask_file')]), (vsm_applyxfm, dwi_applyxfm, [('out_file', 'shift_in_file')]) ]) else: pipeline.connect([ (mask_mag_dil, dwi_applyxfm, [('out_file', 'mask_file')]), (vsm_mean, dwi_applyxfm, [('out_file', 'shift_in_file')]) ]) return pipeline
def sdc_fmb(name='fmb_correction', fugue_params=dict(smooth3d=2.0), bmap_params=dict(delta_te=2.46e-3), epi_params=dict(echospacing=0.77e-3, acc_factor=3, enc_dir='y-')): """ SDC stands for susceptibility distortion correction. FMB stands for fieldmap-based. The fieldmap based method (FMB) implements SDC by using a mapping of the B0 field as proposed by [Jezzard95]_. This workflow uses the implementation of FSL (`FUGUE <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FUGUE>`_). Phase unwrapping is performed using `PRELUDE <http://fsl.fmrib.ox.ac.uk/fsl/fsl-4.1.9/fugue/prelude.html>`_ [Jenkinson03]_. Preparation of the fieldmap is performed reproducing the script in FSL `fsl_prepare_fieldmap <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FUGUE/Guide#SIEMENS_data>`_. Example ------- >>> from nipype.workflows.dmri.fsl.artifacts import sdc_fmb >>> fmb = sdc_fmb() >>> fmb.inputs.inputnode.in_file = 'diffusion.nii' >>> fmb.inputs.inputnode.in_bval = 'diffusion.bval' >>> fmb.inputs.inputnode.in_mask = 'mask.nii' >>> fmb.inputs.inputnode.bmap_mag = 'magnitude.nii' >>> fmb.inputs.inputnode.bmap_pha = 'phase.nii' >>> fmb.run() # doctest: +SKIP .. warning:: Only SIEMENS format fieldmaps are supported. .. admonition:: References .. [Jezzard95] Jezzard P, and Balaban RS, `Correction for geometric distortion in echo planar images from B0 field variations <http://dx.doi.org/10.1002/mrm.1910340111>`_, MRM 34(1):65-73. (1995). doi: 10.1002/mrm.1910340111. .. [Jenkinson03] Jenkinson M., `Fast, automated, N-dimensional phase-unwrapping algorithm <http://dx.doi.org/10.1002/mrm.10354>`_, MRM 49(1):193-197, 2003, doi: 10.1002/mrm.10354. """ inputnode = pe.Node(niu.IdentityInterface(fields=['in_file', 'in_bval', 'in_mask', 'bmap_pha', 'bmap_mag']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=['out_file', 'out_vsm', 'out_warp']), name='outputnode') delta_te = epi_params['echospacing'] / (1.0 * epi_params['acc_factor']) firstmag = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name='GetFirst') n4 = pe.Node(ants.N4BiasFieldCorrection(dimension=3), name='Bias') bet = pe.Node(fsl.BET(frac=0.4, mask=True), name='BrainExtraction') dilate = pe.Node(fsl.maths.MathsCommand(nan2zeros=True, args='-kernel sphere 5 -dilM'), name='MskDilate') pha2rads = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_file'], function=siemens2rads), name='PreparePhase') prelude = pe.Node(fsl.PRELUDE(process3d=True), name='PhaseUnwrap') rad2rsec = pe.Node(niu.Function(input_names=['in_file', 'delta_te'], output_names=['out_file'], function=rads2radsec), name='ToRadSec') rad2rsec.inputs.delta_te = bmap_params['delta_te'] avg_b0 = pe.Node(niu.Function(input_names=['in_dwi', 'in_bval'], output_names=['out_file'], function=b0_average), name='b0_avg') flirt = pe.Node(fsl.FLIRT(interp='spline', cost='normmi', cost_func='normmi', dof=6, bins=64, save_log=True, padding_size=10, searchr_x=[-4, 4], searchr_y=[-4, 4], searchr_z=[-4, 4], fine_search=1, coarse_search=10), name='BmapMag2B0') applyxfm = pe.Node(fsl.ApplyXfm(interp='spline', padding_size=10, apply_xfm=True), name='BmapPha2B0') pre_fugue = pe.Node(fsl.FUGUE(save_fmap=True), name='PreliminaryFugue') demean = pe.Node(niu.Function(input_names=['in_file', 'in_mask'], output_names=['out_file'], function=demean_image), name='DemeanFmap') cleanup = cleanup_edge_pipeline() addvol = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_file'], function=add_empty_vol), name='AddEmptyVol') vsm = pe.Node(fsl.FUGUE(save_shift=True, **fugue_params), name="ComputeVSM") vsm.inputs.asym_se_time = bmap_params['delta_te'] vsm.inputs.dwell_time = delta_te split = pe.Node(fsl.Split(dimension='t'), name='SplitDWIs') merge = pe.Node(fsl.Merge(dimension='t'), name='MergeDWIs') unwarp = pe.MapNode(fsl.FUGUE(icorr=True, forward_warping=False), iterfield=['in_file'], name='UnwarpDWIs') unwarp.inputs.unwarp_direction = epi_params['enc_dir'] thres = pe.MapNode(fsl.Threshold(thresh=0.0), iterfield=['in_file'], name='RemoveNegative') vsm2dfm = vsm2warp() vsm2dfm.inputs.inputnode.scaling = 1.0 vsm2dfm.inputs.inputnode.enc_dir = epi_params['enc_dir'] wf = pe.Workflow(name=name) wf.connect([ (inputnode, pha2rads, [('bmap_pha', 'in_file')]), (inputnode, firstmag, [('bmap_mag', 'in_file')]), (inputnode, avg_b0, [('in_file', 'in_dwi'), ('in_bval', 'in_bval')]), (firstmag, n4, [('roi_file', 'input_image')]), (n4, bet, [('output_image', 'in_file')]), (bet, dilate, [('mask_file', 'in_file')]), (pha2rads, prelude, [('out_file', 'phase_file')]), (n4, prelude, [('output_image', 'magnitude_file')]), (dilate, prelude, [('out_file', 'mask_file')]), (prelude, rad2rsec, [('unwrapped_phase_file', 'in_file')]), (avg_b0, flirt, [('out_file', 'reference')]), (inputnode, flirt, [('in_mask', 'ref_weight')]), (n4, flirt, [('output_image', 'in_file')]), (dilate, flirt, [('out_file', 'in_weight')]), (avg_b0, applyxfm, [('out_file', 'reference')]), (rad2rsec, applyxfm, [('out_file', 'in_file')]), (flirt, applyxfm, [('out_matrix_file', 'in_matrix_file')]), (applyxfm, pre_fugue, [('out_file', 'fmap_in_file')]), (inputnode, pre_fugue, [('in_mask', 'mask_file')]), (pre_fugue, demean, [('fmap_out_file', 'in_file')]), (inputnode, demean, [('in_mask', 'in_mask')]), (demean, cleanup, [('out_file', 'inputnode.in_file')]), (inputnode, cleanup, [('in_mask', 'inputnode.in_mask')]), (cleanup, addvol, [('outputnode.out_file', 'in_file')]), (inputnode, vsm, [('in_mask', 'mask_file')]), (addvol, vsm, [('out_file', 'fmap_in_file')]), (inputnode, split, [('in_file', 'in_file')]), (split, unwarp, [('out_files', 'in_file')]), (vsm, unwarp, [('shift_out_file', 'shift_in_file')]), (unwarp, thres, [('unwarped_file', 'in_file')]), (thres, merge, [('out_file', 'in_files')]), (merge, vsm2dfm, [('merged_file', 'inputnode.in_ref')]), (vsm, vsm2dfm, [('shift_out_file', 'inputnode.in_vsm')]), (merge, outputnode, [('merged_file', 'out_file')]), (vsm, outputnode, [('shift_out_file', 'out_vsm')]), (vsm2dfm, outputnode, [('outputnode.out_warp', 'out_warp')]) ]) return wf