def makeTempDir(): import os, random, string, sys from lib.errorMessage import errorMessage from lib.printMessage import printMessage from lib.readMRtrixConfSetting import readMRtrixConfSetting global args, tempDir if args.cont: printMessage('Skipping temporary directory creation due to use of -continue option') return if tempDir: errorMessage('Script error: Cannot use multiple temporary directories') if args.tempdir: dir_path = os.path.abspath(args.tempdir) else: dir_path = readMRtrixConfSetting('TmpFileDir') if not dir_path: if os.name == 'posix': dir_path = '/tmp' else: dir_path = '.' prefix = readMRtrixConfSetting('TmpFilePrefix') if not prefix: prefix = os.path.basename(sys.argv[0]) + '-tmp-' tempDir = dir_path while os.path.isdir(tempDir): random_string = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(6)) tempDir = os.path.join(dir_path, prefix + random_string) + os.sep os.makedirs(tempDir) printMessage('Generated temporary directory: ' + tempDir) with open(os.path.join(tempDir, 'cwd.txt'), 'w') as outfile: outfile.write(workingDir + '\n') with open(os.path.join(tempDir, 'command.txt'), 'w') as outfile: outfile.write(' '.join(sys.argv) + '\n')
def gotoTempDir(): import os from lib.errorMessage import errorMessage from lib.printMessage import printMessage global tempDir if not tempDir: errorMessage('Script error: No temporary directory location set') if verbosity: printMessage('Changing to temporary directory (' + tempDir + ')') os.chdir(tempDir)
def initialise(desc): import argparse import lib.app from lib.errorMessage import errorMessage global parser if not lib.app.author: errorMessage('Script error: No author defined') lib.app.parser = Parser(description=desc, add_help=False) standard_options = lib.app.parser.add_argument_group('Standard options') standard_options.add_argument( '-continue', nargs=2, dest='cont', metavar=('<TempDir>', '<LastFile>'), help= 'Continue the script from a previous execution; must provide the temporary directory path, and the name of the last successfully-generated file' ) standard_options.add_argument( '-force', action='store_true', help='Force overwrite of output files if pre-existing') standard_options.add_argument( '-help', action='store_true', help='Display help information for the script') standard_options.add_argument( '-nocleanup', action='store_true', help= 'Do not delete temporary files during script, or temporary directory at script completion' ) standard_options.add_argument( '-nthreads', metavar='number', help= 'Use this number of threads in MRtrix multi-threaded applications (0 disables multi-threading)' ) standard_options.add_argument( '-tempdir', metavar='/path/to/tmp/', help= 'Manually specify the path in which to generate the temporary directory' ) standard_options.add_argument( '-quiet', action='store_true', help='Suppress all console output during script execution') standard_options.add_argument( '-verbose', action='store_true', help='Display additional information for every command invoked') flagMutuallyExclusiveOptions(['quiet', 'verbose'])
def getInputFiles(): import os import lib.app from lib.imagesMatch import imagesMatch from lib.errorMessage import errorMessage from lib.getHeaderInfo import getHeaderInfo from lib.runCommand import runCommand if hasattr(lib.app.args, 'mask') and lib.app.args.mask is not None: runCommand('mrconvert ' + lib.app.args.mask + ' ' + os.path.join(lib.app.tempDir, 'mask.mif') + ' -datatype bit -stride -1,+2,+3') if hasattr(lib.app.args, 't2') and lib.app.args.t2 is not None: if not imagesMatch(lib.app.args.input, lib.app.args.t2): errorMessage('Provided T2 image does not match input T1 image') runCommand('mrconvert ' + lib.app.args.t2 + ' ' + os.path.join(lib.app.tempDir, 'T2.nii') + ' -stride -1,+2,+3')
def runCommand(cmd, exitOnError=True): import lib.app, os, sys from lib.errorMessage import errorMessage from lib.warnMessage import warnMessage import distutils from distutils.spawn import find_executable global mrtrix_bin_list global mrtrix_bin_path if not mrtrix_bin_list: mrtrix_bin_path = os.path.join(os.path.abspath(os.path.dirname(os.path.realpath(__file__))), os.pardir, os.pardir, 'release', 'bin'); mrtrix_bin_list = os.listdir(mrtrix_bin_path) if lib.app.lastFile: # Check to see if the last file produced is produced by this command; # if it is, this will be the last called command that gets skipped if lib.app.lastFile in cmd: lib.app.lastFile = '' if lib.app.verbosity: sys.stdout.write('Skipping command: ' + cmd + '\n') sys.stdout.flush() return binary_name = cmd.split()[0] # Automatically add the relevant flags to any mrtrix command calls, including filling them in around the pipes if binary_name in mrtrix_bin_list: cmdsplit = cmd.split() binary_in_path = find_executable(binary_name) if not binary_in_path or not os.path.samefile(binary_in_path, os.path.join(mrtrix_bin_path, binary_name)): cmdsplit[0] = os.path.join(mrtrix_bin_path, binary_name) for index, item in enumerate(cmdsplit): if item == '|': if lib.app.mrtrixNThreads: cmdsplit[index] = lib.app.mrtrixNThreads + ' |' if lib.app.mrtrixQuiet: cmdsplit[index] = lib.app.mrtrixQuiet + ' ' + cmdsplit[index] cmdsplit.append(lib.app.mrtrixNThreads) cmdsplit.append(lib.app.mrtrixQuiet) cmd = ' '.join(cmdsplit) if lib.app.verbosity: sys.stdout.write(lib.app.colourConsole + 'Command:' + lib.app.colourClear + ' ' + cmd + '\n') sys.stdout.flush() if (os.system(cmd)): if exitOnError: errorMessage('Command failed: ' + cmd) else: warnMessage('Command failed: ' + cmd)
def runCommand(cmd, exitOnError=True): import lib.app, os, sys from lib.errorMessage import errorMessage from lib.warnMessage import warnMessage global mrtrix_bin_list if not mrtrix_bin_list: mrtrix_bin_path = os.path.join( os.path.abspath(os.path.dirname(os.path.realpath(sys.argv[0]))), os.pardir, 'bin') mrtrix_bin_list = [ f for f in os.listdir(mrtrix_bin_path) if not "__" in f ] if lib.app.lastFile: # Check to see if the last file produced is produced by this command; # if it is, this will be the last called command that gets skipped if lib.app.lastFile in cmd: lib.app.lastFile = '' if lib.app.verbosity: sys.stdout.write('Skipping command: ' + cmd + '\n') sys.stdout.flush() return binary_name = cmd.split()[0] # Automatically add the relevant flags to any mrtrix command calls, including filling them in around the pipes if binary_name in mrtrix_bin_list: cmdsplit = cmd.split() for index, item in enumerate(cmdsplit): if item == '|': if lib.app.mrtrixNThreads: cmdsplit[index] = lib.app.mrtrixNThreads + ' |' if lib.app.mrtrixQuiet: cmdsplit[ index] = lib.app.mrtrixQuiet + ' ' + cmdsplit[index] cmdsplit.append(lib.app.mrtrixNThreads) cmdsplit.append(lib.app.mrtrixQuiet) cmd = ' '.join(cmdsplit) if lib.app.verbosity: sys.stdout.write(lib.app.colourConsole + 'Command: ' + lib.app.colourClear + cmd + '\n') sys.stdout.flush() if (os.system(cmd)): if exitOnError: errorMessage('Command failed: ' + cmd) else: warnMessage('Command failed: ' + cmd)
def checkOutputFile(path): import os from lib.errorMessage import errorMessage from lib.warnMessage import warnMessage global args, mrtrixForce if not path: return if os.path.exists(path): if args.force: warnMessage('Output file ' + os.path.basename(path) + ' already exists; will be overwritten at script completion') mrtrixForce = ' -force' else: errorMessage('Output file ' + path + ' already exists (use -force to override)') sys.exit(1)
def checkOutputFile(path): import os from lib.errorMessage import errorMessage from lib.warnMessage import warnMessage global args, mrtrixForce if not path: return if os.path.exists(path): if args.force: warnMessage( 'Output file ' + os.path.basename(path) + ' already exists; will be overwritten at script completion') mrtrixForce = ' -force' else: errorMessage('Output file ' + os.path.basename(path) + ' already exists (use -force to override)') sys.exit(1)
def getFSLEddyPath(cuda): import os from lib.binaryInPath import binaryInPath from lib.errorMessage import errorMessage from lib.warnMessage import warnMessage if cuda: if binaryInPath('eddy_cuda'): return 'eddy_cuda' else: warnMessage('CUDA version of eddy not found; running standard version') if binaryInPath('eddy_openmp'): return 'eddy_openmp' if binaryInPath('eddy'): return 'eddy' if binaryInPath('fsl5.0-eddy'): return 'fsl5.0-eddy' errorMessage('Could not find FSL program eddy; please verify FSL install')
def getPEAxis(string): from lib.errorMessage import errorMessage if string.isdigit(): PE_axis = int(string) if PE_axis < 0 or PE_axis > 2: errorMessage('Phase encode axis must be either 0, 1 or 2') return PE_axis else: string = string.lower() if string == 'lr' or string == 'rl': return 0 elif string == 'ap' or string == 'pa': return 1 elif string == 'is' or string == 'si': return 2 else: errorMessage('Unrecognized phase encode direction specifier')
def getFSLEddyPath(cuda): import os from lib.binaryInPath import binaryInPath from lib.errorMessage import errorMessage from lib.warnMessage import warnMessage if cuda: if binaryInPath('eddy_cuda'): return 'eddy_cuda' else: warnMessage( 'CUDA version of eddy not found; running standard version') if binaryInPath('eddy_openmp'): return 'eddy_openmp' if binaryInPath('eddy'): return 'eddy' if binaryInPath('fsl5.0-eddy'): return 'fsl5.0-eddy' errorMessage('Could not find FSL program eddy; please verify FSL install')
def initialise(desc): import argparse import lib.app from lib.errorMessage import errorMessage global parser if not lib.app.author: errorMessage('Script error: No author defined') lib.app.parser = Parser(description=desc, add_help=False) standard_options = lib.app.parser.add_argument_group('Standard options') standard_options.add_argument('-continue', nargs=2, dest='cont', metavar=('<TempDir>', '<LastFile>'), help='Continue the script from a previous execution; must provide the temporary directory path, and the name of the last successfully-generated file') standard_options.add_argument('-force', action='store_true', help='Force overwrite of output files if pre-existing') standard_options.add_argument('-help', action='store_true', help='Display help information for the script') standard_options.add_argument('-nocleanup', action='store_true', help='Do not delete temporary files during script, or temporary directory at script completion') standard_options.add_argument('-nthreads', metavar='number', help='Use this number of threads in MRtrix multi-threaded applications (0 disables multi-threading)') standard_options.add_argument('-tempdir', metavar='/path/to/tmp/', help='Manually specify the path in which to generate the temporary directory') standard_options.add_argument('-quiet', action='store_true', help='Suppress all console output during script execution') standard_options.add_argument('-verbose', action='store_true', help='Display additional information for every command invoked') flagMutuallyExclusiveOptions( [ 'quiet', 'verbose' ] )
def getInputFiles(): import os import lib.app from lib.imagesMatch import imagesMatch from lib.errorMessage import errorMessage from lib.getHeaderInfo import getHeaderInfo from lib.getUserPath import getUserPath from lib.runCommand import runCommand if hasattr(lib.app.args, 'mask') and lib.app.args.mask is not None: runCommand('mrconvert ' + getUserPath(lib.app.args.mask, True) + ' ' + os.path.join(lib.app.tempDir, 'mask.mif') + ' -datatype bit -stride -1,+2,+3') if hasattr(lib.app.args, 't2') and lib.app.args.t2 is not None: if not imagesMatch(lib.app.args.input, lib.app.args.t2): errorMessage('Provided T2 image does not match input T1 image') runCommand('mrconvert ' + getUserPath(lib.app.args.t2, True) + ' ' + os.path.join(lib.app.tempDir, 'T2.nii') + ' -stride -1,+2,+3')
def getPEAxis(string): from lib.errorMessage import errorMessage if string.isdigit(): PE_axis = int(string) if PE_axis < 0 or PE_axis > 2: errorMessage("Phase encode axis must be either 0, 1 or 2") return PE_axis else: string = string.lower() if string == "lr" or string == "rl": return 0 elif string == "ap" or string == "pa": return 1 elif string == "is" or string == "si": return 2 else: errorMessage("Unrecognized phase encode direction specifier")
def makeTempDir(): import os, random, string, sys from lib.errorMessage import errorMessage from lib.printMessage import printMessage from lib.readMRtrixConfSetting import readMRtrixConfSetting global args, tempDir if args.cont: printMessage( 'Skipping temporary directory creation due to use of -continue option' ) return if tempDir: errorMessage('Script error: Cannot use multiple temporary directories') if args.tempdir: dir_path = os.path.abspath(args.tempdir) else: dir_path = readMRtrixConfSetting('TmpFileDir') if not dir_path: if os.name == 'posix': dir_path = '/tmp' else: dir_path = '.' prefix = readMRtrixConfSetting('TmpFilePrefix') if not prefix: prefix = os.path.basename(sys.argv[0]) + '-tmp-' tempDir = dir_path while os.path.isdir(tempDir): random_string = ''.join( random.choice(string.ascii_uppercase + string.digits) for x in range(6)) tempDir = os.path.join(dir_path, prefix + random_string) + os.sep os.makedirs(tempDir) printMessage('Generated temporary directory: ' + tempDir) with open(os.path.join(tempDir, 'cwd.txt'), 'w') as outfile: outfile.write(workingDir + '\n') with open(os.path.join(tempDir, 'command.txt'), 'w') as outfile: outfile.write(' '.join(sys.argv) + '\n')
def execute(): import os, shutil import lib.app from lib.errorMessage import errorMessage from lib.getHeaderInfo import getHeaderInfo from lib.getUserPath import getUserPath from lib.runCommand import runCommand shells = [ int(round(float(x))) for x in getHeaderInfo('dwi.mif', 'shells').split() ] # Get lmax information (if provided) lmax = [] if lib.app.args.lmax: lmax = [int(x.strip()) for x in lib.app.args.lmax.split(',')] if not len(lmax) == len(shells): errorMessage('Number of manually-defined lmax\'s (' + str(len(lmax)) + ') does not match number of b-value shells (' + str(len(shells)) + ')') for l in lmax: if l % 2: errorMessage('Values for lmax must be even') if l < 0: errorMessage('Values for lmax must be non-negative') # Do we have directions, or do we need to calculate them? if not os.path.exists('dirs.mif'): runCommand( 'dwi2tensor dwi.mif - -mask in_voxels.mif | tensor2metric - -vector dirs.mif' ) # Loop over shells response = [] max_length = 0 for index, b in enumerate(shells): lmax_option = '' if lmax: lmax_option = ' -lmax ' + str(lmax[index]) runCommand( 'dwiextract dwi.mif - -shell ' + str(b) + ' | amp2sh - - | sh2response - in_voxels.mif dirs.mif response_b' + str(b) + '.txt' + lmax_option) shell_response = open('response_b' + str(b) + '.txt', 'r').read().split() response.append(shell_response) max_length = max(max_length, len(shell_response)) with open('response.txt', 'w') as f: for line in response: line += ['0'] * (max_length - len(line)) f.write(' '.join(line) + '\n') shutil.copyfile('response.txt', getUserPath(lib.app.args.output, False)) shutil.copyfile('in_voxels.mif', 'voxels.mif')
def getPEDir(string): from lib.errorMessage import errorMessage try: PE_axis = abs(int(string)) if PE_axis > 2: errorMessage('Phase encode axis must be either 0, 1 or 2') reverse = (string[0] == '-') # Allow -0 return ( PE_axis, reverse ) except: string = string.lower() if string == 'lr': return ( 0, False ) elif string == 'rl': return ( 0, True ) elif string == 'pa': return ( 1, False ) elif string == 'ap': return ( 1, True ) elif string == 'is': return ( 2, False ) elif string == 'si': return ( 2, True ) else: errorMessage('Unrecognized phase encode direction specifier')
def execute(): import os, sys import lib.app from lib.errorMessage import errorMessage from lib.getUserPath import getUserPath from lib.runCommand import runCommand lut_input_path = 'LUT.txt' if not os.path.exists('LUT.txt'): freesurfer_home = os.environ.get('FREESURFER_HOME', '') if not freesurfer_home: errorMessage( 'Environment variable FREESURFER_HOME is not set; please run appropriate FreeSurfer configuration script, set this variable manually, or provide script with path to file FreeSurferColorLUT.txt using -lut option' ) lut_input_path = os.path.join(freesurfer_home, 'FreeSurferColorLUT.txt') if not os.path.isfile(lut_input_path): errorMessage( 'Could not find FreeSurfer lookup table file (expected location: ' + freesurfer_lut + '), and none provided using -lut') if lib.app.args.sgm_amyg_hipp: lut_output_file_name = 'FreeSurfer2ACT_sgm_amyg_hipp.txt' else: lut_output_file_name = 'FreeSurfer2ACT.txt' lut_output_path = os.path.join( os.path.dirname(os.path.abspath(sys.argv[0])), 'data', lut_output_file_name) if not os.path.isfile(lut_output_path): errorMessage( 'Could not find lookup table file for converting FreeSurfer parcellation output to tissues (expected location: ' + lut_output_path + ')') # Initial conversion from FreeSurfer parcellation to five principal tissue types runCommand('labelconvert input.mif ' + lut_input_path + ' ' + lut_output_path + ' indices.mif') # Use mrcrop to reduce file size if lib.app.args.nocrop: image = 'indices.mif' else: image = 'indices_cropped.mif' runCommand('mrthreshold indices.mif - -abs 0.5 | mrcrop indices.mif ' + image + ' -mask -') # Convert into the 5TT format for ACT runCommand('mrcalc ' + image + ' 1 -eq cgm.mif') runCommand('mrcalc ' + image + ' 2 -eq sgm.mif') runCommand('mrcalc ' + image + ' 3 -eq wm.mif') runCommand('mrcalc ' + image + ' 4 -eq csf.mif') runCommand('mrcalc ' + image + ' 5 -eq path.mif') runCommand( 'mrcat cgm.mif sgm.mif wm.mif csf.mif path.mif - -axis 3 | mrconvert - result.mif -datatype float32' )
def execute(): import os, shutil import lib.app from lib.errorMessage import errorMessage from lib.getHeaderInfo import getHeaderInfo from lib.getUserPath import getUserPath from lib.runCommand import runCommand shells = [ int(round(float(x))) for x in getHeaderInfo('dwi.mif', 'shells').split() ] # Get lmax information (if provided) lmax = [ ] if lib.app.args.lmax: lmax = [ int(x.strip()) for x in lib.app.args.lmax.split(',') ] if not len(lmax) == len(shells): errorMessage('Number of manually-defined lmax\'s (' + str(len(lmax)) + ') does not match number of b-value shells (' + str(len(shells)) + ')') for l in lmax: if l%2: errorMessage('Values for lmax must be even') if l<0: errorMessage('Values for lmax must be non-negative') # Do we have directions, or do we need to calculate them? if not os.path.exists('dirs.mif'): runCommand('dwi2tensor dwi.mif - -mask in_voxels.mif | tensor2metric - -vector dirs.mif') # Loop over shells response = [ ] max_length = 0 for index, b in enumerate(shells): lmax_option = '' if lmax: lmax_option = ' -lmax ' + str(lmax[index]) runCommand('dwiextract dwi.mif - -shell ' + str(b) + ' | amp2sh - - | sh2response - in_voxels.mif dirs.mif response_b' + str(b) + '.txt' + lmax_option) shell_response = open('response_b' + str(b) + '.txt', 'r').read().split() response.append(shell_response) max_length = max(max_length, len(shell_response)) with open('response.txt', 'w') as f: for line in response: line += ['0'] * (max_length - len(line)) f.write(' '.join(line) + '\n') shutil.copyfile('response.txt', getUserPath(lib.app.args.output, False)) shutil.copyfile('in_voxels.mif', 'voxels.mif')
def execute(): import os, sys import lib.app from lib.errorMessage import errorMessage from lib.getUserPath import getUserPath from lib.runCommand import runCommand lut_input_path = 'LUT.txt' if not os.path.exists('LUT.txt'): freesurfer_home = os.environ.get('FREESURFER_HOME', '') if not freesurfer_home: errorMessage('Environment variable FREESURFER_HOME is not set; please run appropriate FreeSurfer configuration script, set this variable manually, or provide script with path to file FreeSurferColorLUT.txt using -lut option') lut_input_path = os.path.join(freesurfer_home, 'FreeSurferColorLUT.txt') if not os.path.isfile(lut_input_path): errorMessage('Could not find FreeSurfer lookup table file (expected location: ' + freesurfer_lut + '), and none provided using -lut') if lib.app.args.sgm_amyg_hipp: lut_output_file_name = 'FreeSurfer2ACT_sgm_amyg_hipp.txt' else: lut_output_file_name = 'FreeSurfer2ACT.txt' lut_output_path = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), 'data', lut_output_file_name); if not os.path.isfile(lut_output_path): errorMessage('Could not find lookup table file for converting FreeSurfer parcellation output to tissues (expected location: ' + lut_output_path + ')') # Initial conversion from FreeSurfer parcellation to five principal tissue types runCommand('labelconvert input.mif ' + lut_input_path + ' ' + lut_output_path + ' indices.mif') # Use mrcrop to reduce file size if lib.app.args.nocrop: image = 'indices.mif' else: image = 'indices_cropped.mif' runCommand('mrthreshold indices.mif - -abs 0.5 | mrcrop indices.mif ' + image + ' -mask -') # Convert into the 5TT format for ACT runCommand('mrcalc ' + image + ' 1 -eq cgm.mif') runCommand('mrcalc ' + image + ' 2 -eq sgm.mif') runCommand('mrcalc ' + image + ' 3 -eq wm.mif') runCommand('mrcalc ' + image + ' 4 -eq csf.mif') runCommand('mrcalc ' + image + ' 5 -eq path.mif') runCommand('mrcat cgm.mif sgm.mif wm.mif csf.mif path.mif - -axis 3 | mrconvert - result.mif -datatype float32')
options.add_argument('-group_subset', help='Define a subset of participants to be used when generating the group-average FOD template and response functions. The subset is to be supplied as a comma separate list. Note the subset should be representable of your entire population and not biased towards one particular group. For example in a patient-control comparison, choose equal numbers of patients and controls. Used in group1 and group2 analysis levels.', nargs=1) options.add_argument('-num_tracks', type=int, default='20000000', help='define the number of streamlines to be computed ' 'when performing tractography on the FOD template. ' '(group3 analysis level only)') options.add_argument('-num_tracks_sift', type=int, default='2000000', help='define the number of streamlines to ' 'remain after performing SIFT on the tractogram' '(group3 analysis level only)') lib.app.initialise() if isWindows(): errorMessage('Script cannot be run on Windows due to FSL dependency') nthreads = '' if (lib.app.args.n_cpus): lib.app.mrtrixNThreads = '-nthreads ' + str(lib.app.args.n_cpus) + ' ' subjects_to_analyze = [] # only for a subset of subjects if lib.app.args.participant_label: subjects_to_analyze = lib.app.args.participant_label # for all subjects else: subject_dirs = glob(os.path.join(lib.app.args.bids_dir, 'sub-*')) subjects_to_analyze = [subject_dir.split("-")[-1] for subject_dir in subject_dirs] # create output subjects directory
def execute(): import math, os, shutil import lib.app from lib.getHeaderInfo import getHeaderInfo from lib.getImageStat import getImageStat from lib.getUserPath import getUserPath from lib.printMessage import printMessage from lib.runCommand import runCommand from lib.warnMessage import warnMessage from lib.errorMessage import errorMessage # Ideally want to use the oversampling-based regridding of the 5TT image from the SIFT model, not mrtransform # May need to commit 5ttregrid... # Verify input 5tt image sizes = [ int(x) for x in getHeaderInfo('5tt.mif', 'size').split() ] datatype = getHeaderInfo('5tt.mif', 'datatype') if not len(sizes) == 4 or not sizes[3] == 5 or not datatype.startswith('Float'): errorMessage('Imported anatomical image ' + os.path.basename(lib.app.args.in_5tt) + ' is not in the 5TT format') # Get shell information shells = [ int(round(float(x))) for x in getHeaderInfo('dwi.mif', 'shells').split() ] if len(shells) < 3: warnMessage('Less than three b-value shells; response functions will not be applicable in MSMT-CSD algorithm') # Get lmax information (if provided) wm_lmax = [ ] if lib.app.args.lmax: wm_lmax = [ int(x.strip()) for x in lib.app.args.lmax.split(',') ] if not len(wm_lmax) == len(shells): errorMessage('Number of manually-defined lmax\'s (' + str(len(wm_lmax)) + ') does not match number of b-value shells (' + str(len(shells)) + ')') for l in wm_lmax: if l%2: errorMessage('Values for lmax must be even') if l<0: errorMessage('Values for lmax must be non-negative') runCommand('dwi2tensor dwi.mif - -mask mask.mif | tensor2metric - -fa fa.mif -vector vector.mif') if not os.path.exists('dirs.mif'): shutil.copy('vector.mif', 'dirs.mif') runCommand('mrtransform 5tt.mif 5tt_regrid.mif -template fa.mif -interp linear') # Basic tissue masks runCommand('mrconvert 5tt_regrid.mif - -coord 3 2 -axes 0,1,2 | mrcalc - ' + str(lib.app.args.pvf) + ' -gt mask.mif -mult wm_mask.mif') runCommand('mrconvert 5tt_regrid.mif - -coord 3 0 -axes 0,1,2 | mrcalc - ' + str(lib.app.args.pvf) + ' -gt fa.mif ' + str(lib.app.args.fa) + ' -lt -mult mask.mif -mult gm_mask.mif') runCommand('mrconvert 5tt_regrid.mif - -coord 3 3 -axes 0,1,2 | mrcalc - ' + str(lib.app.args.pvf) + ' -gt fa.mif ' + str(lib.app.args.fa) + ' -lt -mult mask.mif -mult csf_mask.mif') # Revise WM mask to only include single-fibre voxels printMessage('Calling dwi2response recursively to select WM single-fibre voxels using \'' + lib.app.args.wm_algo + '\' algorithm') recursive_cleanup_option='' if not lib.app.cleanup: recursive_cleanup_option = ' -nocleanup' runCommand('dwi2response -quiet -tempdir ' + lib.app.tempDir + recursive_cleanup_option + ' ' + lib.app.args.wm_algo + ' dwi.mif wm_ss_response.txt -mask wm_mask.mif -voxels wm_sf_mask.mif') # Check for empty masks wm_voxels = int(getImageStat('wm_sf_mask.mif', 'count', 'wm_sf_mask.mif')) gm_voxels = int(getImageStat('gm_mask.mif', 'count', 'gm_mask.mif')) csf_voxels = int(getImageStat('csf_mask.mif', 'count', 'csf_mask.mif')) empty_masks = [ ] if not wm_voxels: empty_masks.append('WM') if not gm_voxels: empty_masks.append('GM') if not csf_voxels: empty_masks.append('CSF') if empty_masks: message = ','.join(empty_masks) message += ' tissue mask' if len(empty_masks) > 1: message += 's' message += ' empty; cannot estimate response function' if len(empty_masks) > 1: message += 's' errorMessage(message) # For each of the three tissues, generate a multi-shell response # Since here we're guaranteeing that GM and CSF will be isotropic in all shells, let's use mrstats rather than sh2response (seems a bit weird passing a directions file to sh2response with lmax=0...) wm_responses = [ ] gm_responses = [ ] csf_responses = [ ] max_length = 0 for index, b in enumerate(shells): dwi_path = 'dwi_b' + str(b) + '.mif' # dwiextract will yield a 4D image, even if there's only a single volume in a shell runCommand('dwiextract dwi.mif -shell ' + str(b) + ' ' + dwi_path) this_b_lmax_option = '' if wm_lmax: this_b_lmax_option = ' -lmax ' + str(wm_lmax[index]) runCommand('amp2sh ' + dwi_path + ' - | sh2response - wm_sf_mask.mif dirs.mif wm_response_b' + str(b) + '.txt' + this_b_lmax_option) wm_response = open('wm_response_b' + str(b) + '.txt', 'r').read().split() wm_responses.append(wm_response) max_length = max(max_length, len(wm_response)) mean_dwi_path = 'dwi_b' + str(b) + '_mean.mif' runCommand('mrmath ' + dwi_path + ' mean ' + mean_dwi_path + ' -axis 3') gm_mean = float(getImageStat(mean_dwi_path, 'mean', 'gm_mask.mif')) csf_mean = float(getImageStat(mean_dwi_path, 'mean', 'csf_mask.mif')) gm_responses .append( str(gm_mean * math.sqrt(4.0 * math.pi)) ) csf_responses.append( str(csf_mean * math.sqrt(4.0 * math.pi)) ) with open('wm.txt', 'w') as f: for line in wm_responses: line += ['0'] * (max_length - len(line)) f.write(' '.join(line) + '\n') with open('gm.txt', 'w') as f: for line in gm_responses: f.write(line + '\n') with open('csf.txt', 'w') as f: for line in csf_responses: f.write(line + '\n') shutil.copyfile('wm.txt', getUserPath(lib.app.args.out_wm, False)) shutil.copyfile('gm.txt', getUserPath(lib.app.args.out_gm, False)) shutil.copyfile('csf.txt', getUserPath(lib.app.args.out_csf, False)) # Generate output 4D binary image with voxel selections; RGB as in MSMT-CSD paper runCommand('mrcat csf_mask.mif gm_mask.mif wm_sf_mask.mif voxels.mif -axis 3')
def runCommand(cmd, exitOnError=True): import lib.app, os, subprocess, sys from lib.errorMessage import errorMessage from lib.isWindows import isWindows from lib.printMessage import printMessage from lib.warnMessage import warnMessage import distutils from distutils.spawn import find_executable global mrtrix_bin_list global mrtrix_bin_path if not mrtrix_bin_list: mrtrix_bin_path = os.path.abspath(os.path.join(os.path.abspath(os.path.dirname(os.path.realpath(__file__))), os.pardir, os.pardir, 'release', 'bin')); # On Windows, strip the .exe's mrtrix_bin_list = [ os.path.splitext(name)[0] for name in os.listdir(mrtrix_bin_path) ] if lib.app.lastFile: # Check to see if the last file produced is produced by this command; # if it is, this will be the last called command that gets skipped if lib.app.lastFile in cmd: lib.app.lastFile = '' if lib.app.verbosity: sys.stdout.write('Skipping command: ' + cmd + '\n') sys.stdout.flush() return # Vectorise the command string, preserving anything encased within quotation marks # This will eventually allow the use of subprocess rather than os.system() # TODO Use shlex.split()? quotation_split = cmd.split('\"') if not len(quotation_split)%2: errorMessage('Malformed command \"' + cmd + '\": odd number of quotation marks') cmdsplit = [ ] if len(quotation_split) == 1: cmdsplit = cmd.split() else: for index, item in enumerate(quotation_split): if index%2: cmdsplit.append(item) else: cmdsplit.extend(item.split()) # For any MRtrix commands, need to insert the nthreads and quiet calls new_cmdsplit = [ ] is_mrtrix_binary = False next_is_binary = True for item in cmdsplit: if next_is_binary: is_mrtrix_binary = item in mrtrix_bin_list # Make sure we're not accidentally running an MRtrix command from a different installation to the script if is_mrtrix_binary: binary_sys = find_executable(item) binary_manual = os.path.join(mrtrix_bin_path, item) if (isWindows()): binary_manual = binary_manual + '.exe' use_manual_binary_path = not binary_sys if not use_manual_binary_path: # os.path.samefile() not supported on all platforms / Python versions if hasattr(os.path, 'samefile'): use_manual_binary_path = not os.path.samefile(binary_sys, binary_manual) else: # Hack equivalent of samefile(); not perfect, but should be adequate for use here use_manual_binary_path = not os.path.normcase(os.path.normpath(binary_sys)) == os.path.normcase(os.path.normpath(binary_manual)) if use_manual_binary_path: item = binary_manual next_is_binary = False if item == '|': if is_mrtrix_binary: if lib.app.mrtrixNThreads: new_cmdsplit.extend(lib.app.mrtrixNThreads.strip().split()) if lib.app.mrtrixQuiet: new_cmdsplit.append(lib.app.mrtrixQuiet.strip()) next_is_binary = True new_cmdsplit.append(item) if is_mrtrix_binary: if lib.app.mrtrixNThreads: new_cmdsplit.extend(lib.app.mrtrixNThreads.strip().split()) if lib.app.mrtrixQuiet: new_cmdsplit.append(lib.app.mrtrixQuiet.strip()) cmdsplit = new_cmdsplit # If the piping symbol appears anywhere, we need to split this into multiple commands and execute them separately # If no piping symbols, the entire command should just appear as a single row in cmdstack cmdstack = [ ] prev = 0 for index, item in enumerate(cmdsplit): if item == '|': cmdstack.append(cmdsplit[prev:index]) prev = index + 1 cmdstack.append(cmdsplit[prev:]) if lib.app.verbosity: sys.stdout.write(lib.app.colourConsole + 'Command:' + lib.app.colourClear + ' ' + cmd + '\n') sys.stdout.flush() error = False error_text = '' # TODO If script is running in verbose mode, ideally want to duplicate stderr output in the terminal if len(cmdstack) == 1: process = subprocess.Popen(cmdstack[0], stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdoutdata, stderrdata) = process.communicate() if process.returncode: error = True error_text = stdoutdata.decode('utf-8') + stderrdata.decode('utf-8') else: processes = [ ] for index, command in enumerate(cmdstack): if index > 0: proc_in = processes[index-1].stdout else: proc_in = None process = subprocess.Popen (command, stdin=proc_in, stdout=subprocess.PIPE, stderr=subprocess.PIPE) processes.append(process) # Wait for all commands to complete for index, process in enumerate(processes): if index < len(cmdstack)-1: # Only capture the output if the command failed; otherwise, let it pipe to the next command process.wait() if process.returncode: error = True (stdoutdata, stderrdata) = process.communicate() error_text = error_text + stdoutdata.decode('utf-8') + stderrdata.decode('utf-8') else: (stdoutdata, stderrdata) = process.communicate() if process.returncode: error = True error_text = error_text + stdoutdata.decode('utf-8') + stderrdata.decode('utf-8') if (error): if exitOnError: printMessage('') sys.stderr.write(os.path.basename(sys.argv[0]) + ': ' + lib.app.colourError + '[ERROR] Command failed: ' + cmd + lib.app.colourClear + '\n') sys.stderr.write(os.path.basename(sys.argv[0]) + ': ' + lib.app.colourPrint + 'Output of failed command:' + lib.app.colourClear + '\n') sys.stderr.write(error_text) if not lib.app.cleanup and lib.app.tempDir: with open(os.path.join(lib.app.tempDir, 'error.txt'), 'w') as outfile: outfile.write(cmd + '\n\n' + error_text + '\n') lib.app.complete() exit(1) else: warnMessage('Command failed: ' + cmd) # Only now do we append to the script log, since the command has completed successfully # Note: Writing the command as it was formed as the input to runCommand(): # other flags may potentially change if this file is eventually used to resume the script if lib.app.tempDir: with open(os.path.join(lib.app.tempDir, 'log.txt'), 'a') as outfile: outfile.write(cmd + '\n')
def runCommand(cmd, exitOnError=True): import lib.app, os, subprocess, sys from lib.errorMessage import errorMessage from lib.isWindows import isWindows from lib.warnMessage import warnMessage import distutils from distutils.spawn import find_executable global mrtrix_bin_list global mrtrix_bin_path if not mrtrix_bin_list: mrtrix_bin_path = os.path.abspath(os.path.join(os.path.abspath(os.path.dirname(os.path.realpath(__file__))), os.pardir, os.pardir, 'release', 'bin')); # On Windows, strip the .exe's mrtrix_bin_list = [ os.path.splitext(name)[0] for name in os.listdir(mrtrix_bin_path) ] if lib.app.lastFile: # Check to see if the last file produced is produced by this command; # if it is, this will be the last called command that gets skipped if lib.app.lastFile in cmd: lib.app.lastFile = '' if lib.app.verbosity: sys.stdout.write('Skipping command: ' + cmd + '\n') sys.stdout.flush() return # Vectorise the command string, preserving anything encased within quotation marks # This will eventually allow the use of subprocess rather than os.system() quotation_split = cmd.split('\"') if not len(quotation_split)%2: errorMessage('Malformed command \"' + cmd + '\": odd number of quotation marks') cmdsplit = [ ] if len(quotation_split) == 1: cmdsplit = cmd.split() else: for index, item in enumerate(quotation_split): if index%2: cmdsplit.append(item) else: cmdsplit.extend(item.split()) # For any MRtrix commands, need to insert the nthreads and quiet calls new_cmdsplit = [ ] is_mrtrix_binary = False next_is_binary = True for item in cmdsplit: if next_is_binary: is_mrtrix_binary = item in mrtrix_bin_list # Make sure we're not accidentally running an MRtrix command from a different installation to the script if is_mrtrix_binary: binary_sys = find_executable(item) binary_manual = os.path.join(mrtrix_bin_path, item) if (isWindows()): binary_manual = binary_manual + '.exe' use_manual_binary_path = not binary_sys if not use_manual_binary_path: # os.path.samefile() not supported on all platforms / Python versions if hasattr(os.path, 'samefile'): use_manual_binary_path = not os.path.samefile(binary_sys, binary_manual) else: # Hack equivalent of samefile(); not perfect, but should be adequate for use here use_manual_binary_path = not os.path.normcase(os.path.normpath(binary_sys)) == os.path.normcase(os.path.normpath(binary_manual)) if use_manual_binary_path: item = binary_manual next_is_binary = False if item == '|': if is_mrtrix_binary: if lib.app.mrtrixNThreads: new_cmdsplit.extend(lib.app.mrtrixNThreads.strip().split()) if lib.app.mrtrixQuiet: new_cmdsplit.append(lib.app.mrtrixQuiet.strip()) next_is_binary = True new_cmdsplit.append(item) if is_mrtrix_binary: if lib.app.mrtrixNThreads: new_cmdsplit.extend(lib.app.mrtrixNThreads.strip().split()) if lib.app.mrtrixQuiet: new_cmdsplit.append(lib.app.mrtrixQuiet.strip()) cmdsplit = new_cmdsplit # If the piping symbol appears anywhere, we need to split this into multiple commands and execute them separately # If no piping symbols, the entire command should just appear as a single row in cmdstack cmdstack = [ ] prev = 0 for index, item in enumerate(cmdsplit): if item == '|': cmdstack.append(cmdsplit[prev:index]) prev = index + 1 cmdstack.append(cmdsplit[prev:]) if lib.app.verbosity: sys.stdout.write(lib.app.colourConsole + 'Command:' + lib.app.colourClear + ' ' + cmd + '\n') sys.stdout.flush() error = False if len(cmdstack) == 1: error = subprocess.call (cmdstack[0], stdin=None, stdout=None, stderr=None) else: processes = [ ] for index, command in enumerate(cmdstack): if index > 0: proc_in = processes[index-1].stdout else: proc_in = None if index < len(cmdstack)-1: proc_out = subprocess.PIPE else: proc_out = None process = subprocess.Popen (command, stdin=proc_in, stdout=proc_out, stderr=None) processes.append(process) # Wait for all commands to complete for process in processes: process.wait() if process.returncode: error = True if (error): if exitOnError: errorMessage('Command failed: ' + cmd) else: warnMessage('Command failed: ' + cmd)
def initialise(): import os, sys from lib.errorMessage import errorMessage from lib.printMessage import printMessage from lib.readMRtrixConfSetting import readMRtrixConfSetting global args, citationList, cleanup, externalCitations, lastFile, mrtrixNThreads, mrtrixQuiet, parser, tempDir, verbosity, workingDir global colourClear, colourConsole, colourError, colourPrint, colourWarn if not parser: errorMessage( 'Script error: Command-line parser must be initialised before app') if len(sys.argv) == 1: parser.print_help() sys.exit(0) if sys.argv[-1] == '__print_usage_rst__': parser.printUsageRst() exit(0) workingDir = os.getcwd() args = parser.parse_args() if args.help: parser.print_help() sys.exit(0) use_colour = readMRtrixConfSetting('TerminalColor') if use_colour: use_colour = use_colour.lower() in ('yes', 'true', '1') else: # Windows now also gets coloured text terminal support, so make this the default use_colour = True if use_colour: colourClear = '\033[0m' colourConsole = '\033[03;34m' colourError = '\033[01;31m' colourPrint = '\033[03;32m' colourWarn = '\033[00;31m' if args.nocleanup: cleanup = False if args.nthreads: mrtrixNThreads = ' -nthreads ' + args.nthreads if args.quiet: verbosity = 0 mrtrixQuiet = ' -quiet' elif args.verbose: verbosity = 2 mrtrixQuiet = '' if citationList: printMessage('') citation_warning = 'Note that this script makes use of commands / algorithms that have relevant articles for citation' if externalCitations: citation_warning += '; INCLUDING FROM EXTERNAL SOFTWARE PACKAGES' citation_warning += '. Please consult the help page (-help option) for more information.' printMessage(citation_warning) printMessage('') if args.cont: tempDir = os.path.abspath(args.cont[0]) lastFile = args.cont[1]
def runCommand(cmd, exitOnError=True): import lib.app, os, subprocess, sys from lib.errorMessage import errorMessage from lib.isWindows import isWindows from lib.printMessage import printMessage from lib.warnMessage import warnMessage import distutils from distutils.spawn import find_executable global mrtrix_bin_list global mrtrix_bin_path if not mrtrix_bin_list: mrtrix_bin_path = os.path.abspath( os.path.join( os.path.abspath(os.path.dirname(os.path.realpath(__file__))), os.pardir, os.pardir, 'release', 'bin')) # On Windows, strip the .exe's mrtrix_bin_list = [ os.path.splitext(name)[0] for name in os.listdir(mrtrix_bin_path) ] if lib.app.lastFile: # Check to see if the last file produced is produced by this command; # if it is, this will be the last called command that gets skipped if lib.app.lastFile in cmd: lib.app.lastFile = '' if lib.app.verbosity: sys.stdout.write('Skipping command: ' + cmd + '\n') sys.stdout.flush() return # Vectorise the command string, preserving anything encased within quotation marks # This will eventually allow the use of subprocess rather than os.system() # TODO Use shlex.split()? quotation_split = cmd.split('\"') if not len(quotation_split) % 2: errorMessage('Malformed command \"' + cmd + '\": odd number of quotation marks') cmdsplit = [] if len(quotation_split) == 1: cmdsplit = cmd.split() else: for index, item in enumerate(quotation_split): if index % 2: cmdsplit.append(item) else: cmdsplit.extend(item.split()) # For any MRtrix commands, need to insert the nthreads and quiet calls new_cmdsplit = [] is_mrtrix_binary = False next_is_binary = True for item in cmdsplit: if next_is_binary: is_mrtrix_binary = item in mrtrix_bin_list # Make sure we're not accidentally running an MRtrix command from a different installation to the script if is_mrtrix_binary: binary_sys = find_executable(item) binary_manual = os.path.join(mrtrix_bin_path, item) if (isWindows()): binary_manual = binary_manual + '.exe' use_manual_binary_path = not binary_sys if not use_manual_binary_path: # os.path.samefile() not supported on all platforms / Python versions if hasattr(os.path, 'samefile'): use_manual_binary_path = not os.path.samefile( binary_sys, binary_manual) else: # Hack equivalent of samefile(); not perfect, but should be adequate for use here use_manual_binary_path = not os.path.normcase( os.path.normpath(binary_sys)) == os.path.normcase( os.path.normpath(binary_manual)) if use_manual_binary_path: item = binary_manual next_is_binary = False if item == '|': if is_mrtrix_binary: if lib.app.mrtrixNThreads: new_cmdsplit.extend(lib.app.mrtrixNThreads.strip().split()) if lib.app.mrtrixQuiet: new_cmdsplit.append(lib.app.mrtrixQuiet.strip()) next_is_binary = True new_cmdsplit.append(item) if is_mrtrix_binary: if lib.app.mrtrixNThreads: new_cmdsplit.extend(lib.app.mrtrixNThreads.strip().split()) if lib.app.mrtrixQuiet: new_cmdsplit.append(lib.app.mrtrixQuiet.strip()) cmdsplit = new_cmdsplit # If the piping symbol appears anywhere, we need to split this into multiple commands and execute them separately # If no piping symbols, the entire command should just appear as a single row in cmdstack cmdstack = [] prev = 0 for index, item in enumerate(cmdsplit): if item == '|': cmdstack.append(cmdsplit[prev:index]) prev = index + 1 cmdstack.append(cmdsplit[prev:]) if lib.app.verbosity: sys.stdout.write(lib.app.colourConsole + 'Command:' + lib.app.colourClear + ' ' + cmd + '\n') sys.stdout.flush() error = False error_text = '' # TODO If script is running in verbose mode, ideally want to duplicate stderr output in the terminal if len(cmdstack) == 1: process = subprocess.Popen(cmdstack[0], stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdoutdata, stderrdata) = process.communicate() if process.returncode: error = True error_text = stdoutdata.decode('utf-8') + stderrdata.decode( 'utf-8') else: processes = [] for index, command in enumerate(cmdstack): if index > 0: proc_in = processes[index - 1].stdout else: proc_in = None process = subprocess.Popen(command, stdin=proc_in, stdout=subprocess.PIPE, stderr=subprocess.PIPE) processes.append(process) # Wait for all commands to complete for index, process in enumerate(processes): if index < len(cmdstack) - 1: # Only capture the output if the command failed; otherwise, let it pipe to the next command process.wait() if process.returncode: error = True (stdoutdata, stderrdata) = process.communicate() error_text = error_text + stdoutdata.decode( 'utf-8') + stderrdata.decode('utf-8') else: (stdoutdata, stderrdata) = process.communicate() if process.returncode: error = True error_text = error_text + stdoutdata.decode( 'utf-8') + stderrdata.decode('utf-8') if (error): lib.app.cleanup = False if exitOnError: printMessage('') sys.stderr.write( os.path.basename(sys.argv[0]) + ': ' + lib.app.colourError + '[ERROR] Command failed: ' + cmd + lib.app.colourClear + '\n') sys.stderr.write( os.path.basename(sys.argv[0]) + ': ' + lib.app.colourPrint + 'Output of failed command:' + lib.app.colourClear + '\n') sys.stderr.write(error_text) if lib.app.tempDir: with open(os.path.join(lib.app.tempDir, 'error.txt'), 'w') as outfile: outfile.write(cmd + '\n\n' + error_text + '\n') lib.app.complete() exit(1) else: warnMessage('Command failed: ' + cmd) # Only now do we append to the script log, since the command has completed successfully # Note: Writing the command as it was formed as the input to runCommand(): # other flags may potentially change if this file is eventually used to resume the script if lib.app.tempDir: with open(os.path.join(lib.app.tempDir, 'log.txt'), 'a') as outfile: outfile.write(cmd + '\n')
def execute(): import math, os, shutil import lib.app from lib.getHeaderInfo import getHeaderInfo from lib.getImageStat import getImageStat from lib.getUserPath import getUserPath from lib.printMessage import printMessage from lib.runCommand import runCommand from lib.warnMessage import warnMessage from lib.errorMessage import errorMessage # Get b-values and number of volumes per b-value. bvalues = [ int(round(float(x))) for x in getHeaderInfo('dwi.mif', 'shells').split() ] bvolumes = [ int(x) for x in getHeaderInfo('dwi.mif', 'shellcounts').split() ] printMessage( str(len(bvalues)) + ' unique b-value(s) detected: ' + ','.join(map(str, bvalues)) + ' with ' + ','.join(map(str, bvolumes)) + ' volumes.') if len(bvalues) < 2: errorMessage('Need at least 2 unique b-values (including b=0).') # Get lmax information (if provided). sfwm_lmax = [] if lib.app.args.lmax: sfwm_lmax = [int(x.strip()) for x in lib.app.args.lmax.split(',')] if not len(sfwm_lmax) == len(bvalues): errorMessage('Number of lmax\'s (' + str(len(sfwm_lmax)) + ', as supplied to the -lmax option: ' + ','.join(map(str, sfwm_lmax)) + ') does not match number of unique b-values.') for l in sfwm_lmax: if l % 2: errorMessage( 'Values supplied to the -lmax option must be even.') if l < 0: errorMessage( 'Values supplied to the -lmax option must be non-negative.' ) # Erode (brain) mask. if lib.app.args.erode > 0: runCommand('maskfilter mask.mif erode eroded_mask.mif -npass ' + str(lib.app.args.erode)) else: runCommand('mrconvert mask.mif eroded_mask.mif -datatype bit') # Get volumes, compute mean signal and SDM per b-value; compute overall SDM; get rid of erroneous values. totvolumes = 0 fullsdmcmd = 'mrcalc' errcmd = 'mrcalc' zeropath = 'mean_b' + str(bvalues[0]) + '.mif' for i, b in enumerate(bvalues): dwipath = 'dwi_b' + str(b) + '.mif' runCommand('dwiextract dwi.mif -shell ' + str(b) + ' ' + dwipath) meanpath = 'mean_b' + str(b) + '.mif' runCommand('mrmath ' + dwipath + ' mean ' + meanpath + ' -axis 3') errpath = 'err_b' + str(b) + '.mif' runCommand('mrcalc ' + meanpath + ' -finite ' + meanpath + ' 0 -if 0 -le ' + errpath + ' -datatype bit') errcmd += ' ' + errpath if i > 0: errcmd += ' -add' sdmpath = 'sdm_b' + str(b) + '.mif' runCommand('mrcalc ' + zeropath + ' ' + meanpath + ' -divide -log ' + sdmpath) totvolumes += bvolumes[i] fullsdmcmd += ' ' + sdmpath + ' ' + str(bvolumes[i]) + ' -mult' if i > 1: fullsdmcmd += ' -add' fullsdmcmd += ' ' + str(totvolumes) + ' -divide full_sdm.mif' runCommand(fullsdmcmd) runCommand( 'mrcalc full_sdm.mif -finite full_sdm.mif 0 -if 0 -le err_sdm.mif -datatype bit' ) errcmd += ' err_sdm.mif -add 0 eroded_mask.mif -if safe_mask.mif -datatype bit' runCommand(errcmd) runCommand('mrcalc safe_mask.mif full_sdm.mif 0 -if 10 -min safe_sdm.mif') # Compute FA and principal eigenvectors; crude WM versus GM-CSF separation based on FA. runCommand( 'dwi2tensor dwi.mif - -mask safe_mask.mif | tensor2metric - -fa safe_fa.mif -vector safe_vecs.mif -modulate none -mask safe_mask.mif' ) runCommand('mrcalc safe_mask.mif safe_fa.mif 0 -if ' + str(lib.app.args.fa) + ' -gt crude_wm.mif -datatype bit') runCommand( 'mrcalc crude_wm.mif 0 safe_mask.mif -if _crudenonwm.mif -datatype bit' ) # Crude GM versus CSF separation based on SDM. crudenonwmmedian = getImageStat('safe_sdm.mif', 'median', '_crudenonwm.mif') runCommand( 'mrcalc _crudenonwm.mif safe_sdm.mif ' + str(crudenonwmmedian) + ' -subtract 0 -if - | mrthreshold - - -mask _crudenonwm.mif | mrcalc _crudenonwm.mif - 0 -if crude_csf.mif -datatype bit' ) runCommand( 'mrcalc crude_csf.mif 0 _crudenonwm.mif -if crude_gm.mif -datatype bit' ) # Refine WM: remove high SDM outliers. crudewmmedian = getImageStat('safe_sdm.mif', 'median', 'crude_wm.mif') runCommand('mrcalc crude_wm.mif safe_sdm.mif 0 -if ' + str(crudewmmedian) + ' -gt _crudewmhigh.mif -datatype bit') runCommand( 'mrcalc _crudewmhigh.mif 0 crude_wm.mif -if _crudewmlow.mif -datatype bit' ) crudewmQ1 = float(getImageStat('safe_sdm.mif', 'median', '_crudewmlow.mif')) crudewmQ3 = float( getImageStat('safe_sdm.mif', 'median', '_crudewmhigh.mif')) crudewmoutlthresh = crudewmQ3 + (crudewmQ3 - crudewmQ1) runCommand('mrcalc crude_wm.mif safe_sdm.mif 0 -if ' + str(crudewmoutlthresh) + ' -gt _crudewmoutliers.mif -datatype bit') runCommand( 'mrcalc _crudewmoutliers.mif 0 crude_wm.mif -if refined_wm.mif -datatype bit' ) # Refine GM: separate safer GM from partial volumed voxels. crudegmmedian = getImageStat('safe_sdm.mif', 'median', 'crude_gm.mif') runCommand('mrcalc crude_gm.mif safe_sdm.mif 0 -if ' + str(crudegmmedian) + ' -gt _crudegmhigh.mif -datatype bit') runCommand( 'mrcalc _crudegmhigh.mif 0 crude_gm.mif -if _crudegmlow.mif -datatype bit' ) runCommand( 'mrcalc _crudegmhigh.mif safe_sdm.mif ' + str(crudegmmedian) + ' -subtract 0 -if - | mrthreshold - - -mask _crudegmhigh.mif -invert | mrcalc _crudegmhigh.mif - 0 -if _crudegmhighselect.mif -datatype bit' ) runCommand( 'mrcalc _crudegmlow.mif safe_sdm.mif ' + str(crudegmmedian) + ' -subtract -neg 0 -if - | mrthreshold - - -mask _crudegmlow.mif -invert | mrcalc _crudegmlow.mif - 0 -if _crudegmlowselect.mif -datatype bit' ) runCommand( 'mrcalc _crudegmhighselect.mif 1 _crudegmlowselect.mif -if refined_gm.mif -datatype bit' ) # Refine CSF: recover lost CSF from crude WM SDM outliers, separate safer CSF from partial volumed voxels. crudecsfmin = getImageStat('safe_sdm.mif', 'min', 'crude_csf.mif') runCommand('mrcalc _crudewmoutliers.mif safe_sdm.mif 0 -if ' + str(crudecsfmin) + ' -gt 1 crude_csf.mif -if _crudecsfextra.mif -datatype bit') runCommand( 'mrcalc _crudecsfextra.mif safe_sdm.mif ' + str(crudecsfmin) + ' -subtract 0 -if - | mrthreshold - - -mask _crudecsfextra.mif | mrcalc _crudecsfextra.mif - 0 -if refined_csf.mif -datatype bit' ) # Get final voxels for single-fibre WM response function estimation from WM using 'tournier' algorithm. refwmcount = float( getImageStat('refined_wm.mif', 'count', 'refined_wm.mif')) voxsfwmcount = int(round(refwmcount * lib.app.args.sfwm / 100.0)) printMessage('Running \'tournier\' algorithm to select ' + str(voxsfwmcount) + ' single-fibre WM voxels.') cleanopt = '' if not lib.app.cleanup: cleanopt = ' -nocleanup' runCommand( 'dwi2response tournier dwi.mif _respsfwmss.txt -sf_voxels ' + str(voxsfwmcount) + ' -iter_voxels ' + str(voxsfwmcount * 10) + ' -mask refined_wm.mif -voxels voxels_sfwm.mif -quiet -tempdir ' + lib.app.tempDir + cleanopt) # Get final voxels for GM response function estimation from GM. refgmmedian = getImageStat('safe_sdm.mif', 'median', 'refined_gm.mif') runCommand('mrcalc refined_gm.mif safe_sdm.mif 0 -if ' + str(refgmmedian) + ' -gt _refinedgmhigh.mif -datatype bit') runCommand( 'mrcalc _refinedgmhigh.mif 0 refined_gm.mif -if _refinedgmlow.mif -datatype bit' ) refgmhighcount = float( getImageStat('_refinedgmhigh.mif', 'count', '_refinedgmhigh.mif')) refgmlowcount = float( getImageStat('_refinedgmlow.mif', 'count', '_refinedgmlow.mif')) voxgmhighcount = int(round(refgmhighcount * lib.app.args.gm / 100.0)) voxgmlowcount = int(round(refgmlowcount * lib.app.args.gm / 100.0)) runCommand( 'mrcalc _refinedgmhigh.mif safe_sdm.mif 0 -if - | mrthreshold - - -bottom ' + str(voxgmhighcount) + ' -ignorezero | mrcalc _refinedgmhigh.mif - 0 -if _refinedgmhighselect.mif -datatype bit' ) runCommand( 'mrcalc _refinedgmlow.mif safe_sdm.mif 0 -if - | mrthreshold - - -top ' + str(voxgmlowcount) + ' -ignorezero | mrcalc _refinedgmlow.mif - 0 -if _refinedgmlowselect.mif -datatype bit' ) runCommand( 'mrcalc _refinedgmhighselect.mif 1 _refinedgmlowselect.mif -if voxels_gm.mif -datatype bit' ) # Get final voxels for CSF response function estimation from CSF. refcsfcount = float( getImageStat('refined_csf.mif', 'count', 'refined_csf.mif')) voxcsfcount = int(round(refcsfcount * lib.app.args.csf / 100.0)) runCommand( 'mrcalc refined_csf.mif safe_sdm.mif 0 -if - | mrthreshold - - -top ' + str(voxcsfcount) + ' -ignorezero | mrcalc refined_csf.mif - 0 -if voxels_csf.mif -datatype bit' ) # Show summary of voxels counts. textarrow = ' --> ' printMessage('Summary of voxel counts:') printMessage( 'Mask: ' + str(int(getImageStat('mask.mif', 'count', 'mask.mif'))) + textarrow + str(int(getImageStat('eroded_mask.mif', 'count', 'eroded_mask.mif'))) + textarrow + str(int(getImageStat('safe_mask.mif', 'count', 'safe_mask.mif')))) printMessage( 'WM: ' + str(int(getImageStat('crude_wm.mif', 'count', 'crude_wm.mif'))) + textarrow + str(int(getImageStat('refined_wm.mif', 'count', 'refined_wm.mif'))) + textarrow + str(int(getImageStat('voxels_sfwm.mif', 'count', 'voxels_sfwm.mif'))) + ' (SF)') printMessage( 'GM: ' + str(int(getImageStat('crude_gm.mif', 'count', 'crude_gm.mif'))) + textarrow + str(int(getImageStat('refined_gm.mif', 'count', 'refined_gm.mif'))) + textarrow + str(int(getImageStat('voxels_gm.mif', 'count', 'voxels_gm.mif')))) printMessage( 'CSF: ' + str(int(getImageStat('crude_csf.mif', 'count', 'crude_csf.mif'))) + textarrow + str(int(getImageStat('refined_csf.mif', 'count', 'refined_csf.mif'))) + textarrow + str(int(getImageStat('voxels_csf.mif', 'count', 'voxels_csf.mif')))) # Generate single-fibre WM, GM and CSF responses sfwm_responses = [] gm_responses = [] csf_responses = [] max_length = 0 for index, b in enumerate(bvalues): dwipath = 'dwi_b' + str(b) + '.mif' this_b_lmax_option = '' if sfwm_lmax: this_b_lmax_option = ' -lmax ' + str(sfwm_lmax[index]) runCommand( 'amp2sh ' + dwipath + ' - | sh2response - voxels_sfwm.mif safe_vecs.mif _respsfwmb' + str(b) + '.txt' + this_b_lmax_option) sfwm_response = open('_respsfwmb' + str(b) + '.txt', 'r').read().split() sfwm_responses.append(sfwm_response) max_length = max(max_length, len(sfwm_response)) meanpath = 'mean_b' + str(b) + '.mif' gm_mean = float(getImageStat(meanpath, 'mean', 'voxels_gm.mif')) csf_mean = float(getImageStat(meanpath, 'mean', 'voxels_csf.mif')) gm_responses.append(str(gm_mean * math.sqrt(4.0 * math.pi))) csf_responses.append(str(csf_mean * math.sqrt(4.0 * math.pi))) with open('response_sfwm.txt', 'w') as f: for line in sfwm_responses: line += ['0'] * (max_length - len(line)) f.write(' '.join(line) + '\n') with open('response_gm.txt', 'w') as f: for line in gm_responses: f.write(line + '\n') with open('response_csf.txt', 'w') as f: for line in csf_responses: f.write(line + '\n') shutil.copyfile('response_sfwm.txt', getUserPath(lib.app.args.out_sfwm, False)) shutil.copyfile('response_gm.txt', getUserPath(lib.app.args.out_gm, False)) shutil.copyfile('response_csf.txt', getUserPath(lib.app.args.out_csf, False)) # Generate 4D binary images with voxel selection at major stages in algorithm (RGB as in MSMT-CSD paper). runCommand( 'mrcat crude_csf.mif crude_gm.mif crude_wm.mif crude.mif -axis 3') runCommand( 'mrcat refined_csf.mif refined_gm.mif refined_wm.mif refined.mif -axis 3' ) runCommand( 'mrcat voxels_csf.mif voxels_gm.mif voxels_sfwm.mif voxels.mif -axis 3' )
def execute(): import math, os, shutil import lib.app from lib.getHeaderInfo import getHeaderInfo from lib.getImageStat import getImageStat from lib.getUserPath import getUserPath from lib.printMessage import printMessage from lib.runCommand import runCommand from lib.warnMessage import warnMessage from lib.errorMessage import errorMessage # Ideally want to use the oversampling-based regridding of the 5TT image from the SIFT model, not mrtransform # May need to commit 5ttregrid... # Verify input 5tt image sizes = [int(x) for x in getHeaderInfo('5tt.mif', 'size').split()] datatype = getHeaderInfo('5tt.mif', 'datatype') if not len(sizes) == 4 or not sizes[3] == 5 or not datatype.startswith( 'Float'): errorMessage('Imported anatomical image ' + os.path.basename(lib.app.args.in_5tt) + ' is not in the 5TT format') # Get shell information shells = [ int(round(float(x))) for x in getHeaderInfo('dwi.mif', 'shells').split() ] if len(shells) < 3: warnMessage( 'Less than three b-value shells; response functions will not be applicable in MSMT-CSD algorithm' ) # Get lmax information (if provided) wm_lmax = [] if lib.app.args.lmax: wm_lmax = [int(x.strip()) for x in lib.app.args.lmax.split(',')] if not len(wm_lmax) == len(shells): errorMessage('Number of manually-defined lmax\'s (' + str(len(wm_lmax)) + ') does not match number of b-value shells (' + str(len(shells)) + ')') for l in wm_lmax: if l % 2: errorMessage('Values for lmax must be even') if l < 0: errorMessage('Values for lmax must be non-negative') runCommand( 'dwi2tensor dwi.mif - -mask mask.mif | tensor2metric - -fa fa.mif -vector vector.mif' ) if not os.path.exists('dirs.mif'): shutil.copy('vector.mif', 'dirs.mif') runCommand( 'mrtransform 5tt.mif 5tt_regrid.mif -template fa.mif -interp linear') # Basic tissue masks runCommand( 'mrconvert 5tt_regrid.mif - -coord 3 2 -axes 0,1,2 | mrcalc - ' + str(lib.app.args.pvf) + ' -gt mask.mif -mult wm_mask.mif') runCommand( 'mrconvert 5tt_regrid.mif - -coord 3 0 -axes 0,1,2 | mrcalc - ' + str(lib.app.args.pvf) + ' -gt fa.mif ' + str(lib.app.args.fa) + ' -lt -mult mask.mif -mult gm_mask.mif') runCommand( 'mrconvert 5tt_regrid.mif - -coord 3 3 -axes 0,1,2 | mrcalc - ' + str(lib.app.args.pvf) + ' -gt fa.mif ' + str(lib.app.args.fa) + ' -lt -mult mask.mif -mult csf_mask.mif') # Revise WM mask to only include single-fibre voxels printMessage( 'Calling dwi2response recursively to select WM single-fibre voxels using \'' + lib.app.args.wm_algo + '\' algorithm') recursive_cleanup_option = '' if not lib.app.cleanup: recursive_cleanup_option = ' -nocleanup' runCommand( 'dwi2response -quiet -tempdir ' + lib.app.tempDir + recursive_cleanup_option + ' ' + lib.app.args.wm_algo + ' dwi.mif wm_ss_response.txt -mask wm_mask.mif -voxels wm_sf_mask.mif') # Check for empty masks wm_voxels = int(getImageStat('wm_sf_mask.mif', 'count', 'wm_sf_mask.mif')) gm_voxels = int(getImageStat('gm_mask.mif', 'count', 'gm_mask.mif')) csf_voxels = int(getImageStat('csf_mask.mif', 'count', 'csf_mask.mif')) empty_masks = [] if not wm_voxels: empty_masks.append('WM') if not gm_voxels: empty_masks.append('GM') if not csf_voxels: empty_masks.append('CSF') if empty_masks: message = ','.join(empty_masks) message += ' tissue mask' if len(empty_masks) > 1: message += 's' message += ' empty; cannot estimate response function' if len(empty_masks) > 1: message += 's' errorMessage(message) # For each of the three tissues, generate a multi-shell response # Since here we're guaranteeing that GM and CSF will be isotropic in all shells, let's use mrstats rather than sh2response (seems a bit weird passing a directions file to sh2response with lmax=0...) wm_responses = [] gm_responses = [] csf_responses = [] max_length = 0 for index, b in enumerate(shells): dwi_path = 'dwi_b' + str(b) + '.mif' # dwiextract will yield a 4D image, even if there's only a single volume in a shell runCommand('dwiextract dwi.mif -shell ' + str(b) + ' ' + dwi_path) this_b_lmax_option = '' if wm_lmax: this_b_lmax_option = ' -lmax ' + str(wm_lmax[index]) runCommand('amp2sh ' + dwi_path + ' - | sh2response - wm_sf_mask.mif dirs.mif wm_response_b' + str(b) + '.txt' + this_b_lmax_option) wm_response = open('wm_response_b' + str(b) + '.txt', 'r').read().split() wm_responses.append(wm_response) max_length = max(max_length, len(wm_response)) mean_dwi_path = 'dwi_b' + str(b) + '_mean.mif' runCommand('mrmath ' + dwi_path + ' mean ' + mean_dwi_path + ' -axis 3') gm_mean = float(getImageStat(mean_dwi_path, 'mean', 'gm_mask.mif')) csf_mean = float(getImageStat(mean_dwi_path, 'mean', 'csf_mask.mif')) gm_responses.append(str(gm_mean * math.sqrt(4.0 * math.pi))) csf_responses.append(str(csf_mean * math.sqrt(4.0 * math.pi))) with open('wm.txt', 'w') as f: for line in wm_responses: line += ['0'] * (max_length - len(line)) f.write(' '.join(line) + '\n') with open('gm.txt', 'w') as f: for line in gm_responses: f.write(line + '\n') with open('csf.txt', 'w') as f: for line in csf_responses: f.write(line + '\n') shutil.copyfile('wm.txt', getUserPath(lib.app.args.out_wm, False)) shutil.copyfile('gm.txt', getUserPath(lib.app.args.out_gm, False)) shutil.copyfile('csf.txt', getUserPath(lib.app.args.out_csf, False)) # Generate output 4D binary image with voxel selections; RGB as in MSMT-CSD paper runCommand( 'mrcat csf_mask.mif gm_mask.mif wm_sf_mask.mif voxels.mif -axis 3')
def execute(): import os import lib.app from lib.binaryInPath import binaryInPath from lib.errorMessage import errorMessage from lib.getFSLSuffix import getFSLSuffix from lib.getHeaderInfo import getHeaderInfo from lib.imagesMatch import imagesMatch from lib.isWindows import isWindows from lib.runCommand import runCommand from lib.warnMessage import warnMessage if isWindows(): errorMessage('\'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: errorMessage('Environment variable FSLDIR is not set; please run appropriate FSL configuration script') ssroi_cmd = 'standard_space_roi' if not binaryInPath(ssroi_cmd): ssroi_cmd = 'fsl5.0-standard_space_roi' if not binaryInPath(ssroi_cmd): errorMessage('Could not find FSL program standard_space_roi; please verify FSL install') bet_cmd = 'bet' if not binaryInPath(bet_cmd): bet_cmd = 'fsl5.0-bet' if not binaryInPath(bet_cmd): errorMessage('Could not find FSL program bet; please verify FSL install') fast_cmd = 'fast' if not binaryInPath(fast_cmd): fast_cmd = 'fsl5.0-fast' if not binaryInPath(fast_cmd): errorMessage('Could not find FSL program fast; please verify FSL install') first_cmd = 'run_first_all' if not binaryInPath(first_cmd): first_cmd = "fsl5.0-run_first_all" if not binaryInPath(first_cmd): errorMessage('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): errorMessage('Atlases required for FSL\'s FIRST program not installed;\nPlease install fsl-first-data using your relevant package manager') fsl_suffix = getFSLSuffix() sgm_structures = [ 'L_Accu', 'R_Accu', 'L_Caud', 'R_Caud', 'L_Pall', 'R_Pall', 'L_Puta', 'R_Puta', 'L_Thal', 'R_Thal' ] if lib.app.args.sgm_amyg_hipp: sgm_structures.extend([ 'L_Amyg', 'R_Amyg', 'L_Hipp', 'R_Hipp' ]) runCommand('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 imagesMatch('T1.nii', 'mask.mif'): runCommand('mrcalc T1.nii mask.mif -mult ' + fast_t1_input) mask_path = 'mask.mif' else: warnMessage('Mask image does not match input image - re-gridding') runCommand('mrtransform mask.mif mask_regrid.mif -template T1.nii') runCommand('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 runCommand('mrcalc T2.nii ' + mask_path + ' -mult ' + fast_t2_input) elif lib.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; # standard_space_roi can sometimes crash; if this happens, try to allow the script to continue if mni_mask_dilation: runCommand('maskfilter ' + mni_mask_path + ' dilate mni_mask.nii -npass ' + str(mni_mask_dilation)) if lib.app.args.nocrop: ssroi_roi_option = ' -roiNONE' else: ssroi_roi_option = ' -roiFOV' runCommand(ssroi_cmd + ' T1.nii T1_preBET' + fsl_suffix + ' -maskMASK mni_mask.nii' + ssroi_roi_option, False) else: runCommand(ssroi_cmd + ' T1.nii T1_preBET' + fsl_suffix + ' -b', False) if not os.path.exists('T1_preBET' + fsl_suffix): warnMessage('FSL command ' + ssroi_cmd + ' appears to have failed; passing T1 directly to BET') runCommand('mrconvert input.mif T1_preBET' + fsl_suffix + ' -stride -1,+2,+3') # BET fast_t1_input = 'T1_BET' + fsl_suffix runCommand(bet_cmd + ' T1_preBET' + fsl_suffix + ' ' + fast_t1_input + ' -f 0.15 -R') if os.path.exists('T2.nii'): if lib.app.args.nocrop: fast_t2_input = 'T2.nii' else: # Just a reduction of FoV, no sub-voxel interpolation going on runCommand('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: runCommand(fast_cmd + ' -S 2 ' + fast_t2_input + ' ' + fast_t1_input) else: runCommand(fast_cmd + ' ' + fast_t1_input) fast_output_prefix = fast_t1_input.split('.')[0] # FIRST first_input_is_brain_extracted = '' if lib.app.args.premasked: first_input_is_brain_extracted = ' -b' runCommand(first_cmd + ' -s ' + ','.join(sgm_structures) + ' -i T1.nii -o first' + first_input_is_brain_extracted) # 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' if not os.path.exists(vtk_in_path): errorMessage('Missing .vtk file for structure ' + struct + '; run_first_all must have failed') runCommand('meshconvert ' + vtk_in_path + ' ' + vtk_temp_path + ' -transform_first2real T1.nii') runCommand('mesh2pve ' + vtk_temp_path + ' ' + fast_t1_input + ' ' + pve_image_path) pve_image_list.append(pve_image_path) pve_cat = ' '.join(pve_image_list) runCommand('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 runCommand('mrthreshold ' + fast_output_prefix + '_pve_2' + fast_suffix + ' - -abs 0.001 | maskfilter - connect wm_mask.mif -largest') # Step 2: Generate the images in the same fashion as the 5ttgen command runCommand('mrconvert ' + fast_output_prefix + '_pve_0' + fast_suffix + ' csf.mif') runCommand('mrcalc 1.0 csf.mif -sub all_sgms.mif -min sgm.mif') runCommand('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') runCommand('mrcalc multiplier.mif -finite multiplier.mif 0.0 -if multiplier_noNAN.mif') runCommand('mrcalc ' + fast_output_prefix + '_pve_1' + fast_suffix + ' multiplier_noNAN.mif -mult cgm.mif') runCommand('mrcalc ' + fast_output_prefix + '_pve_2' + fast_suffix + ' multiplier_noNAN.mif -mult wm_mask.mif -mult wm.mif') runCommand('mrcalc 0 wm.mif -min path.mif') runCommand('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 lib.app.args.nocrop: runCommand('mrconvert combined_precrop.mif result.mif') else: runCommand('mrmath combined_precrop.mif sum - -axis 3 | mrthreshold - - -abs 0.5 | mrcrop combined_precrop.mif result.mif -mask -')
def execute(): import os import lib.app from lib.binaryInPath import binaryInPath from lib.errorMessage import errorMessage from lib.getFSLSuffix import getFSLSuffix from lib.getHeaderInfo import getHeaderInfo from lib.isWindows import isWindows from lib.runCommand import runCommand from lib.warnMessage import warnMessage if isWindows(): errorMessage('Script cannot run in FSL mode on Windows') fsl_path = os.environ.get('FSLDIR', '') if not fsl_path: errorMessage('Environment variable FSLDIR is not set; please run appropriate FSL configuration script') ssroi_cmd = 'standard_space_roi' if not binaryInPath(ssroi_cmd): ssroi_cmd = 'fsl5.0-standard_space_roi' if not binaryInPath(ssroi_cmd): errorMessage('Could not find FSL program standard_space_roi; please verify FSL install') bet_cmd = 'bet' if not binaryInPath(bet_cmd): bet_cmd = 'fsl5.0-bet' if not binaryInPath(bet_cmd): errorMessage('Could not find FSL program bet; please verify FSL install') fast_cmd = 'fast' if not binaryInPath(fast_cmd): fast_cmd = 'fsl5.0-fast' if not binaryInPath(fast_cmd): errorMessage('Could not find FSL program fast; please verify FSL install') first_cmd = 'run_first_all' if not binaryInPath(first_cmd): first_cmd = "fsl5.0-run_first_all" if not binaryInPath(first_cmd): errorMessage('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): errorMessage('Atlases required for FSL\'s FIRST program not installed;\nPlease install fsl-first-data using your relevant package manager') fsl_suffix = getFSLSuffix() sgm_structures = [ 'L_Accu', 'R_Accu', 'L_Caud', 'R_Caud', 'L_Pall', 'R_Pall', 'L_Puta', 'R_Puta', 'L_Thal', 'R_Thal' ] runCommand('mrconvert input.mif T1.nii') # Decide whether or not we're going to do any brain masking if os.path.exists('mask.mif'): # Check to see if the dimensions match the T1 image T1_size = getHeaderInfo('input.mif', 'size') mask_size = getHeaderInfo('mask.mif', 'size') if mask_size == T1_size: runCommand('mrcalc input.mif mask.mif -mult T1_masked.nii') else: runCommand('mrtransform mask.mif mask_regrid.mif -template input.mif') runCommand('mrcalc input.mif mask_regrid.mif -mult T1_masked.nii') elif lib.app.args.premasked: runCommand('mrconvert input.mif T1_masked.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; # standard_space_roi can sometimes crash; if this happens, try to allow the script to continue if mni_mask_dilation: runCommand('maskfilter ' + mni_mask_path + ' dilate mni_mask.nii -npass ' + str(mni_mask_dilation)) if lib.app.args.nocrop: ssroi_roi_option = ' -roiNONE' else: ssroi_roi_option = ' -roiFOV' runCommand(ssroi_cmd + ' T1.nii T1_preBET' + fsl_suffix + ' -maskMASK mni_mask.nii' + ssroi_roi_option, False) else: runCommand(ssroi_cmd + ' T1.nii T1_preBET' + fsl_suffix + ' -b', False) if not os.path.isfile('T1_preBET' + fsl_suffix): warnMessage('FSL command ' + ssroi_cmd + ' appears to have failed; passing T1 directly to BET') runCommand('mrconvert input.mif T1_preBET' + fsl_suffix) # BET runCommand(bet_cmd + ' T1_preBET' + fsl_suffix + ' T1_masked' + fsl_suffix + ' -f 0.15 -R') # Finish branching based on brain masking # FAST runCommand(fast_cmd + ' T1_masked' + fsl_suffix) # FIRST first_input_is_brain_extracted = '' if lib.app.args.premasked: first_input_is_brain_extracted = ' -b' runCommand(first_cmd + ' -s ' + ','.join(sgm_structures) + ' -i T1.nii -o first' + first_input_is_brain_extracted) # Convert FIRST meshes to partial volume images pve_image_list = [ ] for struct in sgm_structures: pve_image_path = 'mesh2pve_' + struct + '.nii' vtk_in_path = 'first-' + struct + '_first.vtk' vtk_temp_path = struct + '.vtk' if not os.path.exists(vtk_in_path): errorMessage('Missing .vtk file for structure ' + struct + '; run_first_all must have failed') runCommand('meshconvert ' + vtk_in_path + ' ' + vtk_temp_path + ' -transform_first2real input.mif') runCommand('mesh2pve ' + vtk_temp_path + ' T1_preBET' + fsl_suffix + ' ' + pve_image_path) pve_image_list.append(pve_image_path) pve_cat = ' '.join(pve_image_list) runCommand('mrmath ' + pve_cat + ' sum - | mrcalc - 1.0 -min all_sgms.nii') # 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 runCommand('mrthreshold T1_masked_pve_2' + fast_suffix + ' - -abs 0.001 | maskfilter - connect wm_mask.mif -largest') # Step 2: Generate the images in the same fashion as the 5ttgen command runCommand('mrconvert T1_masked_pve_0' + fast_suffix + ' csf.mif') runCommand('mrcalc 1 csf.mif -sub all_sgms.nii -min sgm.mif') runCommand('mrcalc 1.0 csf.mif sgm.mif -add -sub T1_masked_pve_1' + fast_suffix + ' T1_masked_pve_2' + fast_suffix + ' -add -div multiplier.mif') runCommand('mrcalc multiplier.mif -finite multiplier.mif 0.0 -if multiplier_noNAN.mif') runCommand('mrcalc T1_masked_pve_1' + fast_suffix + ' multiplier_noNAN.mif -mult cgm.mif') runCommand('mrcalc T1_masked_pve_2' + fast_suffix + ' multiplier_noNAN.mif -mult wm_mask.mif -mult wm.mif') runCommand('mrcalc 0 wm.mif -min path.mif') runCommand('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 lib.app.args.nocrop: runCommand('mrconvert combined_precrop.mif result.mif') else: runCommand('mrmath combined_precrop.mif sum - -axis 3 | mrthreshold - - -abs 0.5 | mrcrop combined_precrop.mif result.mif -mask -')
def initialise(): import os, sys from lib.errorMessage import errorMessage from lib.printMessage import printMessage from lib.readMRtrixConfSetting import readMRtrixConfSetting global args, citationList, cleanup, externalCitations, lastFile, mrtrixNThreads, mrtrixQuiet, parser, tempDir, verbosity, workingDir global colourClear, colourConsole, colourError, colourPrint, colourWarn if not parser: errorMessage('Script error: Command-line parser must be initialised before app') if len(sys.argv) == 1: parser.print_help() sys.exit(0) if sys.argv[-1] == '__print_usage_rst__': parser.printUsageRst() exit(0) workingDir = os.getcwd() args = parser.parse_args() if args.help: parser.print_help() sys.exit(0) use_colour = readMRtrixConfSetting('TerminalColor') if use_colour: use_colour = use_colour.lower() in ('yes', 'true', '1') else: # Windows now also gets coloured text terminal support, so make this the default use_colour = True if use_colour: colourClear = '\033[0m' colourConsole = '\033[03;34m' colourError = '\033[01;31m' colourPrint = '\033[03;32m' colourWarn = '\033[00;31m' if args.nocleanup: cleanup = False if args.nthreads: mrtrixNThreads = ' -nthreads ' + args.nthreads if args.quiet: verbosity = 0 mrtrixQuiet = ' -quiet' elif args.verbose: verbosity = 2 mrtrixQuiet = '' if citationList: printMessage('') citation_warning = 'Note that this script makes use of commands / algorithms that have relevant articles for citation' if externalCitations: citation_warning += '; INCLUDING FROM EXTERNAL SOFTWARE PACKAGES' citation_warning += '. Please consult the help page (-help option) for more information.' printMessage(citation_warning) printMessage('') if args.cont: tempDir = os.path.abspath(args.cont[0]) lastFile = args.cont[1]