def afni_unifize(in_file, write_dir=None, out_file=None, caching=False, terminal_output='allatonce', verbose=True, environ=None, copy_geometry=False, **unifize_kwargs): if write_dir is None: write_dir = os.path.dirname(in_file) if environ is None: environ = {'AFNI_DECONFLICT': 'OVERWRITE'} if caching: memory = Memory(write_dir) copy_geom = memory.cache(fsl.CopyGeom) unifize = memory.cache(afni.Unifize) copy = memory.cache(afni.Copy) unifize.interface().set_default_terminal_output(terminal_output) copy.interface().set_default_terminal_output(terminal_output) else: copy_geom = fsl.CopyGeom(terminal_output=terminal_output).run unifize = afni.Unifize(terminal_output=terminal_output).run copy = afni.Copy(terminal_output=terminal_output).run if out_file is None: out_file = fname_presuffix(in_file, suffix='_unifized', newpath=write_dir) if copy_geometry: unifized_file = fname_presuffix(in_file, suffix='_unifized_rough_geom', newpath=write_dir) else: unifized_file = out_file out_unifize = unifize(in_file=in_file, out_file=unifized_file, environ=environ, quiet=not (verbose), **unifize_kwargs) if copy_geometry: out_copy = copy(in_file=out_unifize.outputs.out_file, out_file=out_file, environ=environ) out_copy_geom = copy_geom(dest_file=out_copy.outputs.out_file, in_file=in_file) return out_file
def ants_n4(in_file, write_dir=None, caching=False, terminal_output='allatonce', environ=None, copy_geometry=True): if write_dir is None: write_dir = os.path.dirname(in_file) if environ is None: environ = {'AFNI_DECONFLICT': 'OVERWRITE'} if caching: memory = Memory(write_dir) bias_correct = memory.cache(ants.N4BiasFieldCorrection) copy = memory.cache(afni.Copy) copy_geom = memory.cache(fsl.CopyGeom) bias_correct.interface().set_default_terminal_output(terminal_output) copy.interface().set_default_terminal_output(terminal_output) else: bias_correct = ants.N4BiasFieldCorrection( terminal_output=terminal_output).run copy = afni.Copy(terminal_output=terminal_output).run copy_geom = fsl.CopyGeom(terminal_output=terminal_output).run unbiased_file = fname_presuffix(in_file, suffix='_n4', newpath=write_dir) if copy_geometry: output_image = fname_presuffix(in_file, suffix='_n4_rough_geom', newpath=write_dir) else: output_image = unbiased_file out_bias_correct = bias_correct( input_image=in_file, shrink_factor=_compute_n4_max_shrink(in_file), output_image=output_image) if copy_geometry: out_copy = copy(in_file=out_bias_correct.outputs.output_image, out_file=unbiased_file, environ=environ) out_copy_geom = copy_geom(dest_file=out_copy.outputs.out_file, in_file=in_file) return unbiased_file
def create_preprocess_phase_wf(): """Create's phase preprocessing workflow with the following steps: 1) Convert data to float 2) Determine scaling required for radians 3) Apply radian scaling 4) Convert to real and imaginary 5) Apply magnitude motion correction parameters 6) Correct geometry changes (AFNI issue) 7) Convert back to phase 8) Unwrap and detrend data 9) Mask data using magnitude mask 10) Calculate noise from data """ preprocphase = pe.Workflow(name="preprocphase") preprocphase.config['execution']['remove_unnecessary_outputs'] = False # define inputs inputspec = pe.Node( ul.IdentityInterface(fields=[ 'input_phase', # raw phase data 'input_mag', # raw mag data 'motion_par', # afni transform concatenated from magnitude data 'mask_file', # bet mask from magnitude data 'rest', # volumes of rest in block design 'task', # volumes of task in block design ]), name='inputspec') # 1) Convert data to float img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float', op_string='', suffix='_dtype'), iterfield=['in_file'], name='img2float') # 2) Determine radian scaling required findscaling = pe.MapNode(interface=ul.Function( input_names=['in_file'], output_names=['scaling_arg'], function=findscalingarg), name='findscaling', iterfield=['in_file']) # 3) Apply radian scaling convert2rad = pe.MapNode(interface=fsl.maths.MathsCommand(), name='convert2rad', iterfield=['in_file', 'args']) # 4) Convert to real and imaginary (2 step process) makecomplex = pe.MapNode(interface=fsl.Complex(complex_polar=True), name='makecomplex', iterfield=['magnitude_in_file', 'phase_in_file']) splitcomplex = pe.MapNode(interface=fsl.Complex(real_cartesian=True), name='splitcomplex', iterfield=['complex_in_file']) # 5) Apply magnitude motion correction parameters mocoreal = pe.MapNode(interface=afni.Allineate(), name='mocoreal', iterfield=['in_file', 'in_matrix']) mocoreal.inputs.outputtype = 'NIFTI_GZ' mocoreal.inputs.out_file = 'mocophase.nii.gz' mocoreal.inputs.num_threads = 2 mocoimag = mocoreal.clone('mocoimag') # 6) Correct geometry changes (AFNI issue) cpgeommocoreal = pe.MapNode(interface=fsl.CopyGeom(), name='cpgeommoco', iterfield=['dest_file', 'in_file']) cpgeommocoimag = cpgeommocoreal.clone('cpgeommocoimag') cpgeommocophase = cpgeommocoreal.clone('cpgeommocophase') # 7) Convert back to phase (2 step process) makecomplexmoco = pe.MapNode( interface=fsl.Complex(complex_cartesian=True), name='makecomplexmoco', iterfield=['real_in_file', 'imaginary_in_file']) splitcomplexmoco = pe.MapNode(interface=fsl.Complex(real_polar=True), name='splitcomplexmoco', iterfield=['complex_in_file']) # 8) Remove first volume, unwrap and detrend phase data prepphase = pe.MapNode(interface=pp.PreprocessPhase(), name='prepphase', iterfield=['phase']) # 9) Mask data using magnitude mask maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file'], name='maskfunc') # 10) Calculate noise from data calcSNR = pe.MapNode(interface=pp.RestAverage(), name='calcSNR', iterfield=['func', 'rest', 'task']) # outputspec outputspec = pe.Node(ul.IdentityInterface( fields=['proc_phase', 'uw_phase', 'delta_phase', 'std_phase']), name='outputspec') preprocphase = pe.Workflow(name='preprocphase') preprocphase.connect([ (inputspec, img2float, [('input_phase', 'in_file')]), # 1 (inputspec, findscaling, [('input_phase', 'in_file')]), # 2 (findscaling, convert2rad, [('scaling_arg', 'args')]), (img2float, convert2rad, [('out_file', 'in_file')]), (convert2rad, makecomplex, [('out_file', 'phase_in_file')]), # 3 (inputspec, makecomplex, [('input_mag', 'magnitude_in_file')]), (makecomplex, splitcomplex, [('complex_out_file', 'complex_in_file') ]), # 4 (inputspec, mocoreal, [('motion_par', 'in_matrix')]), # 5 real (splitcomplex, mocoreal, [('real_out_file', 'in_file')]), (mocoreal, cpgeommocoreal, [('out_file', 'dest_file')]), #6 real (img2float, cpgeommocoreal, [('out_file', 'in_file')]), (inputspec, mocoimag, [('motion_par', 'in_matrix')]), # 5 imag (splitcomplex, mocoimag, [('imaginary_out_file', 'in_file')]), (mocoimag, cpgeommocoimag, [('out_file', 'dest_file')]), # 6 imag (img2float, cpgeommocoimag, [('out_file', 'in_file')]), (cpgeommocoreal, makecomplexmoco, [('out_file', 'real_in_file')]), # 7 (cpgeommocoimag, makecomplexmoco, [('out_file', 'imaginary_in_file')]), (makecomplexmoco, splitcomplexmoco, [('complex_out_file', 'complex_in_file')]), (splitcomplexmoco, cpgeommocophase, [('phase_out_file', 'dest_file')]), (img2float, cpgeommocophase, [('out_file', 'in_file')]), (cpgeommocophase, prepphase, [('out_file', 'phase')]), # 8 (prepphase, maskfunc, [('detrended_phase', 'in_file')]), # 9 (inputspec, maskfunc, [('mask_file', 'in_file2')]), (maskfunc, outputspec, [('out_file', 'proc_phase')]), (prepphase, outputspec, [('uw_phase', 'uw_phase')]), (prepphase, outputspec, [('delta_phase', 'delta_phase')]), ( inputspec, calcSNR, [ ('rest', 'rest'), # 10 ('task', 'task') ]), (prepphase, calcSNR, [('detrended_phase', 'func')]), (calcSNR, outputspec, [('noise', 'std_phase')]) ]) return preprocphase
pipe.connect(bandpass_filter, 'out_file', graph, 'inputspec.subject') pipe.connect(preproc, 'outputspec.func_brain_mask', graph, 'inputspec.template') ######################################################################################################################## # get fisrt element of results get_element = pe.MapNode(interface=util.Function(input_names=['list', 'index'], output_names=['out'], function=get_element), name='get_element', iterfield=['list']) get_element.inputs.index = 0 pipe.connect(graph, 'outputspec.centrality_outputs', get_element, 'list') copg = pe.MapNode(interface=fsl.CopyGeom(), name="copy_geom", iterfield=['in_file', 'dest_file']) pipe.connect(get_element, 'out', copg, 'dest_file') pipe.connect(preproc, 'outputspec.func_brain_mask', copg, 'in_file') ######################################################################################################################## # get_zscore #from CPAC.utils import get_zscore zscore_cent = cent.get_zscore('cent', wf_name='Ztrans_cent') pipe.connect(preproc, 'outputspec.func_brain_mask', zscore_cent, 'inputspec.mask_file') pipe.connect(copg, 'out_file', zscore_cent, 'inputspec.input_file')
def coregister_fmri_session(session_data, t_r, write_dir, brain_volume, use_rats_tool=True, slice_timing=True, prior_rigid_body_registration=False, caching=False, voxel_size_x=.1, voxel_size_y=.1, verbose=True, **environ_kwargs): """ Coregistration of the subject's functional and anatomical images. The functional volume is aligned to the anatomical, first with a rigid body registration and then on a per-slice basis (only a fine correction, this is mostly for correction of EPI distortion). Parameters ---------- session_data : sammba.registration.SessionData Single animal data, giving paths to its functional and anatomical image, as well as it identifier. t_r : float Repetition time for the EPI, in seconds. write_dir : str Directory to save the output and temporary images. brain_volume : int Volume of the brain in mm3 used for brain extraction. Typically 400 for mouse and 1800 for rat. use_rats_tool : bool, optional If True, brain mask is computed using RATS Mathematical Morphology. Otherwise, a histogram-based brain segmentation is used. prior_rigid_body_registration : bool, optional If True, a rigid-body registration of the anat to the func is performed prior to the warp. Useful if the images headers have missing/wrong information. voxel_size_x : float, optional Resampling resolution for the x-axis, in mm. voxel_size_y : float, optional Resampling resolution for the y-axis, in mm. caching : bool, optional Wether or not to use caching. verbose : bool, optional If True, all steps are verbose. Note that caching implies some verbosity in any case. environ_kwargs : extra arguments keywords Extra arguments keywords, passed to interfaces environ variable. Returns ------- the same sequence with each animal_data updated: the following attributes are added - `output_dir_` : str Path to the output directory. - `coreg_func_` : str Path to paths to the coregistered functional image. - `coreg_anat_` : str Path to paths to the coregistered functional image. - `coreg_transform_` : str Path to the transform from anat to func. Notes ----- If `use_rats_tool` is turned on, RATS tool is used for brain extraction and has to be cited. For more information, see `RATS <http://www.iibi.uiowa.edu/content/rats-overview/>`_ """ func_filename = session_data.func anat_filename = session_data.anat environ = {'AFNI_DECONFLICT': 'OVERWRITE'} for (key, value) in environ_kwargs.items(): environ[key] = value if verbose: terminal_output = 'allatonce' else: terminal_output = 'none' if use_rats_tool: if segmentation.interfaces.Info().version() is None: raise ValueError('Can not locate RATS') else: ComputeMask = segmentation.MathMorphoMask else: ComputeMask = segmentation.HistogramMask if ants.base.Info().version is None: raise ValueError('Can not locate ANTS') if caching: memory = Memory(write_dir) tshift = memory.cache(afni.TShift) clip_level = memory.cache(afni.ClipLevel) volreg = memory.cache(afni.Volreg) allineate = memory.cache(afni.Allineate) tstat = memory.cache(afni.TStat) compute_mask = memory.cache(ComputeMask) calc = memory.cache(afni.Calc) allineate = memory.cache(afni.Allineate) allineate2 = memory.cache(afni.Allineate) unifize = memory.cache(afni.Unifize) bias_correct = memory.cache(ants.N4BiasFieldCorrection) catmatvec = memory.cache(afni.CatMatvec) warp = memory.cache(afni.Warp) resample = memory.cache(afni.Resample) slicer = memory.cache(afni.ZCutUp) warp_apply = memory.cache(afni.NwarpApply) qwarp = memory.cache(afni.Qwarp) merge = memory.cache(afni.Zcat) copy_geom = memory.cache(fsl.CopyGeom) overwrite = False for step in [ tshift, volreg, allineate, allineate2, tstat, compute_mask, calc, unifize, resample, slicer, warp_apply, qwarp, merge ]: step.interface().set_default_terminal_output(terminal_output) else: tshift = afni.TShift(terminal_output=terminal_output).run clip_level = afni.ClipLevel().run volreg = afni.Volreg(terminal_output=terminal_output).run allineate = afni.Allineate(terminal_output=terminal_output).run allineate2 = afni.Allineate(terminal_output=terminal_output ).run # TODO: remove after fixed bug tstat = afni.TStat(terminal_output=terminal_output).run compute_mask = ComputeMask().run calc = afni.Calc(terminal_output=terminal_output).run unifize = afni.Unifize(terminal_output=terminal_output).run bias_correct = ants.N4BiasFieldCorrection( terminal_output=terminal_output).run catmatvec = afni.CatMatvec().run warp = afni.Warp().run resample = afni.Resample(terminal_output=terminal_output).run slicer = afni.ZCutUp(terminal_output=terminal_output).run warp_apply = afni.NwarpApply(terminal_output=terminal_output).run qwarp = afni.Qwarp(terminal_output=terminal_output).run merge = afni.Zcat(terminal_output=terminal_output).run copy_geom = fsl.CopyGeom(terminal_output=terminal_output).run overwrite = True session_data._check_inputs() output_dir = os.path.join(os.path.abspath(write_dir), session_data.animal_id) session_data._set_output_dir_(output_dir) current_dir = os.getcwd() os.chdir(output_dir) output_files = [] ####################################### # Correct functional for slice timing # ####################################### if slice_timing: out_tshift = tshift(in_file=func_filename, outputtype='NIFTI_GZ', tpattern='altplus', tr=str(t_r), environ=environ) func_filename = out_tshift.outputs.out_file output_files.append(func_filename) ################################################ # Register functional volumes to the first one # ################################################ # XXX why do you need a thresholded image ? out_clip_level = clip_level(in_file=func_filename) out_calc_threshold = calc(in_file_a=func_filename, expr='ispositive(a-{0}) * a'.format( out_clip_level.outputs.clip_val), outputtype='NIFTI_GZ') thresholded_filename = out_calc_threshold.outputs.out_file out_volreg = volreg( # XXX dfile not saved in_file=thresholded_filename, outputtype='NIFTI_GZ', environ=environ, oned_file=fname_presuffix(thresholded_filename, suffix='Vr.1Dfile.1D', use_ext=False), oned_matrix_save=fname_presuffix(thresholded_filename, suffix='Vr.aff12.1D', use_ext=False)) # Apply the registration to the whole head out_allineate = allineate(in_file=func_filename, master=func_filename, in_matrix=out_volreg.outputs.oned_matrix_save, out_file=fname_presuffix(func_filename, suffix='Av'), environ=environ) # 3dAllineate removes the obliquity. This is not a good way to readd it as # removes motion correction info in the header if it were an AFNI file...as # it happens it's NIfTI which does not store that so irrelevant! out_copy_geom = copy_geom(dest_file=out_allineate.outputs.out_file, in_file=out_volreg.outputs.out_file) allineated_filename = out_copy_geom.outputs.out_file # Create a (hopefully) nice mean image for use in the registration out_tstat = tstat(in_file=allineated_filename, args='-mean', outputtype='NIFTI_GZ', environ=environ) # Update outputs output_files.extend([ thresholded_filename, out_volreg.outputs.oned_matrix_save, out_volreg.outputs.out_file, out_volreg.outputs.md1d_file, allineated_filename, out_tstat.outputs.out_file ]) ########################################### # Corret anat and func for intensity bias # ########################################### # Correct the functional average for intensities bias out_bias_correct = bias_correct(input_image=out_tstat.outputs.out_file) unbiased_func_filename = out_bias_correct.outputs.output_image # Bias correct the antomical image out_unifize = unifize(in_file=anat_filename, outputtype='NIFTI_GZ', environ=environ) unbiased_anat_filename = out_unifize.outputs.out_file # Update outputs output_files.extend([unbiased_func_filename, unbiased_anat_filename]) ############################################# # Rigid-body registration anat -> mean func # ############################################# if prior_rigid_body_registration: # Mask the mean functional volume outside the brain. out_clip_level = clip_level(in_file=unbiased_func_filename) out_compute_mask_func = compute_mask( in_file=unbiased_func_filename, volume_threshold=brain_volume, intensity_threshold=int(out_clip_level.outputs.clip_val)) out_cacl_func = calc(in_file_a=unbiased_func_filename, in_file_b=out_compute_mask_func.outputs.out_file, expr='a*b', outputtype='NIFTI_GZ', environ=environ) # Mask the anatomical volume outside the brain. out_clip_level = clip_level(in_file=unbiased_anat_filename) out_compute_mask_anat = compute_mask( in_file=unbiased_anat_filename, volume_threshold=brain_volume, intensity_threshold=int(out_clip_level.outputs.clip_val)) out_cacl_anat = calc(in_file_a=unbiased_anat_filename, in_file_b=out_compute_mask_anat.outputs.out_file, expr='a*b', outputtype='NIFTI_GZ', environ=environ) # Compute the transformation from functional to anatomical brain # XXX: why in this sense out_allineate = allineate2( in_file=out_cacl_func.outputs.out_file, reference=out_cacl_anat.outputs.out_file, out_matrix=fname_presuffix(out_cacl_func.outputs.out_file, suffix='_shr.aff12.1D', use_ext=False), center_of_mass='', warp_type='shift_rotate', out_file=fname_presuffix(out_cacl_func.outputs.out_file, suffix='_shr'), environ=environ) rigid_transform_file = out_allineate.outputs.out_matrix output_files.extend([ out_compute_mask_func.outputs.out_file, out_cacl_func.outputs.out_file, out_compute_mask_anat.outputs.out_file, out_cacl_anat.outputs.out_file, rigid_transform_file, out_allineate.outputs.out_file ]) # apply the inverse transform to register the anatomical to the func catmatvec_out_file = fname_presuffix(rigid_transform_file, suffix='INV') out_catmatvec = catmatvec(in_file=[(rigid_transform_file, 'I')], oneline=True, out_file=catmatvec_out_file) output_files.append(out_catmatvec.outputs.out_file) out_allineate = allineate(in_file=unbiased_anat_filename, master=unbiased_func_filename, in_matrix=out_catmatvec.outputs.out_file, out_file=fname_presuffix( unbiased_anat_filename, suffix='_shr_in_func_space'), environ=environ) allineated_anat_filename = out_allineate.outputs.out_file output_files.append(allineated_anat_filename) else: allineated_anat_filename = unbiased_anat_filename ############################################ # Nonlinear registration anat -> mean func # ############################################ # 3dWarp doesn't put the obliquity in the header, so do it manually # This step generates one file per slice and per time point, so we are # making sure they are removed at the end out_warp = warp(in_file=allineated_anat_filename, oblique_parent=unbiased_func_filename, interp='quintic', gridset=unbiased_func_filename, outputtype='NIFTI_GZ', verbose=True, environ=environ) registered_anat_filename = out_warp.outputs.out_file registered_anat_oblique_filename = fix_obliquity(registered_anat_filename, unbiased_func_filename, verbose=verbose) # Concatenate all the anat to func tranforms mat_filename = fname_presuffix(registered_anat_filename, suffix='_warp.mat', use_ext=False) # XXX Handle this correctly according to caching if not os.path.isfile(mat_filename): np.savetxt(mat_filename, [out_warp.runtime.stdout], fmt='%s') output_files.append(mat_filename) transform_filename = fname_presuffix(registered_anat_filename, suffix='_anat_to_func.aff12.1D', use_ext=False) if prior_rigid_body_registration: _ = catmatvec(in_file=[(mat_filename, 'ONELINE'), (rigid_transform_file, 'ONELINE')], oneline=True, out_file=transform_filename) else: _ = catmatvec(in_file=[(mat_filename, 'ONELINE')], oneline=True, out_file=transform_filename) ################################################## # Per-slice non-linear registration func -> anat # ################################################## # Slice anatomical image anat_img = nibabel.load(registered_anat_oblique_filename) anat_n_slices = anat_img.header.get_data_shape()[2] sliced_registered_anat_filenames = [] for slice_n in range(anat_n_slices): out_slicer = slicer(in_file=registered_anat_oblique_filename, keep='{0} {0}'.format(slice_n), out_file=fname_presuffix( registered_anat_oblique_filename, suffix='Sl%d' % slice_n), environ=environ) oblique_slice = fix_obliquity(out_slicer.outputs.out_file, registered_anat_oblique_filename, verbose=verbose) sliced_registered_anat_filenames.append(oblique_slice) # Slice mean functional sliced_bias_corrected_filenames = [] img = nibabel.load(func_filename) n_slices = img.header.get_data_shape()[2] for slice_n in range(n_slices): out_slicer = slicer(in_file=unbiased_func_filename, keep='{0} {0}'.format(slice_n), out_file=fname_presuffix(unbiased_func_filename, suffix='Sl%d' % slice_n), environ=environ) oblique_slice = fix_obliquity(out_slicer.outputs.out_file, unbiased_func_filename, verbose=verbose) sliced_bias_corrected_filenames.append(oblique_slice) # Below line is to deal with slices where there is no signal (for example # rostral end of some anatomicals) # The inverse warp frequently fails, Resampling can help it work better # XXX why specifically .1 in voxel_size ? voxel_size_z = anat_img.header.get_zooms()[2] resampled_registered_anat_filenames = [] for sliced_registered_anat_filename in sliced_registered_anat_filenames: out_resample = resample(in_file=sliced_registered_anat_filename, voxel_size=(voxel_size_x, voxel_size_y, voxel_size_z), outputtype='NIFTI_GZ', environ=environ) resampled_registered_anat_filenames.append( out_resample.outputs.out_file) resampled_bias_corrected_filenames = [] for sliced_bias_corrected_filename in sliced_bias_corrected_filenames: out_resample = resample(in_file=sliced_bias_corrected_filename, voxel_size=(voxel_size_x, voxel_size_y, voxel_size_z), outputtype='NIFTI_GZ', environ=environ) resampled_bias_corrected_filenames.append( out_resample.outputs.out_file) # single slice non-linear functional to anatomical registration warped_slices = [] warp_filenames = [] for (resampled_bias_corrected_filename, resampled_registered_anat_filename) in zip( resampled_bias_corrected_filenames, resampled_registered_anat_filenames): warped_slice = fname_presuffix(resampled_bias_corrected_filename, suffix='_qw') out_qwarp = qwarp( in_file=resampled_bias_corrected_filename, base_file=resampled_registered_anat_filename, iwarp=True, # XXX: is this necessary noneg=True, blur=[0], nmi=True, noXdis=True, allineate=True, allineate_opts='-parfix 1 0 -parfix 2 0 -parfix 3 0 ' '-parfix 4 0 -parfix 5 0 -parfix 6 0 ' '-parfix 7 0 -parfix 9 0 ' '-parfix 10 0 -parfix 12 0', out_file=warped_slice, environ=environ) warped_slices.append(out_qwarp.outputs.warped_source) warp_filenames.append(out_qwarp.outputs.source_warp) output_files.append(out_qwarp.outputs.base_warp) # There are files geenrated by the allineate option output_files.extend([ fname_presuffix(out_qwarp.outputs.warped_source, suffix='_Allin'), fname_presuffix(out_qwarp.outputs.warped_source, suffix='_Allin.nii', use_ext=False), fname_presuffix(out_qwarp.outputs.warped_source, suffix='_Allin.aff12.1D', use_ext=False) ]) # Resample the mean volume back to the initial resolution, voxel_size = nibabel.load(unbiased_func_filename).header.get_zooms() resampled_warped_slices = [] for warped_slice in warped_slices: out_resample = resample(in_file=warped_slice, voxel_size=voxel_size, outputtype='NIFTI_GZ', environ=environ) resampled_warped_slices.append(out_resample.outputs.out_file) # fix the obliquity resampled_warped_slices_oblique = [] for (sliced_registered_anat_filename, resampled_warped_slice) in zip(sliced_registered_anat_filenames, resampled_warped_slices): oblique_slice = fix_obliquity(resampled_warped_slice, sliced_registered_anat_filename, verbose=verbose) resampled_warped_slices_oblique.append(oblique_slice) # slice functional sliced_func_filenames = [] for slice_n in range(n_slices): out_slicer = slicer(in_file=allineated_filename, keep='{0} {0}'.format(slice_n), out_file=fname_presuffix(allineated_filename, suffix='Sl%d' % slice_n), environ=environ) oblique_slice = fix_obliquity(out_slicer.outputs.out_file, allineated_filename, verbose=verbose) sliced_func_filenames.append(oblique_slice) # Apply the precomputed warp slice by slice warped_func_slices = [] for (sliced_func_filename, warp_filename) in zip(sliced_func_filenames, warp_filenames): out_warp_apply = warp_apply(in_file=sliced_func_filename, master=sliced_func_filename, warp=warp_filename, out_file=fname_presuffix( sliced_func_filename, suffix='_qw'), environ=environ) warped_func_slices.append(out_warp_apply.outputs.out_file) # Finally, merge all slices ! out_merge_func = merge(in_files=warped_func_slices, outputtype='NIFTI_GZ', environ=environ) # Fix the obliquity merged_oblique = fix_obliquity(out_merge_func.outputs.out_file, allineated_filename, verbose=verbose) # Update the fmri data setattr(session_data, "coreg_func_", merged_oblique) setattr(session_data, "coreg_anat_", registered_anat_oblique_filename) setattr(session_data, "coreg_transform_", transform_filename) os.chdir(current_dir) # Collect the outputs output_files.extend(sliced_registered_anat_filenames + sliced_bias_corrected_filenames + resampled_registered_anat_filenames + resampled_bias_corrected_filenames + warped_slices + warp_filenames + resampled_warped_slices_oblique + sliced_func_filenames + warped_func_slices) if not caching: for out_file in output_files: if os.path.isfile(out_file): os.remove(out_file)
def _realign(func_filename, write_dir, caching=False, terminal_output='allatonce', environ=None): if environ is None: environ = {'AFNI_DECONFLICT': 'OVERWRITE'} if caching: memory = Memory(write_dir) clip_level = memory.cache(afni.ClipLevel) threshold = memory.cache(fsl.Threshold) volreg = memory.cache(afni.Volreg) allineate = memory.cache(afni.Allineate) copy = memory.cache(afni.Copy) copy_geom = memory.cache(fsl.CopyGeom) tstat = memory.cache(afni.TStat) for step in [threshold, volreg, allineate, tstat, copy, copy_geom]: step.interface().set_default_terminal_output(terminal_output) else: clip_level = afni.ClipLevel().run threshold = fsl.Threshold(terminal_output=terminal_output).run volreg = afni.Volreg(terminal_output=terminal_output).run allineate = afni.Allineate(terminal_output=terminal_output).run copy = afni.Copy(terminal_output=terminal_output).run copy_geom = fsl.CopyGeom(terminal_output=terminal_output).run tstat = afni.TStat(terminal_output=terminal_output).run out_clip_level = clip_level(in_file=func_filename) out_threshold = threshold(in_file=func_filename, thresh=out_clip_level.outputs.clip_val, out_file=fname_presuffix(func_filename, suffix='_thresholded', newpath=write_dir)) thresholded_filename = out_threshold.outputs.out_file out_volreg = volreg( # XXX dfile not saved in_file=thresholded_filename, out_file=fname_presuffix(thresholded_filename, suffix='_volreg', newpath=write_dir), environ=environ, oned_file=fname_presuffix(thresholded_filename, suffix='_volreg.1Dfile.1D', use_ext=False, newpath=write_dir), oned_matrix_save=fname_presuffix(thresholded_filename, suffix='_volreg.aff12.1D', use_ext=False, newpath=write_dir)) # Apply the registration to the whole head out_allineate = allineate(in_file=func_filename, master=func_filename, in_matrix=out_volreg.outputs.oned_matrix_save, out_file=fname_presuffix(func_filename, suffix='_volreg', newpath=write_dir), environ=environ) # 3dAllineate removes the obliquity. This is not a good way to readd it as # removes motion correction info in the header if it were an AFNI file...as # it happens it's NIfTI which does not store that so irrelevant! out_copy = copy(in_file=out_allineate.outputs.out_file, out_file=fname_presuffix(out_allineate.outputs.out_file, suffix='_oblique', newpath=write_dir), environ=environ) out_copy_geom = copy_geom(dest_file=out_copy.outputs.out_file, in_file=out_volreg.outputs.out_file) oblique_allineated_filename = out_copy_geom.outputs.out_file # Create a (hopefully) nice mean image for use in the registration out_tstat = tstat(in_file=oblique_allineated_filename, args='-mean', out_file=fname_presuffix(oblique_allineated_filename, suffix='_tstat', newpath=write_dir), environ=environ) # Remove intermediate outputs if not caching: for output_file in [ thresholded_filename, out_volreg.outputs.oned_matrix_save, out_volreg.outputs.out_file, out_volreg.outputs.md1d_file, out_allineate.outputs.out_file ]: os.remove(output_file) return (oblique_allineated_filename, out_tstat.outputs.out_file, out_volreg.outputs.oned_file)
def preprocess_channels_pipeline(self, **name_maps): pipeline = self.new_pipeline( 'preprocess_channels', name_maps=name_maps, desc=("Convert channel signals in complex coords to polar coords " "and combine")) if (self.provided('header_image') or self.branch('reorient_to_std') or self.parameter('force_channel_flip') is not None): # Read channel files reorient them into standard space and then # write back to directory list_channels = pipeline.add( 'list_channels', ListDir(), inputs={ 'directory': ('channels', multi_nifti_gz_format)}) if self.parameter('force_channel_flip') is not None: force_flip = pipeline.add( 'flip_dims', fsl.SwapDimensions( new_dims=tuple(self.parameter('force_channel_flip'))), inputs={ 'in_file': (list_channels, 'files')}, iterfield=['in_file']) geom_dest_file = (force_flip, 'out_file') else: geom_dest_file = (list_channels, 'files') if self.provided('header_image'): # If header image is provided stomp its geometry over the # acquired channels copy_geom = pipeline.add( 'qsm_copy_geometry', fsl.CopyGeom( output_type='NIFTI_GZ'), inputs={ 'in_file': ('header_image', nifti_gz_format), 'dest_file': geom_dest_file}, iterfield=(['dest_file']), requirements=[fsl_req.v('5.0.8')]) reorient_in_file = (copy_geom, 'out_file') else: reorient_in_file = geom_dest_file if self.branch('reorient_to_std'): reorient = pipeline.add( 'reorient_channel', fsl.Reorient2Std( output_type='NIFTI_GZ'), inputs={ 'in_file': reorient_in_file}, iterfield=['in_file'], requirements=[fsl_req.v('5.0.8')]) copy_to_dir_in_files = (reorient, 'out_file') else: copy_to_dir_in_files = reorient_in_file copy_to_dir = pipeline.add( 'copy_to_dir', CopyToDir(), inputs={ 'in_files': copy_to_dir_in_files, 'file_names': (list_channels, 'files')}) to_polar_in_dir = (copy_to_dir, 'out_dir') else: to_polar_in_dir = ('channels', multi_nifti_gz_format) pipeline.add( 'to_polar', ToPolarCoords( in_fname_re=self.parameter('channel_fname_regex'), real_label=self.parameter('channel_real_label'), imaginary_label=self.parameter('channel_imag_label')), inputs={ 'in_dir': to_polar_in_dir}, outputs={ 'mag_channels': ('magnitudes_dir', multi_nifti_gz_format), 'phase_channels': ('phases_dir', multi_nifti_gz_format)}) return pipeline
def compute_morpho_brain_mask(head_file, brain_volume, write_dir=None, unifize=True, caching=False, verbose=True, terminal_output='allatonce', **unifize_kwargs): """ Parameters ---------- brain_volume : int Volume of the brain in mm3 used for brain extraction. Typically 400 for mouse and 1800 for rat. unifize : bool, optional If True, brain mask is computed using RATS Mathematical Morphology. Otherwise, a histogram-based brain segmentation is used. caching : bool, optional Wether or not to use caching. unifize_kwargs : dict, optional Is passed to nipype.interfaces.afni.Unifize. Returns ------- path to brain extracted image. Notes ----- RATS tool is used for brain extraction and has to be cited. For more information, see `RATS <http://www.iibi.uiowa.edu/content/rats-overview/>`_ """ if write_dir is None: write_dir = os.path.dirname(head_file) if interfaces.Info().version() is None: raise ValueError('Can not locate Rats') environ = {'AFNI_DECONFLICT': 'OVERWRITE'} if caching: memory = Memory(write_dir) clip_level = memory.cache(afni.ClipLevel) compute_mask = memory.cache(interfaces.MathMorphoMask) compute_mask.interface().set_default_terminal_output(terminal_output) copy = memory.cache(afni.Copy) copy.interface().set_default_terminal_output(terminal_output) copy_geom = memory.cache(fsl.CopyGeom) else: clip_level = afni.ClipLevel().run compute_mask = interfaces.MathMorphoMask( terminal_output=terminal_output).run copy = afni.Copy(terminal_output=terminal_output).run copy_geom = fsl.CopyGeom(terminal_output=terminal_output).run if unifize: unifization_options = [ '{}_{}'.format(k, v) for (k, v) in unifize_kwargs.items() ] suffix = 'unifized_' + '_'.join(unifization_options) file_to_mask = afni_unifize(head_file, write_dir, out_file=fname_presuffix( head_file, suffix=suffix, newpath=write_dir), caching=caching, terminal_output=terminal_output, verbose=verbose, environ=environ, **unifize_kwargs) else: file_to_mask = head_file out_clip_level = clip_level(in_file=file_to_mask) out_compute_mask = compute_mask( in_file=file_to_mask, out_file=fname_presuffix(file_to_mask, suffix='_rats_brain_mask', newpath=write_dir), volume_threshold=brain_volume, intensity_threshold=int(out_clip_level.outputs.clip_val)) # RATS sometimes slightly modifies the affine same_geom = _check_same_geometry(out_compute_mask.outputs.out_file, head_file) if not same_geom: mask_file = fname_presuffix(out_compute_mask.outputs.out_file, suffix='_rough_geom', newpath=write_dir) out_copy = copy(in_file=out_compute_mask.outputs.out_file, out_file=mask_file, environ=environ) _ = copy_geom(dest_file=out_copy.outputs.out_file, in_file=head_file) else: mask_file = out_compute_mask.outputs.out_file # Remove intermediate output if not caching and unifize: os.remove(file_to_mask) if not same_geom: os.remove(out_compute_mask.outputs.out_file) return mask_file
def create_preprocess_mag_wf(): preprocmag = pe.Workflow(name="preprocmag") preprocmag.config['execution']['remove_unnecessary_outputs'] = False # define inputs inputspec = pe.Node(ul.IdentityInterface(fields=['input_mag', # raw phase data 'frac', # BET franction (-f parameter) 'rest', # volumes of rest in block design 'task', # volumes of task in block design ]), name='inputspec') # convert image to float img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float', op_string='', suffix='_dtype'), iterfield=['in_file'], name='img2float') # motion correct each run volreg = pe.MapNode(interface=afni.Volreg(), name='volreg', iterfield='in_file') volreg.inputs.outputtype = 'NIFTI_GZ' # calculate relative motions calcrel = pe.MapNode(ul.Function(['in_file'], ['out_file'], calcrelmotion), name='calcrel', iterfield=['in_file']) #generate motion plots plotmc = pe.MapNode(interface=fsl.PlotMotionParams(), name='plotmc', iterfield='in_file') plotmc.inputs.in_source = 'fsl' plotmc.iterables = ("plot_type", ['rotations', 'translations', 'displacement']) # register each run to first volume of first run # A) extract the first volume of the first run extract_ref = pe.MapNode(interface=fsl.ExtractROI(t_size=1, t_min=0), name='extract_ref', iterfield=['in_file']) # B) registration align2first = pe.MapNode(interface=afni.Allineate(), name='align2first', iterfield=['in_file']) align2first.inputs.num_threads = 2 align2first.inputs.out_matrix = 'align2first' # merge xfm from moco and first run alignment merge_xfm = pe.MapNode(interface=ul.Merge(2), name='merge_xfm', iterfield=['in1', 'in2']) # concatenate moco and alignment to run 1 cat_xfm = pe.MapNode(interface=afni.CatMatvec(oneline=True), name='cat_xfm', iterfield=['in_file']) cat_xfm.inputs.out_file = 'concated_xfm.aff12.1D' # apply first volume registration and motion correction in a single step applyalign = pe.MapNode(interface=afni.Allineate(), name='applyalign', iterfield=['in_file', 'in_matrix']) applyalign.inputs.num_threads = 2 applyalign.inputs.final_interpolation = 'nearestneighbour' applyalign.inputs.outputtype = 'NIFTI_GZ' # afni messes with the header (unobliques the data) this puts it back cpgeommoco = pe.MapNode(interface=fsl.CopyGeom(), name='cpgeommoco', iterfield=['dest_file', 'in_file']) # linear detrending prior to SNR calculation detrend = pe.MapNode(interface=pp.DetrendMag(), name='detrend', iterfield=['mag']) # get the mean functional of run 1 for brain extraction meanfunc = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), name='meanfunc', iterfield=['in_file']) # calculate the phase noise (takes in volume of activation, if none provided them assumes resting state) calcSNR = pe.MapNode(interface=pp.RestAverage(), name='calcSNR', iterfield=['func', 'rest', 'task']) # extract brain with fsl and save the mask extractor = pe.Node(interface=fsl.BET(), name="extractor") extractor.inputs.mask = True # apply the mask to all runs maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file'], name='maskfunc') # outputspec outputspec = pe.Node(ul.IdentityInterface(fields=['proc_mag','motion_par', 'motion_data', 'maxdisp_data' , 'motion_plot', 'run_txfm', 'mask_file','mean_file','snr']), name='outputspec') preprocmag = pe.Workflow(name='preprocmag') preprocmag.connect([(inputspec, img2float, [('input_mag', 'in_file')]), (img2float, volreg, [('out_file', 'in_file')]), (volreg, extract_ref, [('out_file', 'in_file')]), (extract_ref, align2first, [('roi_file', 'in_file')]), (extract_ref, align2first, [(('roi_file', pickfirst), 'reference')]), (extract_ref, applyalign, [(('roi_file', pickfirst), 'reference')]), (volreg, merge_xfm, [('oned_matrix_save', 'in2')]), (align2first, merge_xfm, [('out_matrix', 'in1')]), (merge_xfm, cat_xfm, [(('out', wraptuple), 'in_file')]), (volreg,applyalign, [('out_file', 'in_file')]), (volreg, calcrel, [('md1d_file', 'in_file')]), (volreg, plotmc, [('oned_file', 'in_file')]), (cat_xfm, applyalign, [('out_file', 'in_matrix')]), (img2float, cpgeommoco, [('out_file', 'in_file')]), (applyalign, cpgeommoco, [('out_file', 'dest_file')]), (cpgeommoco, detrend, [('out_file', 'mag')]), (detrend, meanfunc, [('detrended_mag', 'in_file')]), (inputspec, calcSNR, [('rest', 'rest'), ('task', 'task')]), (detrend, calcSNR, [('detrended_mag', 'func')]), (inputspec, extractor, [('frac', 'frac')]), (meanfunc, extractor, [(('out_file', pickfirst), 'in_file')]), (cpgeommoco, maskfunc, [('out_file', 'in_file')]), (extractor, maskfunc, [('mask_file', 'in_file2')]), (maskfunc, outputspec, [('out_file', 'proc_mag')]), (volreg, outputspec, [('oned_matrix_save', 'motion_par')]), (volreg, outputspec, [('oned_file', 'motion_data')]), (volreg, outputspec, [('md1d_file', 'maxdisp_data')]), (plotmc, outputspec, [('out_file', 'motion_plot')]), (cat_xfm, outputspec, [('out_file', 'run_txfm')]), (extractor, outputspec, [('mask_file', 'mask_file')]), (extractor, outputspec, [('out_file', 'mean_file')]), (calcSNR, outputspec, [('tsnr', 'snr')]), ]) return preprocmag