NodeHash_604000cba700.inputs.mask = True #Wraps command **fslmaths** NodeHash_600001ab26c0 = pe.MapNode(interface = fsl.ErodeImage(), name = 'NodeName_600001ab26c0', iterfield = ['in_file']) #Wraps command **fslmaths** NodeHash_60c0018a6e40 = pe.MapNode(interface = fsl.ErodeImage(), name = 'NodeName_60c0018a6e40', iterfield = ['in_file']) #Custom interface wrapping function SubTwo NodeHash_60c0018a4860 = pe.Node(interface = utils_math.SubTwo, name = 'NodeName_60c0018a4860') #Custom interface wrapping function Abs NodeHash_600001eab220 = pe.Node(interface = utils_math.Abs, name = 'NodeName_600001eab220') #Wraps command **fsl_prepare_fieldmap** NodeHash_6000018b2600 = pe.MapNode(interface = fsl.PrepareFieldmap(), name = 'NodeName_6000018b2600', iterfield = ['in_phase', 'in_magnitude']) #Wraps command **fugue** NodeHash_60c0018a5a60 = pe.MapNode(interface = fsl.FUGUE(), name = 'NodeName_60c0018a5a60', iterfield = ['in_file', 'fmap_in_file', 'mask_file']) #Generic datasink module to store structured outputs NodeHash_6000010a5b80 = pe.Node(interface = io.DataSink(), name = 'NodeName_6000010a5b80') NodeHash_6000010a5b80.inputs.base_directory = SinkDir NodeHash_6000010a5b80.inputs.regexp_substitutions = [("func_fieldmapcorr/_NodeName_.{13}", "")] #Generic datasink module to store structured outputs NodeHash_608001eb9bc0 = pe.Node(interface = io.DataSink(), name = 'NodeName_608001eb9bc0') NodeHash_608001eb9bc0.inputs.base_directory = SinkDir NodeHash_608001eb9bc0.inputs.regexp_substitutions = [("_NodeName_.{13}", "")] #Very simple frontend for storing values into a JSON file.
def fieldmap_correction(name='fieldmap_correction', nocheck=False): """ .. deprecated:: 0.9.3 Use :func:`nipype.workflows.dmri.preprocess.epi.sdc_fmb` instead. Fieldmap-based retrospective correction of EPI images for the susceptibility distortion artifact (Jezzard et al., 1995). Fieldmap images are assumed to be already registered to EPI data, and a brain mask is required. Replaces the former workflow, still available as create_epidewarp_pipeline(). The difference with respect the epidewarp pipeline is that now the workflow uses the new fsl_prepare_fieldmap available as of FSL 5.0. Example ------- >>> nipype_epicorrect = fieldmap_correction('nipype_epidewarp') >>> nipype_epicorrect.inputs.inputnode.in_file = 'diffusion.nii' >>> nipype_epicorrect.inputs.inputnode.in_mask = 'brainmask.nii' >>> nipype_epicorrect.inputs.inputnode.fieldmap_pha = 'phase.nii' >>> nipype_epicorrect.inputs.inputnode.fieldmap_mag = 'magnitude.nii' >>> nipype_epicorrect.inputs.inputnode.te_diff = 2.46 >>> nipype_epicorrect.inputs.inputnode.epi_echospacing = 0.77 >>> nipype_epicorrect.inputs.inputnode.encoding_direction = 'y' >>> nipype_epicorrect.run() # doctest: +SKIP Inputs:: inputnode.in_file - The volume acquired with EPI sequence inputnode.in_mask - A brain mask inputnode.fieldmap_pha - The phase difference map from the fieldmapping, registered to in_file inputnode.fieldmap_mag - The magnitud maps (usually 4D, one magnitude per GRE scan) from the fieldmapping, registered to in_file inputnode.te_diff - Time difference in msec. between TE in ms of the fieldmapping (usually a GRE sequence). inputnode.epi_echospacing - The effective echo spacing (aka dwell time) in msec. of the EPI sequence. If EPI was acquired with parallel imaging, then the effective echo spacing is eff_es = es / acc_factor. inputnode.encoding_direction - The phase encoding direction in EPI acquisition (default y) inputnode.vsm_sigma - Sigma value of the gaussian smoothing filter applied to the vsm (voxel shift map) Outputs:: outputnode.epi_corrected outputnode.out_vsm """ warnings.warn(('This workflow is deprecated from v.1.0.0, use ' 'nipype.workflows.dmri.preprocess.epi.sdc_fmb instead'), DeprecationWarning) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'in_file', 'in_mask', 'fieldmap_pha', 'fieldmap_mag', 'te_diff', 'epi_echospacing', 'vsm_sigma', 'encoding_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 magnitude (it is required by PreparedFieldMap) mask_mag = pe.Node(fsl.maths.ApplyMask(), name='mask_magnitude') # Run fsl_prepare_fieldmap fslprep = pe.Node(fsl.PrepareFieldmap(), name='prepare_fieldmap') if nocheck: fslprep.inputs.nocheck = True # Use FUGUE to generate the voxel shift map (vsm) vsm = pe.Node(fsl.FUGUE(save_shift=True), name='generate_vsm') # VSM demean is not anymore present in the epi_reg script # 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', 'out_vsm']), name='outputnode') pipeline.connect([ (inputnode, select_mag, [('fieldmap_mag', 'in_file')]), (inputnode, fslprep, [('fieldmap_pha', 'in_phase'), ('te_diff', 'delta_TE')]), (inputnode, mask_mag, [('in_mask', 'mask_file')]), (select_mag, mask_mag, [('roi_file', 'in_file')]), (mask_mag, fslprep, [('out_file', 'in_magnitude')]), (fslprep, vsm, [('out_fieldmap', 'phasemap_in_file')]), (inputnode, vsm, [('fieldmap_mag', 'in_file'), ('encoding_direction', 'unwarp_direction'), (('te_diff', _ms2sec), 'asym_se_time'), ('vsm_sigma', 'smooth2d'), (('epi_echospacing', _ms2sec), 'dwell_time')]), (mask_mag, vsm, [('out_file', 'mask_file')]), (inputnode, dwi_split, [('in_file', 'in_file')]), (dwi_split, dwi_applyxfm, [('out_files', 'in_file')]), (mask_mag, dwi_applyxfm, [('out_file', 'mask_file')]), (vsm, dwi_applyxfm, [('shift_out_file', 'shift_in_file')]), (inputnode, dwi_applyxfm, [('encoding_direction', 'unwarp_direction') ]), (dwi_applyxfm, dwi_merge, [('unwarped_file', 'in_files')]), (dwi_merge, outputnode, [('merged_file', 'epi_corrected')]), (vsm, outputnode, [('shift_out_file', 'out_vsm')]) ]) return pipeline
def fieldmapper(TE1=4.9, TE2=7.3, dwell_time=0.00035, unwarp_direction="y-", SinkTag="func_fieldmapcorr", wf_name="fieldmap_correction"): import os import PUMI.utils.globals as globals SinkDir = os.path.abspath(globals._SinkDir_ + "/" + SinkTag) if not os.path.exists(SinkDir): os.makedirs(SinkDir) ########################################### # HERE INSERT PORCUPINE GENERATED CODE # MUST DEFINE # OutJSON: file path to JSON file contaioning the output strings to be returned # variables can (should) use variable SinkDir (defined here as function argument) ########################################### # To do ########################################### # adjust number of cores with psutil.cpu_count() ########################################### # also subtract: # analysisflow = nipype.Workflow('FieldMapper') # analysisflow.base_dir = '.' ########################################### # Here comes the generated code ########################################### # This is a Nipype generator. Warning, here be dragons. # !/usr/bin/env python import sys import nipype import nipype.pipeline as pe import nipype.interfaces.utility as utility import nipype.interfaces.fsl as fsl import PUMI.utils.utils_math as utils_math import nipype.interfaces.io as io import PUMI.utils.QC as qc import PUMI.utils.utils_convert as utils_convert OutJSON = SinkDir + "/outputs.JSON" # Basic interface class generates identity mappings inputspec = pe.Node(utility.IdentityInterface(fields=[ 'in_file', 'magnitude', 'phase', 'TE1', 'TE2', 'dwell_time', 'unwarp_direction' ]), name='inputspec') #defaults: #inputspec.inputs.func = func #inputspec.inputs.magnitude = magnitude #inputspec.inputs.phase = phase inputspec.inputs.TE1 = TE1 inputspec.inputs.TE2 = TE2 inputspec.inputs.dwell_time = dwell_time inputspec.inputs.unwarp_direction = unwarp_direction # Wraps command **bet** bet = pe.MapNode(interface=fsl.BET(), name='bet', iterfield=['in_file']) bet.inputs.mask = True # Wraps command **fslmaths** erode = pe.MapNode(interface=fsl.ErodeImage(), name='erode', iterfield=['in_file']) # Wraps command **fslmaths** erode2 = pe.MapNode(interface=fsl.ErodeImage(), name='erode2', iterfield=['in_file']) # Custom interface wrapping function SubTwo subtract = pe.Node(interface=utils_math.SubTwo, name='subtract') # Custom interface wrapping function Abs abs = pe.Node(interface=utils_math.Abs, name='abs') # Wraps command **fsl_prepare_fieldmap** preparefm = pe.MapNode(interface=fsl.PrepareFieldmap(), name='preparefm', iterfield=['in_phase', 'in_magnitude']) # Wraps command **fugue** fugue = pe.MapNode(interface=fsl.FUGUE(), name='fugue', iterfield=['in_file', 'fmap_in_file', 'mask_file']) # Generic datasink module to store structured outputs outputspec = pe.Node(interface=io.DataSink(), name='outputspec') outputspec.inputs.base_directory = SinkDir outputspec.inputs.regexp_substitutions = [ ("func_fieldmapcorr/_NodeName_.{13}", "") ] # Generic datasink module to store structured outputs outputspec2 = pe.Node(interface=io.DataSink(), name='outputspec2') outputspec2.inputs.base_directory = SinkDir outputspec2.inputs.regexp_substitutions = [("_NodeName_.{13}", "")] myqc_orig = qc.vol2png("fielmap_correction", tag="original") myqc_unwarp = qc.vol2png("fielmap_correction", tag="unwarped") # Create a workflow to connect all those nodes analysisflow = nipype.Workflow(wf_name) analysisflow.base_dir = '.' analysisflow.connect(preparefm, 'out_fieldmap', outputspec2, 'fieldmap') analysisflow.connect(abs, 'abs', preparefm, 'delta_TE') analysisflow.connect(subtract, 'dif', abs, 'x') analysisflow.connect(inputspec, 'unwarp_direction', fugue, 'unwarp_direction') analysisflow.connect(fugue, 'unwarped_file', outputspec, 'func_fieldmapcorr') analysisflow.connect(preparefm, 'out_fieldmap', fugue, 'fmap_in_file') analysisflow.connect(erode2, 'out_file', fugue, 'mask_file') analysisflow.connect(bet, 'mask_file', erode2, 'in_file') analysisflow.connect(inputspec, 'dwell_time', fugue, 'dwell_time') analysisflow.connect(inputspec, 'in_file', fugue, 'in_file') analysisflow.connect(bet, 'out_file', erode, 'in_file') analysisflow.connect(inputspec, 'TE2', subtract, 'b') analysisflow.connect(inputspec, 'TE1', subtract, 'a') analysisflow.connect(inputspec, 'phase', preparefm, 'in_phase') analysisflow.connect(erode, 'out_file', preparefm, 'in_magnitude') analysisflow.connect(inputspec, 'magnitude', bet, 'in_file') analysisflow.connect(inputspec, 'magnitude', myqc_orig, 'inputspec.bg_image') analysisflow.connect(inputspec, 'in_file', myqc_orig, 'inputspec.overlay_image') analysisflow.connect(inputspec, 'magnitude', myqc_unwarp, 'inputspec.bg_image') analysisflow.connect(fugue, 'unwarped_file', myqc_unwarp, 'inputspec.overlay_image') # Run the workflow #plugin = 'MultiProc' # adjust your desired plugin here #plugin_args = {'n_procs': psutil.cpu_count()} # adjust to your number of cores #analysisflow.write_graph(graph2use='flat', format='png', simple_form=False) #analysisflow.run(plugin=plugin, plugin_args=plugin_args) #################################################################################################### # Porcupine generated code ends here #################################################################################################### #load and return json # you have to be aware the keys of the json map here #ret = json.load(open(OutJSON)) #return ret['func_fieldmapcorr'], ret['fieldmap'] return analysisflow
def apply_fieldmap(file_fmap_magn, file_fmap_phase, file_epi, file_epi_moco, file_surf, delta_te=1.02, smooth=2.5, udir="y-", bw=16.304, nerode=1, cleanup=True): """ This function computes a deformation field from a fieldmap acquisition and applies the inverse transformation to the undistorted surface. The following steps are performed: 1. get median time series 2. skullstrip epi 3. register fieldmap to epi 4. mask fieldmap 5. prepare field 6. get deforamtion field 7. apply inverse deformation to surfaces. 8. remove intermediate files (optional). To run the script, FSL and Freesurfer have to be in the PATH environment. The basenames of the surface files should be in freesurfer convention with the hemisphere indicated as prefix. Inputs: *fiele_fmap_magn: fieldmap magnitude image. *file_fmap_phase: fieldmap phase difference image. *file_epi: filename of raw time series. *file_epi_moco: filname of motion corrected time series. *file_surf: list of surface filnames. *delta_te: echo time difference of fieldmap in ms. *smooth: smoothing kernel for fieldmap unmasking. *udir: direction for fieldmap unmasking. *bw: BandwidthPerPixelPhaseEncode in Hz/px. *nerode: number of skullstrip mask eroding iterations. *cleanup: removes temporary files at the end of the script (boolean). created by Daniel Haenelt Date created: 31-01-2020 Last modified: 20-06-2020 """ import os import numpy as np import nibabel as nb from nipype.interfaces import fsl from lib.skullstrip.skullstrip_epi import skullstrip_epi from lib.io.get_filename import get_filename from lib.cmap.generate_coordinate_mapping import generate_coordinate_mapping from lib.surface.deform_surface import deform_surface # prepare path and filename path_fmap0, name_fmap0, ext_fmap0 = get_filename(file_fmap_magn) path_fmap1, name_fmap1, ext_fmap1 = get_filename(file_fmap_phase) path_data, name_data, ext_data = get_filename(file_epi) path_udata, name_udata, ext_udata = get_filename(file_epi_moco) # filename with file extension name_fmap0 += ext_fmap0 name_fmap1 += ext_fmap1 name_data += ext_data name_udata += ext_udata # change directory to fieldmap directory os.chdir(path_fmap0) # get matrix size in phase encoding direction from uncorrected epi data = nb.load(file_epi) phase_encode = data.header.get_dim_info()[1] ImageMatrixPhaseEncode = data.header["dim"][phase_encode+1] # calculate median epi udata = nb.load(file_epi_moco) arr_udata = udata.get_fdata() arr_udata_median = np.median(arr_udata, axis=3) udata_median = nb.Nifti1Image(arr_udata_median, udata.affine, udata.header) udata_median.header["dim"][0] = 3 udata_median.header["dim"][4] = 1 nb.save(udata_median, os.path.join(path_udata, "median_"+name_udata)) # calculate skullstrip mask of that image skullstrip_epi(os.path.join(path_udata, "median_"+name_udata), roi_size=10, scale=0.75, nerode=1, ndilate=2, savemask=True, cleanup=True) # erode skullstrip mask for j in range(nerode): erode = fsl.ErodeImage() erode.inputs.in_file = os.path.join(path_udata, "mask_median_"+name_udata) erode.inputs.output_type = "NIFTI" erode.inputs.out_file = os.path.join(path_udata, "mask_median_"+name_udata) erode.run() # register fmap1 to median epi (fsl.FLIRT) flirt = fsl.FLIRT() flirt.inputs.cost_func = "mutualinfo" flirt.inputs.dof = 6 flirt.inputs.interp = "trilinear" # trlinear, nearestneighbour, sinc or spline flirt.inputs.in_file = file_fmap_magn flirt.inputs.reference = os.path.join(path_udata, "median_"+name_udata) flirt.inputs.output_type = "NIFTI" flirt.inputs.out_file = os.path.join(path_fmap0, "r"+name_fmap0) flirt.inputs.out_matrix_file = os.path.join(path_fmap0, "fmap2epi.txt") flirt.run() # apply registration to fmap2 applyxfm = fsl.preprocess.ApplyXFM() applyxfm.inputs.in_file = file_fmap_phase applyxfm.inputs.reference = os.path.join(path_udata, "median_"+name_udata) applyxfm.inputs.in_matrix_file = os.path.join(path_fmap0, "fmap2epi.txt") applyxfm.inputs.interp = "trilinear" applyxfm.inputs.output_type = "NIFTI" applyxfm.inputs.out_file = os.path.join(path_fmap1, "r"+name_fmap1) applyxfm.inputs.apply_xfm = True applyxfm.run() # apply skullstrip mask to fmap1 and fmap2 and save with same header information fmap1_img = nb.load(os.path.join(path_fmap0, "r"+name_fmap0)) arr_fmap1 = fmap1_img.get_fdata() fmap2_img = nb.load(os.path.join(path_fmap1, "r"+name_fmap1)) arr_fmap2 = fmap2_img.get_fdata() mask_img = nb.load(os.path.join(path_udata, "mask_median_"+name_udata)) arr_mask = mask_img.get_fdata() arr_fmap1 = arr_fmap1 * arr_mask arr_fmap2 = (arr_fmap2 * arr_mask) arr_fmap2 = arr_fmap2 + np.abs(np.min(arr_fmap2)) arr_fmap2 = arr_fmap2 / np.max(arr_fmap2) * 4095 # rescale phase image to be within 0-4095 fmap1_img = nb.Nifti1Image(arr_fmap1, fmap1_img.affine, fmap1_img.header) nb.save(fmap1_img, os.path.join(path_fmap0, "pr"+name_fmap0)) fmap2_img = nb.Nifti1Image(arr_fmap2, fmap1_img.affine, fmap1_img.header) nb.save(fmap2_img, os.path.join(path_fmap1, "pr"+name_fmap1)) # prepare fieldmap (saves fieldmap in rad/s) prepare = fsl.PrepareFieldmap() prepare.inputs.in_magnitude = os.path.join(path_fmap0, "pr"+name_fmap0) prepare.inputs.in_phase = os.path.join(path_fmap1, "pr"+name_fmap1) prepare.inputs.out_fieldmap = os.path.join(path_fmap0, "fieldmap.nii") prepare.inputs.delta_TE = delta_te prepare.inputs.scanner = "SIEMENS" prepare.inputs.output_type = "NIFTI" prepare.run() # effective echo spacing in s dwell_time = 1/(bw * ImageMatrixPhaseEncode) # unmask fieldmap (fsl.FUGUE) fugue = fsl.preprocess.FUGUE() fugue.inputs.in_file = os.path.join(path_udata, name_udata) fugue.inputs.dwell_time = dwell_time fugue.inputs.fmap_in_file = os.path.join(path_fmap0, "fieldmap.nii") fugue.inputs.smooth3d = smooth fugue.inputs.unwarp_direction = udir fugue.inputs.save_shift = True fugue.inputs.shift_out_file = os.path.join(path_fmap0, "vdm.nii") fugue.inputs.output_type = "NIFTI" fugue.run() # warp coordinate mapping generate_coordinate_mapping(file_epi, 0, path_fmap0, suffix="fmap", time=False, write_output=True) # apply inverse fieldmap to coordinate mapping fugue = fsl.preprocess.FUGUE() fugue.inputs.in_file = os.path.join(path_fmap0, "cmap_fmap.nii") fugue.inputs.shift_in_file = os.path.join(path_fmap0, "vdm.nii") fugue.inputs.forward_warping = False fugue.inputs.unwarp_direction = udir fugue.inputs.output_type = "NIFTI" fugue.run() # apply cmap to surface for i in range(len(file_surf)): path_surf, hemi, name_surf = get_filename(file_surf[i]) deform_surface(input_surf=file_surf[i], input_orig=os.path.join(path_udata, "median_"+name_udata), input_deform=os.path.join(path_fmap0, "cmap_fmap_unwarped.nii"), input_target=os.path.join(path_udata, "median_"+name_udata), hemi=hemi, path_output=path_surf, input_mask=None, interp_method="trilinear", smooth_iter=0, flip_faces=False, cleanup=True) # delete created files if cleanup: os.remove(os.path.join(path_fmap0, "cmap_fmap.nii")) os.remove(os.path.join(path_fmap0, "cmap_fmap_unwarped.nii")) os.remove(os.path.join(path_fmap0, "fieldmap.nii")) os.remove(os.path.join(path_fmap0, "fmap2epi.txt")) os.remove(os.path.join(path_fmap1, os.path.splitext(name_fmap1)[0]+"_flirt.mat")) os.remove(os.path.join(path_fmap0, "r"+name_fmap0)) os.remove(os.path.join(path_fmap0, "pr"+name_fmap0)) os.remove(os.path.join(path_fmap1, "r"+name_fmap1)) os.remove(os.path.join(path_fmap1, "pr"+name_fmap1)) os.remove(os.path.join(path_fmap0, os.path.splitext(name_udata)[0])+"_unwarped.nii") os.remove(os.path.join(path_fmap0, "vdm.nii")) os.remove(os.path.join(path_udata, "mask_median_"+name_udata)) os.remove(os.path.join(path_udata, "median_"+name_udata)) os.remove(os.path.join(path_udata, "pmedian_"+name_udata))
name='magVolTrim') magBiasCorrect = pe.Node(ants.N4BiasFieldCorrection( dimension=3, n_iterations=[50, 50, 30, 20], convergence_threshold=0.0, shrink_factor=3, bspline_fitting_distance=300), name='magBiasCorrect') magExtract = pe.Node(fsl.BET(robust=True, frac=0.6, vertical_gradient=0.18), name='magExtract') erode = pe.Node(fsl.maths.ErodeImage(output_type='NIFTI_GZ'), name='erode') prepareFmap = pe.Node(fsl.PrepareFieldmap(delta_TE=deltaTE, scanner='SIEMENS'), name='prepareFmap') filterFmap = pe.Node(fsl.preprocess.FUGUE(median_2dfilter=True, save_fmap=True), name='filterFmap') # Warp fieldmap before registration magWarp = pe.Node(fsl.preprocess.FUGUE(dwell_time=effectEcho, unwarp_direction=unwarpDir, forward_warping=True, nokspace=True), name='magWarp') epiMean = pe.Node(fsl.maths.MeanImage(dimension='T', output_type='NIFTI_GZ'), name='epiMean')