def getInputs(): #pylint: disable=unused-variable from mrtrix3 import app, path, run run.command('mrconvert ' + path.fromUser(app.args.in_5tt, True) + ' ' + path.toTemp('5tt.mif', True)) if app.args.dirs: run.command('mrconvert ' + path.fromUser(app.args.dirs, True) + ' ' + path.toTemp('dirs.mif', True) + ' -strides 0,0,0,1')
def getInputs(): import os from mrtrix3 import app, path, run run.command('mrconvert ' + path.fromUser(app.args.in_5tt, True) + ' ' + path.toTemp('5tt.mif', True)) if app.args.dirs: run.command('mrconvert ' + path.fromUser(app.args.dirs, True) + ' ' + path.toTemp('dirs.mif', True) + ' -stride 0,0,0,1')
def getInputs(): import os, shutil from mrtrix3 import app, path, run run.command('mrconvert ' + path.fromUser(app.args.input, True) + ' ' + path.toTemp('input.mif', True)) if app.args.lut: run.function(shutil.copyfile, path.fromUser(app.args.lut, False), path.toTemp('LUT.txt', False))
def getInputs(): import os from mrtrix3 import app, image, path, run if app.args.mask: run.command('mrconvert ' + path.fromUser(app.args.mask, True) + ' ' + path.toTemp('mask.mif', True) + ' -datatype bit -stride -1,+2,+3') if app.args.t2: if not image.match(app.args.input, app.args.t2): app.error('Provided T2 image does not match input T1 image') run.command('mrconvert ' + path.fromUser(app.args.t2, True) + ' ' + path.toTemp('T2.nii', True) + ' -stride -1,+2,+3')
def getInputs(): #pylint: disable=unused-variable from mrtrix3 import app, image, path, run image.check3DNonunity(path.fromUser(app.args.input, False)) run.command('mrconvert ' + path.fromUser(app.args.input, True) + ' ' + path.toTemp('input.mif', True)) if app.args.mask: run.command('mrconvert ' + path.fromUser(app.args.mask, True) + ' ' + path.toTemp('mask.mif', True) + ' -datatype bit -strides -1,+2,+3') if app.args.t2: if not image.match(app.args.input, app.args.t2): app.error('Provided T2 image does not match input T1 image') run.command('mrconvert ' + path.fromUser(app.args.t2, True) + ' ' + path.toTemp('T2.nii', True) + ' -strides -1,+2,+3')
def getInputs(): #pylint: disable=unused-variable import os from mrtrix3 import app, path, run mask_path = path.toTemp('mask.mif', False) if os.path.exists(mask_path): app.warn('-mask option is ignored by algorithm \'manual\'') os.remove(mask_path) run.command('mrconvert ' + path.fromUser(app.args.in_voxels, True) + ' ' + path.toTemp('in_voxels.mif', True)) if app.args.dirs: run.command('mrconvert ' + path.fromUser(app.args.dirs, True) + ' ' + path.toTemp('dirs.mif', True) + ' -strides 0,0,0,1')
def getInputs(): import os from mrtrix3 import app, path, run mask_path = path.toTemp('mask.mif', False) if os.path.exists(mask_path): app.warn('-mask option is ignored by algorithm \'manual\'') os.remove(mask_path) run.command('mrconvert ' + path.fromUser(app.args.in_voxels, True) + ' ' + path.toTemp('in_voxels.mif', True)) if app.args.dirs: run.command('mrconvert ' + path.fromUser(app.args.dirs, True) + ' ' + path.toTemp('dirs.mif', True) + ' -stride 0,0,0,1')
def getInputs(): #pylint: disable=unused-variable from mrtrix3 import app, image, path, run image.check3DNonunity(path.fromUser(app.args.input, False)) run.command('mrconvert ' + path.fromUser(app.args.input, True) + ' ' + path.toTemp('input.mif', True)) if app.args.mask: run.command('mrconvert ' + path.fromUser(app.args.mask, True) + ' ' + path.toTemp('mask.mif', True) + ' -datatype bit -strides -1,+2,+3') if app.args.t2: if not image.match(app.args.input, app.args.t2): app.error('Provided T2 image does not match input T1 image') run.command('mrconvert ' + path.fromUser(app.args.t2, True) + ' ' + path.toTemp('T2.nii', True) + ' -strides -1,+2,+3')
def checkFirst(prefix, structures): #pylint: disable=unused-variable import os from mrtrix3 import app, file, path # pylint: disable=redefined-builtin vtk_files = [prefix + '-' + struct + '_first.vtk' for struct in structures] existing_file_count = sum( [os.path.exists(filename) for filename in vtk_files]) if existing_file_count != len(vtk_files): if 'SGE_ROOT' in os.environ: app.console( 'FSL FIRST job has been submitted to SGE; awaiting completion') app.console( '(note however that FIRST may fail silently, and hence this script may hang indefinitely)' ) file.waitFor(vtk_files) else: app.error('FSL FIRST has failed; only ' + str(existing_file_count) + ' of ' + str(len(vtk_files)) + ' structures were segmented successfully (check ' + path.toTemp('first.logs', False) + ')')
def execute(): import os from distutils.spawn import find_executable from mrtrix3 import app, file, fsl, image, run if app.isWindows(): app.error( '\'fsl\' algorithm of 5ttgen script cannot be run on Windows: FSL not available on Windows' ) fsl_path = os.environ.get('FSLDIR', '') if not fsl_path: app.error( 'Environment variable FSLDIR is not set; please run appropriate FSL configuration script' ) ssroi_cmd = 'standard_space_roi' if not find_executable(ssroi_cmd): ssroi_cmd = 'fsl5.0-standard_space_roi' if not find_executable(ssroi_cmd): app.error( 'Could not find FSL program standard_space_roi; please verify FSL install' ) bet_cmd = 'bet' if not find_executable(bet_cmd): bet_cmd = 'fsl5.0-bet' if not find_executable(bet_cmd): app.error( 'Could not find FSL program bet; please verify FSL install') fast_cmd = 'fast' if not find_executable(fast_cmd): fast_cmd = 'fsl5.0-fast' if not find_executable(fast_cmd): app.error( 'Could not find FSL program fast; please verify FSL install') first_cmd = 'run_first_all' if not find_executable(first_cmd): first_cmd = "fsl5.0-run_first_all" if not find_executable(first_cmd): app.error( 'Could not find FSL program run_first_all; please verify FSL install' ) first_atlas_path = os.path.join(fsl_path, 'data', 'first', 'models_336_bin') if not os.path.isdir(first_atlas_path): app.error( 'Atlases required for FSL\'s FIRST program not installed; please install fsl-first-data using your relevant package manager' ) fsl_suffix = fsl.suffix() sgm_structures = [ 'L_Accu', 'R_Accu', 'L_Caud', 'R_Caud', 'L_Pall', 'R_Pall', 'L_Puta', 'R_Puta', 'L_Thal', 'R_Thal' ] if app.args.sgm_amyg_hipp: sgm_structures.extend(['L_Amyg', 'R_Amyg', 'L_Hipp', 'R_Hipp']) run.command('mrconvert input.mif T1.nii -stride -1,+2,+3') fast_t1_input = 'T1.nii' fast_t2_input = '' # Decide whether or not we're going to do any brain masking if os.path.exists('mask.mif'): fast_t1_input = 'T1_masked' + fsl_suffix # Check to see if the mask matches the T1 image if image.match('T1.nii', 'mask.mif'): run.command('mrcalc T1.nii mask.mif -mult ' + fast_t1_input) mask_path = 'mask.mif' else: app.warn('Mask image does not match input image - re-gridding') run.command( 'mrtransform mask.mif mask_regrid.mif -template T1.nii') run.command('mrcalc T1.nii mask_regrid.mif ' + fast_t1_input) mask_path = 'mask_regrid.mif' if os.path.exists('T2.nii'): fast_t2_input = 'T2_masked' + fsl_suffix run.command('mrcalc T2.nii ' + mask_path + ' -mult ' + fast_t2_input) elif app.args.premasked: fast_t1_input = 'T1.nii' if os.path.exists('T2.nii'): fast_t2_input = 'T2.nii' else: # Use FSL command standard_space_roi to do an initial masking of the image before BET # Also reduce the FoV of the image # Using MNI 1mm dilated brain mask rather than the -b option in standard_space_roi (which uses the 2mm mask); the latter looks 'buggy' to me... Unfortunately even with the 1mm 'dilated' mask, it can still cut into some brain areas, hence the explicit dilation mni_mask_path = os.path.join(fsl_path, 'data', 'standard', 'MNI152_T1_1mm_brain_mask_dil.nii.gz') mni_mask_dilation = 0 if os.path.exists(mni_mask_path): mni_mask_dilation = 4 else: mni_mask_path = os.path.join( fsl_path, 'data', 'standard', 'MNI152_T1_2mm_brain_mask_dil.nii.gz') if os.path.exists(mni_mask_path): mni_mask_dilation = 2 if mni_mask_dilation: run.command('maskfilter ' + mni_mask_path + ' dilate mni_mask.nii -npass ' + str(mni_mask_dilation)) if app.args.nocrop: ssroi_roi_option = ' -roiNONE' else: ssroi_roi_option = ' -roiFOV' run.command( ssroi_cmd + ' T1.nii T1_preBET' + fsl_suffix + ' -maskMASK mni_mask.nii' + ssroi_roi_option, False) else: run.command(ssroi_cmd + ' T1.nii T1_preBET' + fsl_suffix + ' -b', False) # For whatever reason, the output file from standard_space_roi may not be # completed before BET is run file.waitFor('T1_preBET' + fsl_suffix) # BET fast_t1_input = 'T1_BET' + fsl_suffix run.command(bet_cmd + ' T1_preBET' + fsl_suffix + ' ' + fast_t1_input + ' -f 0.15 -R') if os.path.exists('T2.nii'): if app.args.nocrop: fast_t2_input = 'T2.nii' else: # Just a reduction of FoV, no sub-voxel interpolation going on run.command('mrtransform T2.nii T2_cropped.nii -template ' + fast_t1_input + ' -interp nearest') fast_t2_input = 'T2_cropped.nii' # Finish branching based on brain masking # FAST if fast_t2_input: run.command(fast_cmd + ' -S 2 ' + fast_t2_input + ' ' + fast_t1_input) else: run.command(fast_cmd + ' ' + fast_t1_input) fast_output_prefix = fast_t1_input.split('.')[0] # FIRST first_input_is_brain_extracted = '' if app.args.premasked: first_input_is_brain_extracted = ' -b' run.command(first_cmd + ' -s ' + ','.join(sgm_structures) + ' -i T1.nii -o first' + first_input_is_brain_extracted) # Test to see whether or not FIRST has succeeded # However if the expected image is absent, it may be due to FIRST being run # on SGE; in this case it is necessary to wait and see if the file appears. # But even in this case, FIRST may still fail, and the file will never appear... combined_image_path = 'first_all_none_firstseg' + fsl_suffix if not os.path.isfile(combined_image_path): if 'SGE_ROOT' in os.environ: app.console( 'FSL FIRST job has been submitted to SGE; awaiting completion') app.console( '(note however that FIRST may fail, and hence this script may hang indefinitely)' ) file.waitFor(combined_image_path) else: app.error( 'FSL FIRST has failed; not all structures were segmented successfully (check ' + path.toTemp('first.logs', False) + ')') # Convert FIRST meshes to partial volume images pve_image_list = [] for struct in sgm_structures: pve_image_path = 'mesh2pve_' + struct + '.mif' vtk_in_path = 'first-' + struct + '_first.vtk' vtk_temp_path = struct + '.vtk' run.command('meshconvert ' + vtk_in_path + ' ' + vtk_temp_path + ' -transform first2real T1.nii') run.command('mesh2pve ' + vtk_temp_path + ' ' + fast_t1_input + ' ' + pve_image_path) pve_image_list.append(pve_image_path) pve_cat = ' '.join(pve_image_list) run.command('mrmath ' + pve_cat + ' sum - | mrcalc - 1.0 -min all_sgms.mif') # Looks like FAST in 5.0 ignores FSLOUTPUTTYPE when writing the PVE images # Will have to wait and see whether this changes, and update the script accordingly if fast_cmd == 'fast': fast_suffix = fsl_suffix else: fast_suffix = '.nii.gz' # Combine the tissue images into the 5TT format within the script itself # Step 1: Run LCC on the WM image run.command( 'mrthreshold ' + fast_output_prefix + '_pve_2' + fast_suffix + ' - -abs 0.001 | maskfilter - connect - -connectivity | mrcalc 1 - 1 -gt -sub remove_unconnected_wm_mask.mif -datatype bit' ) # Step 2: Generate the images in the same fashion as the 5ttgen command run.command('mrcalc ' + fast_output_prefix + '_pve_0' + fast_suffix + ' remove_unconnected_wm_mask.mif -mult csf.mif') run.command('mrcalc 1.0 csf.mif -sub all_sgms.mif -min sgm.mif') run.command('mrcalc 1.0 csf.mif sgm.mif -add -sub ' + fast_output_prefix + '_pve_1' + fast_suffix + ' ' + fast_output_prefix + '_pve_2' + fast_suffix + ' -add -div multiplier.mif') run.command( 'mrcalc multiplier.mif -finite multiplier.mif 0.0 -if multiplier_noNAN.mif' ) run.command( 'mrcalc ' + fast_output_prefix + '_pve_1' + fast_suffix + ' multiplier_noNAN.mif -mult remove_unconnected_wm_mask.mif -mult cgm.mif' ) run.command( 'mrcalc ' + fast_output_prefix + '_pve_2' + fast_suffix + ' multiplier_noNAN.mif -mult remove_unconnected_wm_mask.mif -mult wm.mif' ) run.command('mrcalc 0 wm.mif -min path.mif') run.command( 'mrcat cgm.mif sgm.mif wm.mif csf.mif path.mif - -axis 3 | mrconvert - combined_precrop.mif -stride +2,+3,+4,+1' ) # Use mrcrop to reduce file size (improves caching of image data during tracking) if app.args.nocrop: run.command('mrconvert combined_precrop.mif result.mif') else: run.command( 'mrmath combined_precrop.mif sum - -axis 3 | mrthreshold - - -abs 0.5 | mrcrop combined_precrop.mif result.mif -mask -' )
def getInputs(): #pylint: disable=unused-variable import shutil from mrtrix3 import app, path, run run.command('mrconvert ' + path.fromUser(app.args.input, True) + ' ' + path.toTemp('input.mif', True)) if app.args.lut: run.function(shutil.copyfile, path.fromUser(app.args.lut, False), path.toTemp('LUT.txt', False))
dwi_ind_size = [[0,0,0,0]] if not app.args.fslbval: bvallist = [i + '.bval' for i in DWInlist] else: UserBvalpath = app.args.fslbval.rsplit(',') bvallist = [os.path.realpath(i) for i in UserBvalpath] if not app.args.fslbvec: bveclist = [i + '.bvec' for i in DWInlist] else: UserBvecpath = app.args.fslbvec.rsplit(',') bveclist = [os.path.realpath(i) for i in UserBvecpath] if len(DWInlist) == 1: if not isdicom: run.command('mrconvert -stride -1,2,3,4 -fslgrad ' + bveclist[0] + ' ' + bvallist[0] + ' ' + ''.join(DWInlist) + ''.join(DWIext) + ' ' + path.toTemp('dwi.mif',True)) else: run.command('mrconvert -stride -1,2,3,4 ' + ''.join(DWInlist) + ' ' + path.toTemp('dwi.mif',True)) else: for idx,i in enumerate(DWInlist): if not isdicom: run.command('mrconvert -stride -1,2,3,4 -fslgrad ' + bveclist[idx] + ' ' + bvallist[idx] + ' ' + i + DWIext[idx] + ' ' + path.toTemp('dwi' + str(idx) + '.mif',True)) else: run.command('mrconvert -stride -1,2,3,4 ' + i + ' ' + path.toTemp('dwi' + str(idx) + '.mif',True)) dwi_header = image.Header(path.toTemp('dwi' + str(idx) + '.mif',True)) dwi_ind_size.append([ int(s) for s in dwi_header.size() ]) miflist.append(path.toTemp('dwi' + str(idx) + '.mif',True)) DWImif = ' '.join(miflist) run.command('mrcat -axis 3 ' + DWImif + ' ' + path.toTemp('dwi.mif',True)) app.gotoTempDir()
def checkFirst(prefix, structures): #pylint: disable=unused-variable import os from mrtrix3 import app, file, path # pylint: disable=redefined-builtin vtk_files = [ prefix + '-' + struct + '_first.vtk' for struct in structures ] existing_file_count = sum([ os.path.exists(filename) for filename in vtk_files ]) if existing_file_count != len(vtk_files): if 'SGE_ROOT' in os.environ: app.console('FSL FIRST job has been submitted to SGE; awaiting completion') app.console('(note however that FIRST may fail silently, and hence this script may hang indefinitely)') file.waitFor(vtk_files) else: app.error('FSL FIRST has failed; only ' + str(existing_file_count) + ' of ' + str(len(vtk_files)) + ' structures were segmented successfully (check ' + path.toTemp('first.logs', False) + ')')
def getInputs(): import os, shutil from mrtrix3 import app, path, run if app.args.lut: run.function(shutil.copyfile, path.fromUser(app.args.lut, False), path.toTemp('LUT.txt', False))
def getInputs(): #pylint: disable=unused-variable from mrtrix3 import app, path, run checkGIFinput(path.fromUser(app.args.input, True)) run.command('mrconvert ' + path.fromUser(app.args.input, True) + ' ' + path.toTemp('input.mif', True))
if not app.args.fslbval: bvallist = [i + '.bval' for i in DWInlist] else: UserBvalpath = app.args.fslbval.rsplit(',') bvallist = [os.path.realpath(i) for i in UserBvalpath] if not app.args.fslbvec: bveclist = [i + '.bvec' for i in DWInlist] else: UserBvecpath = app.args.fslbvec.rsplit(',') bveclist = [os.path.realpath(i) for i in UserBvecpath] if len(DWInlist) == 1: if not isdicom: run.command('mrconvert -stride -1,2,3,4 -fslgrad ' + bveclist[0] + ' ' + bvallist[0] + ' ' + ''.join(DWInlist) + ''.join(DWIext) + ' ' + path.toTemp('dwi.mif', True)) else: run.command('mrconvert -stride -1,2,3,4 ' + ''.join(DWInlist) + ' ' + path.toTemp('dwi.mif', True)) else: for idx, i in enumerate(DWInlist): if not isdicom: run.command('mrconvert -stride -1,2,3,4 -fslgrad ' + bveclist[idx] + ' ' + bvallist[idx] + ' ' + i + DWIext[idx] + ' ' + path.toTemp('dwi' + str(idx) + '.mif', True)) else: run.command('mrconvert -stride -1,2,3,4 ' + i + ' ' + path.toTemp('dwi' + str(idx) + '.mif', True)) dwi_header = image.Header(path.toTemp('dwi' + str(idx) + '.mif', True)) dwi_ind_size.append([int(s) for s in dwi_header.size()])
def runSubject(bids_dir, label, output_prefix): output_dir = os.path.join(output_prefix, label) if os.path.exists(output_dir): shutil.rmtree(output_dir) os.makedirs(output_dir) os.makedirs(os.path.join(output_dir, 'connectome')) os.makedirs(os.path.join(output_dir, 'dwi')) fsl_path = os.environ.get('FSLDIR', '') if not fsl_path: app.error( 'Environment variable FSLDIR is not set; please run appropriate FSL configuration script' ) flirt_cmd = fsl.exeName('flirt') fslanat_cmd = fsl.exeName('fsl_anat') fsl_suffix = fsl.suffix() unring_cmd = 'unring.a64' if not find_executable(unring_cmd): app.console('Command \'' + unring_cmd + '\' not found; cannot perform Gibbs ringing removal') unring_cmd = '' dwibiascorrect_algo = '-ants' if not find_executable('N4BiasFieldCorrection'): # Can't use findFSLBinary() here, since we want to proceed even if it's not found if find_executable('fast') or find_executable('fsl5.0-fast'): dwibiascorrect_algo = '-fsl' app.console('Could not find ANTs program N4BiasFieldCorrection; ' 'using FSL FAST for bias field correction') else: dwibiascorrect_algo = '' app.warn( 'Could not find ANTs program \'N4BiasFieldCorrection\' or FSL program \'fast\'; ' 'will proceed without performing DWI bias field correction') if not app.args.parcellation: app.error( 'For participant-level analysis, desired parcellation must be provided using the -parcellation option' ) parc_image_path = '' parc_lut_file = '' mrtrix_lut_file = os.path.join( os.path.dirname(os.path.abspath(app.__file__)), os.pardir, os.pardir, 'share', 'mrtrix3', 'labelconvert') if app.args.parcellation == 'fs_2005' or app.args.parcellation == 'fs_2009': if not 'FREESURFER_HOME' in os.environ: app.error( 'Environment variable FREESURFER_HOME not set; please verify FreeSurfer installation' ) if not find_executable('recon-all'): app.error( 'Could not find FreeSurfer script recon-all; please verify FreeSurfer installation' ) parc_lut_file = os.path.join(os.environ['FREESURFER_HOME'], 'FreeSurferColorLUT.txt') if app.args.parcellation == 'fs_2005': mrtrix_lut_file = os.path.join(mrtrix_lut_file, 'fs_default.txt') else: mrtrix_lut_file = os.path.join(mrtrix_lut_file, 'fs_a2009s.txt') if app.args.parcellation == 'aal' or app.args.parcellation == 'aal2': mni152_path = os.path.join(fsl_path, 'data', 'standard', 'MNI152_T1_1mm.nii.gz') if not os.path.isfile(mni152_path): app.error( 'Could not find MNI152 template image within FSL installation (expected location: ' + mni152_path + ')') if app.args.parcellation == 'aal': parc_image_path = os.path.abspath( os.path.join(os.sep, 'opt', 'aal', 'ROI_MNI_V4.nii')) parc_lut_file = os.path.abspath( os.path.join(os.sep, 'opt', 'aal', 'ROI_MNI_V4.txt')) mrtrix_lut_file = os.path.join(mrtrix_lut_file, 'aal.txt') else: parc_image_path = os.path.abspath( os.path.join(os.sep, 'opt', 'aal', 'ROI_MNI_V5.nii')) parc_lut_file = os.path.abspath( os.path.join(os.sep, 'opt', 'aal', 'ROI_MNI_V5.txt')) mrtrix_lut_file = os.path.join(mrtrix_lut_file, 'aal2.txt') if parc_image_path and not os.path.isfile(parc_image_path): if app.args.atlas_path: parc_image_path = [ parc_image_path, os.path.join(os.path.dirname(app.args.atlas_path), os.path.basename(parc_image_path)) ] if os.path.isfile(parc_image_path[1]): parc_image_path = parc_image_path[1] else: app.error( 'Could not find parcellation image (tested locations: ' + str(parc_image_path) + ')') else: app.error( 'Could not find parcellation image (expected location: ' + parc_image_path + ')') if not os.path.isfile(parc_lut_file): if app.args.atlas_path: parc_lut_file = [ parc_lut_file, os.path.join(os.path.dirname(app.args.atlas_path), os.path.basename(parc_lut_file)) ] if os.path.isfile(parc_lut_file[1]): parc_lut_file = parc_lut_file[1] else: app.error( 'Could not find parcellation lookup table file (tested locations: ' + str(parc_lut_file) + ')') else: app.error( 'Could not find parcellation lookup table file (expected location: ' + parc_lut_file + ')') if not os.path.exists(mrtrix_lut_file): app.error( 'Could not find MRtrix3 connectome lookup table file (expected location: ' + mrtrix_lut_file + ')') app.makeTempDir() # Need to perform an initial import of JSON data using mrconvert; so let's grab the diffusion gradient table as well # If no bvec/bval present, need to go down the directory listing # Only try to import JSON file if it's actually present # direction in the acquisition they'll need to be split across multiple files # May need to concatenate more than one input DWI, since if there's more than one phase-encode direction # in the acquired DWIs (i.e. not just those used for estimating the inhomogeneity field), they will # need to be stored as separate NIfTI files in the 'dwi/' directory. dwi_image_list = glob.glob( os.path.join(bids_dir, label, 'dwi', label) + '*_dwi.nii*') dwi_index = 1 for entry in dwi_image_list: # os.path.split() falls over with .nii.gz extensions; only removes the .gz prefix = entry.split(os.extsep)[0] if os.path.isfile(prefix + '.bval') and os.path.isfile(prefix + '.bvec'): prefix = prefix + '.' else: prefix = os.path.join(bids_dir, 'dwi') if not (os.path.isfile(prefix + 'bval') and os.path.isfile(prefix + 'bvec')): app.error( 'Unable to locate valid diffusion gradient table for image \'' + entry + '\'') grad_import_option = ' -fslgrad ' + prefix + 'bvec ' + prefix + 'bval' json_path = prefix + 'json' if os.path.isfile(json_path): json_import_option = ' -json_import ' + json_path else: json_import_option = '' run.command('mrconvert ' + entry + grad_import_option + json_import_option + ' ' + path.toTemp('dwi' + str(dwi_index) + '.mif', True)) dwi_index += 1 # Go hunting for reversed phase-encode data dedicated to field map estimation fmap_image_list = [] fmap_dir = os.path.join(bids_dir, label, 'fmap') fmap_index = 1 if os.path.isdir(fmap_dir): if app.args.preprocessed: app.error('fmap/ directory detected for subject \'' + label + '\' despite use of ' + option_prefix + 'preprocessed option') fmap_image_list = glob.glob( os.path.join(fmap_dir, label) + '_dir-*_epi.nii*') for entry in fmap_image_list: prefix = entry.split(os.extsep)[0] json_path = prefix + '.json' with open(json_path, 'r') as f: json_elements = json.load(f) if 'IntendedFor' in json_elements and not any( i.endswith(json_elements['IntendedFor']) for i in dwi_image_list): app.console('Image \'' + entry + '\' is not intended for use with DWIs; skipping') continue if os.path.isfile(json_path): json_import_option = ' -json_import ' + json_path # fmap files will not come with any gradient encoding in the JSON; # therefore we need to add it manually ourselves so that mrcat / mrconvert can # appropriately handle the table once these images are concatenated with the DWIs fmap_image_size = image.Header(entry).size() fmap_image_num_volumes = 1 if len( fmap_image_size) == 3 else fmap_image_size[3] run.command('mrconvert ' + entry + json_import_option + ' -set_property dw_scheme \"' + '\\n'.join(['0,0,1,0'] * fmap_image_num_volumes) + '\" ' + path.toTemp('fmap' + str(fmap_index) + '.mif', True)) fmap_index += 1 else: app.warn('No corresponding .json file found for image \'' + entry + '\'; skipping') fmap_image_list = [ 'fmap' + str(index) + '.mif' for index in range(1, fmap_index) ] # If there's no data in fmap/ directory, need to check to see if there's any phase-encoding # contrast within the input DWI(s) elif len(dwi_image_list) < 2 and not app.args.preprocessed: app.error( 'Inadequate data for pre-processing of subject \'' + label + '\': No phase-encoding contrast in input DWIs or fmap/ directory') dwi_image_list = [ 'dwi' + str(index) + '.mif' for index in range(1, dwi_index) ] # Import anatomical image run.command('mrconvert ' + os.path.join(bids_dir, label, 'anat', label + '_T1w.nii.gz') + ' ' + path.toTemp('T1.mif', True)) cwd = os.getcwd() app.gotoTempDir() dwipreproc_se_epi = '' dwipreproc_se_epi_option = '' # For automated testing, down-sampled images are used. However, this invalidates the requirements of # both MP-PCA denoising and Gibbs ringing removal. In addition, eddy can still take a long time # despite the down-sampling. Therefore, provide images that have been pre-processed to the stage # where it is still only DWI, JSON & bvecs/bvals that need to be provided. if app.args.preprocessed: if len(dwi_image_list) > 1: app.error( 'If DWIs have been pre-processed, then only a single DWI file should need to be provided' ) app.console( 'Skipping MP-PCA denoising, ' + ('Gibbs ringing removal, ' if unring_cmd else '') + 'distortion correction and bias field correction due to use of ' + option_prefix + 'preprocessed option') run.function(os.rename, dwi_image_list[0], 'dwi.mif') else: # Do initial image pre-processing (denoising, Gibbs ringing removal if available, distortion correction & bias field correction) as normal # Concatenate any SE EPI images with the DWIs before denoising (& unringing), then # separate them again after the fact dwidenoise_input = 'dwidenoise_input.mif' fmap_num_volumes = 0 if fmap_image_list: run.command('mrcat ' + ' '.join(fmap_image_list) + ' fmap_cat.mif -axis 3') for i in fmap_image_list: file.delTemporary(i) fmap_num_volumes = image.Header('fmap_cat.mif').size()[3] dwidenoise_input = 'all_cat.mif' run.command('mrcat fmap_cat.mif ' + ' '.join(dwi_image_list) + ' ' + dwidenoise_input + ' -axis 3') file.delTemporary('fmap_cat.mif') else: # Even if no explicit fmap images, may still need to concatenate multiple DWI inputs if len(dwi_image_list) > 1: run.command('mrcat ' + ' '.join(dwi_image_list) + ' ' + dwidenoise_input + ' -axis 3') else: run.function(shutil.move, dwi_image_list[0], dwidenoise_input) for i in dwi_image_list: file.delTemporary(i) # Step 1: Denoise run.command('dwidenoise ' + dwidenoise_input + ' dwi_denoised.' + ('nii' if unring_cmd else 'mif')) if unring_cmd: run.command('mrinfo ' + dwidenoise_input + ' -json_keyval input.json') file.delTemporary(dwidenoise_input) # Step 2: Gibbs ringing removal (if available) if unring_cmd: run.command(unring_cmd + ' dwi_denoised.nii dwi_unring' + fsl_suffix + ' -n 100') file.delTemporary('dwi_denoised.nii') unring_output_path = fsl.findImage('dwi_unring') run.command('mrconvert ' + unring_output_path + ' dwi_unring.mif -json_import input.json') file.delTemporary(unring_output_path) file.delTemporary('input.json') # If fmap images and DWIs have been concatenated, now is the time to split them back apart dwipreproc_input = 'dwi_unring.mif' if unring_cmd else 'dwi_denoised.mif' if fmap_num_volumes: cat_input = 'dwi_unring.mif' if unring_cmd else 'dwi_denoised.mif' dwipreproc_se_epi = 'se_epi.mif' run.command('mrconvert ' + cat_input + ' ' + dwipreproc_se_epi + ' -coord 3 0:' + str(fmap_num_volumes - 1)) cat_num_volumes = image.Header(cat_input).size()[3] run.command('mrconvert ' + cat_input + ' dwipreproc_in.mif -coord 3 ' + str(fmap_num_volumes) + ':' + str(cat_num_volumes - 1)) file.delTemporary(dwipreproc_input) dwipreproc_input = 'dwipreproc_in.mif' dwipreproc_se_epi_option = ' -se_epi ' + dwipreproc_se_epi # Step 3: Distortion correction run.command('dwipreproc ' + dwipreproc_input + ' dwi_preprocessed.mif -rpe_header' + dwipreproc_se_epi_option) file.delTemporary(dwipreproc_input) if dwipreproc_se_epi: file.delTemporary(dwipreproc_se_epi) # Step 4: Bias field correction if dwibiascorrect_algo: run.command('dwibiascorrect dwi_preprocessed.mif dwi.mif ' + dwibiascorrect_algo) file.delTemporary('dwi_preprocessed.mif') else: run.function(shutil.move, 'dwi_preprocessed.mif', 'dwi.mif') # No longer branching based on whether or not -preprocessed was specified # Step 5: Generate a brain mask for DWI run.command('dwi2mask dwi.mif dwi_mask.mif') # Step 6: Perform brain extraction on the T1 image in its original space # (this is necessary for histogram matching prior to registration) # Use fsl_anat script run.command('mrconvert T1.mif T1.nii -stride -1,+2,+3') run.command(fslanat_cmd + ' -i T1.nii --noseg --nosubcortseg') run.command('mrconvert ' + fsl.findImage('T1.anat' + os.sep + 'T1_biascorr_brain_mask') + ' T1_mask.mif -datatype bit') run.command('mrconvert ' + fsl.findImage('T1.anat' + os.sep + 'T1_biascorr_brain') + ' T1_biascorr_brain.mif') file.delTemporary('T1.anat') # Step 7: Generate target images for T1->DWI registration run.command('dwiextract dwi.mif -bzero - | ' 'mrcalc - 0.0 -max - | ' 'mrmath - mean -axis 3 dwi_meanbzero.mif') run.command( 'mrcalc 1 dwi_meanbzero.mif -div dwi_mask.mif -mult - | ' 'mrhistmatch - T1_biascorr_brain.mif dwi_pseudoT1.mif -mask_input dwi_mask.mif -mask_target T1_mask.mif' ) run.command( 'mrcalc 1 T1_biascorr_brain.mif -div T1_mask.mif -mult - | ' 'mrhistmatch - dwi_meanbzero.mif T1_pseudobzero.mif -mask_input T1_mask.mif -mask_target dwi_mask.mif' ) # Step 8: Perform T1->DWI registration # Note that two registrations are performed: Even though we have a symmetric registration, # generation of the two histogram-matched images means that you will get slightly different # answers depending on which synthesized image & original image you use. run.command( 'mrregister T1_biascorr_brain.mif dwi_pseudoT1.mif -type rigid -mask1 T1_mask.mif -mask2 dwi_mask.mif -rigid rigid_T1_to_pseudoT1.txt' ) file.delTemporary('T1_biascorr_brain.mif') run.command( 'mrregister T1_pseudobzero.mif dwi_meanbzero.mif -type rigid -mask1 T1_mask.mif -mask2 dwi_mask.mif -rigid rigid_pseudobzero_to_bzero.txt' ) file.delTemporary('dwi_meanbzero.mif') run.command( 'transformcalc rigid_T1_to_pseudoT1.txt rigid_pseudobzero_to_bzero.txt average rigid_T1_to_dwi.txt' ) file.delTemporary('rigid_T1_to_pseudoT1.txt') file.delTemporary('rigid_pseudobzero_to_bzero.txt') run.command( 'mrtransform T1.mif T1_registered.mif -linear rigid_T1_to_dwi.txt') file.delTemporary('T1.mif') # Note: Since we're using a mask from fsl_anat (which crops the FoV), but using it as input to 5ttge fsl # (which is receiving the raw T1), we need to resample in order to have the same dimensions between these two run.command( 'mrtransform T1_mask.mif T1_mask_registered.mif -linear rigid_T1_to_dwi.txt -template T1_registered.mif -interp nearest' ) file.delTemporary('T1_mask.mif') # Step 9: Generate 5TT image for ACT run.command( '5ttgen fsl T1_registered.mif 5TT.mif -mask T1_mask_registered.mif') file.delTemporary('T1_mask_registered.mif') # Step 10: Estimate response functions for spherical deconvolution run.command( 'dwi2response dhollander dwi.mif response_wm.txt response_gm.txt response_csf.txt -mask dwi_mask.mif' ) # Step 11: Determine whether we are working with single-shell or multi-shell data shells = [ int(round(float(value))) for value in image.mrinfo('dwi.mif', 'shellvalues').strip().split() ] multishell = (len(shells) > 2) # Step 12: Perform spherical deconvolution # Use a dilated mask for spherical deconvolution as a 'safety margin' - # ACT should be responsible for stopping streamlines before they reach the edge of the DWI mask run.command('maskfilter dwi_mask.mif dilate dwi_mask_dilated.mif -npass 3') if multishell: run.command( 'dwi2fod msmt_csd dwi.mif response_wm.txt FOD_WM.mif response_gm.txt FOD_GM.mif response_csf.txt FOD_CSF.mif ' '-mask dwi_mask_dilated.mif -lmax 10,0,0') file.delTemporary('FOD_GM.mif') file.delTemporary('FOD_CSF.mif') else: # Still use the msmt_csd algorithm with single-shell data: Use hard non-negativity constraint # Also incorporate the CSF response to provide some fluid attenuation run.command( 'dwi2fod msmt_csd dwi.mif response_wm.txt FOD_WM.mif response_csf.txt FOD_CSF.mif ' '-mask dwi_mask_dilated.mif -lmax 10,0') file.delTemporary('FOD_CSF.mif') # Step 13: Generate the grey matter parcellation # The necessary steps here will vary significantly depending on the parcellation scheme selected run.command( 'mrconvert T1_registered.mif T1_registered.nii -stride +1,+2,+3') if app.args.parcellation == 'fs_2005' or app.args.parcellation == 'fs_2009': # Run FreeSurfer pipeline on this subject's T1 image run.command('recon-all -sd ' + app.tempDir + ' -subjid freesurfer -i T1_registered.nii') run.command('recon-all -sd ' + app.tempDir + ' -subjid freesurfer -all') # Grab the relevant parcellation image and target lookup table for conversion parc_image_path = os.path.join('freesurfer', 'mri') if app.args.parcellation == 'fs_2005': parc_image_path = os.path.join(parc_image_path, 'aparc+aseg.mgz') else: parc_image_path = os.path.join(parc_image_path, 'aparc.a2009s+aseg.mgz') # Perform the index conversion run.command('labelconvert ' + parc_image_path + ' ' + parc_lut_file + ' ' + mrtrix_lut_file + ' parc_init.mif') if app.cleanup: run.function(shutil.rmtree, 'freesurfer') # Fix the sub-cortical grey matter parcellations using FSL FIRST run.command('labelsgmfix parc_init.mif T1_registered.mif ' + mrtrix_lut_file + ' parc.mif') file.delTemporary('parc_init.mif') elif app.args.parcellation == 'aal' or app.args.parcellation == 'aal2': # Can use MNI152 image provided with FSL for registration run.command(flirt_cmd + ' -ref ' + mni152_path + ' -in T1_registered.nii -omat T1_to_MNI_FLIRT.mat -dof 12') run.command('transformconvert T1_to_MNI_FLIRT.mat T1_registered.nii ' + mni152_path + ' flirt_import T1_to_MNI_MRtrix.mat') file.delTemporary('T1_to_MNI_FLIRT.mat') run.command( 'transformcalc T1_to_MNI_MRtrix.mat invert MNI_to_T1_MRtrix.mat') file.delTemporary('T1_to_MNI_MRtrix.mat') run.command('mrtransform ' + parc_image_path + ' AAL.mif -linear MNI_to_T1_MRtrix.mat ' '-template T1_registered.mif -interp nearest') file.delTemporary('MNI_to_T1_MRtrix.mat') run.command('labelconvert AAL.mif ' + parc_lut_file + ' ' + mrtrix_lut_file + ' parc.mif') file.delTemporary('AAL.mif') else: app.error('Unknown parcellation scheme requested: ' + app.args.parcellation) file.delTemporary('T1_registered.nii') # Step 14: Generate the tractogram # If not manually specified, determine the appropriate number of streamlines based on the number of nodes in the parcellation: # mean edge weight of 1,000 streamlines # A smaller FOD amplitude threshold of 0.06 (default 0.1) is used for tracking due to the use of the msmt_csd # algorithm, which imposes a hard rather than soft non-negativity constraint num_nodes = int(image.statistic('parc.mif', 'max')) num_streamlines = 1000 * num_nodes * num_nodes if app.args.streamlines: num_streamlines = app.args.streamlines run.command( 'tckgen FOD_WM.mif tractogram.tck -act 5TT.mif -backtrack -crop_at_gmwmi -cutoff 0.06 -maxlength 250 -power 0.33 ' '-select ' + str(num_streamlines) + ' -seed_dynamic FOD_WM.mif') # Step 15: Use SIFT2 to determine streamline weights fd_scale_gm_option = '' if not multishell: fd_scale_gm_option = ' -fd_scale_gm' run.command( 'tcksift2 tractogram.tck FOD_WM.mif weights.csv -act 5TT.mif -out_mu mu.txt' + fd_scale_gm_option) # Step 16: Generate a TDI (to verify that SIFT2 has worked correctly) with open('mu.txt', 'r') as f: mu = float(f.read()) run.command( 'tckmap tractogram.tck -tck_weights_in weights.csv -template FOD_WM.mif -precise - | ' 'mrcalc - ' + str(mu) + ' -mult tdi.mif') # Step 17: Generate the connectome # Only provide the standard density-weighted connectome for now run.command( 'tck2connectome tractogram.tck parc.mif connectome.csv -tck_weights_in weights.csv' ) file.delTemporary('weights.csv') # Move necessary files to output directory run.function( shutil.copy, 'connectome.csv', os.path.join(output_dir, 'connectome', label + '_connectome.csv')) run.command('mrconvert dwi.mif ' + os.path.join(output_dir, 'dwi', label + '_dwi.nii.gz') + ' -export_grad_fsl ' + os.path.join(output_dir, 'dwi', label + '_dwi.bvec') + ' ' + os.path.join(output_dir, 'dwi', label + '_dwi.bval') + ' -json_export ' + os.path.join(output_dir, 'dwi', label + '_dwi.json')) run.command('mrconvert tdi.mif ' + os.path.join(output_dir, 'dwi', label + '_tdi.nii.gz')) run.function(shutil.copy, 'mu.txt', os.path.join(output_dir, 'connectome', label + '_mu.txt')) run.function(shutil.copy, 'response_wm.txt', os.path.join(output_dir, 'dwi', label + '_response.txt')) # Manually wipe and zero the temp directory (since we might be processing more than one subject) os.chdir(cwd) if app.cleanup: app.console('Deleting temporary directory ' + app.tempDir) # Can't use run.function() here; it'll try to write to the log file that resides in the temp directory just deleted shutil.rmtree(app.tempDir) else: app.console('Contents of temporary directory kept, location: ' + app.tempDir) app.tempDir = ''
def getInputs(): import os from mrtrix3 import app, path, run run.command('mrconvert ' + path.fromUser(app.args.in_5tt, True) + ' ' + path.toTemp('5tt.mif', True)) if app.args.dirs: run.command('mrconvert ' + path.fromUser(app.args.dirs, True) + ' ' + path.toTemp('dirs.mif', True) + ' -stride 0,0,0,1')
if not app.args.fslbval: bvallist = [i + '.bval' for i in DWInlist] else: UserBvalpath = app.args.fslbval.rsplit(',') bvallist = [os.path.realpath(i) for i in UserBvalpath] if not app.args.fslbvec: bveclist = [i + '.bvec' for i in DWInlist] else: UserBvecpath = app.args.fslbvec.rsplit(',') bveclist = [os.path.realpath(i) for i in UserBvecpath] if len(DWInlist) == 1: if not isdicom: call('mrconvert -force -stride -1,2,3,4 -fslgrad ' + bveclist[0] + ' ' + bvallist[0] + ' ' + ''.join(DWInlist) + ''.join(DWIext) + ' ' + path.toTemp('dwi.mif', True), shell=True) else: call('mrconvert -force -stride -1,2,3,4 ' + ''.join(DWInlist) + ' ' + path.toTemp('dwi.mif', True), shell=True) else: for (idx, i) in enumerate(DWInlist): if not isdicom: call('mrconvert -force -stride -1,2,3,4 -fslgrad ' + bveclist[idx] + ' ' + bvallist[idx] + ' ' + i + DWIext[idx] + ' ' + path.toTemp('dwi' + str(idx) + '.mif', True), shell=True) else: call('mrconvert -force -stride -1,2,3,4 ' + i + ' ' + path.toTemp('dwi' + str(idx) + '.mif', True),
def getInputs(): #pylint: disable=unused-variable import os, shutil #pylint: disable=unused-variable from mrtrix3 import app, path, run checkGIFinput(path.fromUser(app.args.input, True)) run.command('mrconvert ' + path.fromUser(app.args.input, True) + ' ' + path.toTemp('input.mif', True))
dwi_ind_size = [[0,0,0,0]] if not app.args.fslbval: bvallist = [i + '.bval' for i in DWInlist] else: UserBvalpath = app.args.fslbval.rsplit(',') bvallist = [os.path.realpath(i) for i in UserBvalpath] if not app.args.fslbvec: bveclist = [i + '.bvec' for i in DWInlist] else: UserBvecpath = app.args.fslbvec.rsplit(',') bveclist = [os.path.realpath(i) for i in UserBvecpath] if len(DWInlist) == 1: if not isdicom: run.command('mrconvert -stride -1,2,3,4 -fslgrad ' + bveclist[0] + ' ' + bvallist[0] + ' ' + ''.join(DWInlist) + ''.join(DWIext) + ' ' + path.toTemp('dwi.mif',True)) else: run.command('mrconvert -stride -1,2,3,4 ' + ''.join(DWInlist) + ' ' + path.toTemp('dwi.mif',True)) else: for idx,i in enumerate(DWInlist): if not isdicom: run.command('mrconvert -stride -1,2,3,4 -fslgrad ' + bveclist[idx] + ' ' + bvallist[idx] + ' ' + i + DWIext[idx] + ' ' + path.toTemp('dwi' + str(idx) + '.mif',True)) else: run.command('mrconvert -stride -1,2,3,4 ' + i + ' ' + path.toTemp('dwi' + str(idx) + '.mif',True)) dwi_header = image.Header(path.toTemp('dwi' + str(idx) + '.mif',True)) dwi_ind_size.append([ int(s) for s in dwi_header.size() ]) miflist.append(path.toTemp('dwi' + str(idx) + '.mif',True)) DWImif = ' '.join(miflist) run.command('mrcat -axis 3 ' + DWImif + ' ' + path.toTemp('dwi.mif',True)) app.gotoTempDir()