def init_gifti_surface_wf(name='gifti_surface_wf'): """ Extract surfaces from FreeSurfer derivatives folder and re-center GIFTI coordinates to align to native T1 space """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(['subjects_dir', 'subject_id']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(['surfaces']), name='outputnode') get_surfaces = pe.Node(nio.FreeSurferSource(), name='get_surfaces') midthickness = pe.MapNode( MakeMidthickness(thickness=True, distance=0.5, out_name='midthickness'), iterfield='in_file', name='midthickness') save_midthickness = pe.Node(nio.DataSink(parameterization=False), name='save_midthickness') surface_list = pe.Node(niu.Merge(4, ravel_inputs=True), name='surface_list', run_without_submitting=True) fs_2_gii = pe.MapNode(fs.MRIsConvert(out_datatype='gii'), iterfield='in_file', name='fs_2_gii') fix_surfs = pe.MapNode(NormalizeSurf(), iterfield='in_file', name='fix_surfs') workflow.connect([ (inputnode, get_surfaces, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id')]), (inputnode, save_midthickness, [('subjects_dir', 'base_directory'), ('subject_id', 'container')]), # Generate midthickness surfaces and save to FreeSurfer derivatives (get_surfaces, midthickness, [('smoothwm', 'in_file'), ('graymid', 'graymid')]), (midthickness, save_midthickness, [('out_file', 'surf.@graymid')]), # Produce valid GIFTI surface files (dense mesh) (get_surfaces, surface_list, [('smoothwm', 'in1'), ('pial', 'in2'), ('inflated', 'in3')]), (save_midthickness, surface_list, [('out_file', 'in4')]), (surface_list, fs_2_gii, [('out', 'in_file')]), (fs_2_gii, fix_surfs, [('converted', 'in_file')]), (fix_surfs, outputnode, [('out_file', 'surfaces')]), ]) return workflow
def init_gifti_surface_wf(name='gifti_surface_wf'): workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(['subjects_dir', 'subject_id']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(['surfaces']), name='outputnode') get_surfaces = pe.Node(nio.FreeSurferSource(), name='get_surfaces') midthickness = pe.MapNode(MakeMidthickness(thickness=True, distance=0.5, out_name='midthickness'), iterfield='in_file', name='midthickness') save_midthickness = pe.Node(nio.DataSink(parameterization=False), name='save_midthickness') surface_list = pe.Node(niu.Merge(4, ravel_inputs=True), name='surface_list', run_without_submitting=True) fs_2_gii = pe.MapNode(fs.MRIsConvert(out_datatype='gii'), iterfield='in_file', name='fs_2_gii') def normalize_surfs(in_file): """ Re-center GIFTI coordinates to fit align to native T1 space For midthickness surfaces, add MidThickness metadata Coordinate update based on: https://github.com/Washington-University/workbench/blob/1b79e56/src/Algorithms/AlgorithmSurfaceApplyAffine.cxx#L73-L91 and https://github.com/Washington-University/Pipelines/blob/ae69b9a/PostFreeSurfer/scripts/FreeSurfer2CaretConvertAndRegisterNonlinear.sh#L147 """ import os import numpy as np import nibabel as nib img = nib.load(in_file) pointset = img.get_arrays_from_intent('NIFTI_INTENT_POINTSET')[0] coords = pointset.data c_ras_keys = ('VolGeomC_R', 'VolGeomC_A', 'VolGeomC_S') ras = np.array([float(pointset.metadata[key]) for key in c_ras_keys]) # Apply C_RAS translation to coordinates pointset.data = (coords + ras).astype(coords.dtype) secondary = nib.gifti.GiftiNVPairs('AnatomicalStructureSecondary', 'MidThickness') geom_type = nib.gifti.GiftiNVPairs('GeometricType', 'Anatomical') has_ass = has_geo = False for nvpair in pointset.meta.data: # Remove C_RAS translation from metadata to avoid double-dipping in FreeSurfer if nvpair.name in c_ras_keys: nvpair.value = '0.000000' # Check for missing metadata elif nvpair.name == secondary.name: has_ass = True elif nvpair.name == geom_type.name: has_geo = True fname = os.path.basename(in_file) # Update metadata for MidThickness/graymid surfaces if 'midthickness' in fname.lower() or 'graymid' in fname.lower(): if not has_ass: pointset.meta.data.insert(1, secondary) if not has_geo: pointset.meta.data.insert(2, geom_type) img.to_filename(fname) return os.path.abspath(fname) fix_surfs = pe.MapNode(niu.Function(function=normalize_surfs), iterfield='in_file', name='fix_surfs') workflow.connect([ (inputnode, get_surfaces, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id')]), (inputnode, save_midthickness, [('subjects_dir', 'base_directory'), ('subject_id', 'container')]), # Generate midthickness surfaces and save to FreeSurfer derivatives (get_surfaces, midthickness, [('smoothwm', 'in_file'), ('graymid', 'graymid')]), (midthickness, save_midthickness, [('out_file', 'surf.@graymid')]), # Produce valid GIFTI surface files (dense mesh) (get_surfaces, surface_list, [('smoothwm', 'in1'), ('pial', 'in2'), ('inflated', 'in3')]), (save_midthickness, surface_list, [('out_file', 'in4')]), (surface_list, fs_2_gii, [('out', 'in_file')]), (fs_2_gii, fix_surfs, [('converted', 'in_file')]), (fix_surfs, outputnode, [('out', 'surfaces')]), ]) return workflow
def init_gifti_surface_wf(name='gifti_surface_wf'): r""" This workflow prepares GIFTI surfaces from a FreeSurfer subjects directory If midthickness (or graymid) surfaces do not exist, they are generated and saved to the subject directory as ``lh/rh.midthickness``. These, along with the gray/white matter boundary (``lh/rh.smoothwm``), pial sufaces (``lh/rh.pial``) and inflated surfaces (``lh/rh.inflated``) are converted to GIFTI files. Additionally, the vertex coordinates are :py:class:`recentered <fmriprep.interfaces.NormalizeSurf>` to align with native T1w space. .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.anatomical import init_gifti_surface_wf wf = init_gifti_surface_wf() **Inputs** subjects_dir FreeSurfer SUBJECTS_DIR subject_id FreeSurfer subject ID **Outputs** surfaces GIFTI surfaces for gray/white matter boundary, pial surface, midthickness (or graymid) surface, and inflated surfaces """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(['subjects_dir', 'subject_id']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(['surfaces']), name='outputnode') get_surfaces = pe.Node(nio.FreeSurferSource(), name='get_surfaces') midthickness = pe.MapNode(MakeMidthickness(thickness=True, distance=0.5, out_name='midthickness'), iterfield='in_file', name='midthickness') save_midthickness = pe.Node(nio.DataSink(parameterization=False), name='save_midthickness') surface_list = pe.Node(niu.Merge(4, ravel_inputs=True), name='surface_list', run_without_submitting=True) fs_2_gii = pe.MapNode(fs.MRIsConvert(out_datatype='gii'), iterfield='in_file', name='fs_2_gii') fix_surfs = pe.MapNode(NormalizeSurf(), iterfield='in_file', name='fix_surfs') workflow.connect([ (inputnode, get_surfaces, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id')]), (inputnode, save_midthickness, [('subjects_dir', 'base_directory'), ('subject_id', 'container')]), # Generate midthickness surfaces and save to FreeSurfer derivatives (get_surfaces, midthickness, [('smoothwm', 'in_file'), ('graymid', 'graymid')]), (midthickness, save_midthickness, [('out_file', 'surf.@graymid')]), # Produce valid GIFTI surface files (dense mesh) (get_surfaces, surface_list, [('smoothwm', 'in1'), ('pial', 'in2'), ('inflated', 'in3')]), (save_midthickness, surface_list, [('out_file', 'in4')]), (surface_list, fs_2_gii, [('out', 'in_file')]), (fs_2_gii, fix_surfs, [('converted', 'in_file')]), (fix_surfs, outputnode, [('out_file', 'surfaces')]), ]) return workflow
def init_refine_brainmask_wf(name='refine_brainmask'): """ This workflow refines the brainmask implicit in the FreeSurfer's ``aseg.mgz`` brain tissue segmentation to reconcile ANTs' and FreeSurfer's brain masks. First, the ``aseg.mgz`` mask from FreeSurfer is refined in two steps, using binary morphological operations: 1. With a binary closing operation the sulci are included into the mask. This results in a smoother brain mask that does not exclude deep, wide sulci. 2. Fill any holes (typically, there could be a hole next to the pineal gland and the corpora quadrigemina if the great cerebral brain is segmented out). Second, the brain mask is grown, including pixels that have a high likelihood to the GM tissue distribution: 3. Dilate and substract the brain mask, defining the region to search for candidate pixels that likely belong to cortical GM. 4. Pixels found in the search region that are labeled as GM by ANTs (during ``antsBrainExtraction.sh``) are directly added to the new mask. 5. Otherwise, estimate GM tissue parameters locally in patches of ``ww`` size, and test the likelihood of the pixel to belong in the GM distribution. This procedure is inspired on mindboggle's solution to the problem: https://github.com/nipy/mindboggle/blob/master/mindboggle/guts/segment.py#L1660 .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.anatomical import init_refine_brainmask_wf wf = init_refine_brainmask_wf() **Inputs** in_file Anatomical, merged T1w image after INU correction ants_segs Brain tissue segmentation from ANTS ``antsBrainExtraction.sh`` subjects_dir FreeSurfer SUBJECTS_DIR subject_id FreeSurfer subject ID **Outputs** out_file New, refined brain mask """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface([ 'in_file', 'ants_segs', 'subjects_dir', 'subject_id']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(['out_file']), name='outputnode') get_aseg = pe.Node(nio.FreeSurferSource(), name='get_aseg') tonative = pe.Node(fs.Label2Vol(), name='tonative') tonii = pe.Node(fs.MRIConvert(out_type='niigz', resample_type='nearest'), name='tonii') refine = pe.Node(RefineBrainMask(), name='refine') workflow.connect([ (inputnode, refine, [('in_file', 'in_anat'), ('ants_segs', 'in_ants')]), (inputnode, get_aseg, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id')]), (inputnode, tonii, [('in_file', 'reslice_like')]), (get_aseg, tonative, [('aseg', 'seg_file'), ('rawavg', 'template_file'), ('aseg', 'reg_header')]), (tonative, tonii, [('vol_label_file', 'in_file')]), (tonii, refine, [('out_file', 'in_aseg')]), (refine, outputnode, [('out_file', 'out_file')]), ]) return workflow