Пример #1
0
def getInputFiles():
  import os
  import lib.app
  from lib.runCommand import runCommand
  runCommand('mrconvert ' + lib.app.args.in_5tt + ' ' + os.path.join(lib.app.tempDir, '5tt.mif'))
  if lib.app.args.dirs:
    runCommand('mrconvert ' + lib.app.args.dirs + ' ' + os.path.join(lib.app.tempDir, 'dirs.mif') + ' -stride 0,0,0,1')
Пример #2
0
def execute():
  import os, shutil
  import lib.app
  from lib.errorMessage  import errorMessage
  from lib.getHeaderInfo import getHeaderInfo
  from lib.runCommand    import runCommand
  
  lmax_option = ''
  if lib.app.args.lmax:
    lmax_option = ' -lmax ' + lib.app.args.lmax
  
  shells = [ int(round(float(x))) for x in getHeaderInfo('dwi.mif', 'shells').split() ]

  # 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):
    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', os.path.join(lib.app.workingDir, lib.app.args.output))
  shutil.copyfile('in_voxels.mif', 'voxels.mif')
Пример #3
0
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')
Пример #4
0
def getInputFiles():
    import os
    import lib.app
    from lib.runCommand import runCommand
    runCommand('mrconvert ' + lib.app.args.in_5tt + ' ' +
               os.path.join(lib.app.tempDir, '5tt.mif'))
    if lib.app.args.dirs:
        runCommand('mrconvert ' + lib.app.args.dirs + ' ' +
                   os.path.join(lib.app.tempDir, 'dirs.mif') +
                   ' -stride 0,0,0,1')
Пример #5
0
def getInputFiles():
  import os
  import lib.app
  from lib.runCommand  import runCommand
  from lib.warnMessage import warnMessage
  mask_path = os.path.join(lib.app.tempDir, 'mask.mif')
  if os.path.exists(mask_path):
    warnMessage('-mask option is ignored by algorithm \'manual\'')
    os.remove(mask_path)
  runCommand('mrconvert ' + lib.app.args.in_voxels + ' ' + os.path.join(lib.app.tempDir, 'in_voxels.mif'))
  if lib.app.args.dirs:
    runCommand('mrconvert ' + lib.app.args.dirs + ' ' + os.path.join(lib.app.tempDir, 'dirs.mif') + ' -stride 0,0,0,1')
Пример #6
0
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')
Пример #7
0
def getInputFiles():
    import os
    import lib.app
    from lib.runCommand import runCommand
    from lib.warnMessage import warnMessage
    mask_path = os.path.join(lib.app.tempDir, 'mask.mif')
    if os.path.exists(mask_path):
        warnMessage('-mask option is ignored by algorithm \'manual\'')
        os.remove(mask_path)
    runCommand('mrconvert ' + lib.app.args.in_voxels + ' ' +
               os.path.join(lib.app.tempDir, 'in_voxels.mif'))
    if lib.app.args.dirs:
        runCommand('mrconvert ' + lib.app.args.dirs + ' ' +
                   os.path.join(lib.app.tempDir, 'dirs.mif') +
                   ' -stride 0,0,0,1')
Пример #8
0
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')
Пример #9
0
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')
Пример #10
0
def execute():
    import os, shutil
    import lib.app
    from lib.errorMessage import errorMessage
    from lib.getHeaderInfo import getHeaderInfo
    from lib.runCommand import runCommand

    lmax_option = ''
    if lib.app.args.lmax:
        lmax_option = ' -lmax ' + lib.app.args.lmax

    shells = [
        int(round(float(x)))
        for x in getHeaderInfo('dwi.mif', 'shells').split()
    ]

    # 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):
        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',
                    os.path.join(lib.app.workingDir, lib.app.args.output))
    shutil.copyfile('in_voxels.mif', 'voxels.mif')
Пример #11
0
def execute():
  import os, shutil
  import lib.app
  from lib.delFile      import delFile
  from lib.getImageStat import getImageStat
  from lib.getUserPath  import getUserPath
  from lib.printMessage import printMessage
  from lib.runCommand   import runCommand
  
  lmax_option = ''
  if lib.app.args.lmax:
    lmax_option = ' -lmax ' + lib.app.args.lmax

  if lib.app.args.max_iters < 2:
    errorMessage('Number of iterations must be at least 2')

  runCommand('amp2sh dwi.mif dwiSH.mif' + lmax_option)

  for iteration in range(0, lib.app.args.max_iters):
    prefix = 'iter' + str(iteration) + '_'
  
    if iteration == 0:
      RF_in_path = 'init_RF.txt'
      mask_in_path = 'mask.mif'
      init_RF = '1 -1 1'
      with open(RF_in_path, 'w') as f:
        f.write(init_RF);
      iter_lmax_option = ' -lmax 4'
    else:
      RF_in_path = 'iter' + str(iteration-1) + '_RF.txt'
      mask_in_path = 'iter' + str(iteration-1) + '_SF_dilated.mif'
      iter_lmax_option = lmax_option

    # Run CSD
    runCommand('dwi2fod csd dwi.mif ' + RF_in_path + ' ' + prefix + 'FOD.mif -mask ' + mask_in_path + iter_lmax_option)
    # Get amplitudes of two largest peaks, and direction of largest
    # TODO Speed-test fod2fixel against sh2peaks
    # TODO Add maximum number of fixels per voxel option to fod2fixel?
    runCommand('fod2fixel ' + prefix + 'FOD.mif -peak ' + prefix + 'peaks.msf -mask ' + mask_in_path + ' -fmls_no_thresholds')
    delFile(prefix + 'FOD.mif')
    if iteration:
      delFile(mask_in_path)
    runCommand('fixel2voxel ' + prefix + 'peaks.msf split_value ' + prefix + 'amps.mif')
    runCommand('mrconvert ' + prefix + 'amps.mif ' + prefix + 'first_peaks.mif -coord 3 0 -axes 0,1,2')
    runCommand('mrconvert ' + prefix + 'amps.mif ' + prefix + 'second_peaks.mif -coord 3 1 -axes 0,1,2')
    delFile(prefix + 'amps.mif')
    runCommand('fixel2voxel ' + prefix + 'peaks.msf split_dir ' + prefix + 'all_dirs.mif')
    delFile(prefix + 'peaks.msf')
    runCommand('mrconvert ' + prefix + 'all_dirs.mif ' + prefix + 'first_dir.mif -coord 3 0:2')
    delFile(prefix + 'all_dirs.mif')
    # Calculate the 'cost function' Donald derived for selecting single-fibre voxels
    # https://github.com/MRtrix3/mrtrix3/pull/426
    #  sqrt(|peak1|) * (1 - |peak2| / |peak1|)^2
    runCommand('mrcalc ' + prefix + 'first_peaks.mif -sqrt 1 ' + prefix + 'second_peaks.mif ' + prefix + 'first_peaks.mif -div -sub 2 -pow -mult '+ prefix + 'CF.mif')
    delFile(prefix + 'first_peaks.mif')
    delFile(prefix + 'second_peaks.mif')
    # Select the top-ranked voxels
    runCommand('mrthreshold ' + prefix + 'CF.mif -top ' + str(lib.app.args.sf_voxels) + ' ' + prefix + 'SF.mif')
    # Generate a new response function based on this selection
    runCommand('sh2response dwiSH.mif ' + prefix + 'SF.mif ' + prefix + 'first_dir.mif ' + prefix + 'RF.txt' + iter_lmax_option)
    delFile(prefix + 'first_dir.mif')
    # Should we terminate?
    if iteration > 0:
      runCommand('mrcalc ' + prefix + 'SF.mif iter' + str(iteration-1) + '_SF.mif -sub ' + prefix + 'SF_diff.mif')
      delFile('iter' + str(iteration-1) + '_SF.mif')
      max_diff = getImageStat(prefix + 'SF_diff.mif', 'max')
      delFile(prefix + 'SF_diff.mif')
      if int(max_diff) == 0:
        printMessage('Convergence of SF voxel selection detected at iteration ' + str(iteration))
        delFile(prefix + 'CF.mif')
        shutil.copyfile(prefix + 'RF.txt', 'response.txt')
        shutil.move(prefix + 'SF.mif', 'voxels.mif')
        break

    # Select a greater number of top single-fibre voxels, and dilate (within bounds of initial mask);
    #   these are the voxels that will be re-tested in the next iteration
    runCommand('mrthreshold ' + prefix + 'CF.mif -top ' + str(lib.app.args.iter_voxels) + ' - | maskfilter - dilate - -npass ' + str(lib.app.args.dilate) + ' | mrcalc mask.mif - -mult ' + prefix + 'SF_dilated.mif')
    delFile(prefix + 'CF.mif')

  # Commence the next iteration

  # If terminating due to running out of iterations, still need to put the results in the appropriate location
  if not os.path.exists('response.txt'):
    printMessage('Exiting after maximum ' + str(lib.app.args.max_iters) + ' iterations')
    shutil.copyfile('iter' + str(lib.app.args.max_iters-1) + '_RF.txt', 'response.txt')
    shutil.move('iter' + str(lib.app.args.max_iters-1) + '_SF.mif', 'voxels.mif')
    
  shutil.copyfile('response.txt', getUserPath(lib.app.args.output, False))
Пример #12
0
def execute():
  import math, os, shutil
  import lib.app
  from lib.getImageStat import getImageStat
  from lib.printMessage import printMessage
  from lib.runCommand   import runCommand
  
  lmax_option = ''
  if lib.app.args.lmax:
    lmax_option = ' -lmax ' + lib.app.args.lmax
    
  runCommand('amp2sh dwi.mif dwiSH.mif' + lmax_option)
  convergence_change = 0.01 * lib.app.args.convergence

  for iteration in range(0, lib.app.args.max_iters):
    prefix = 'iter' + str(iteration) + '_'

    # How to initialise response function?
    # old dwi2response command used mean & standard deviation of DWI data; however
    #   this may force the output FODs to lmax=2 at the first iteration
    # Chantal used a tensor with low FA, but it'd be preferable to get the scaling right
    # Other option is to do as before, but get the ratio between l=0 and l=2, and
    #   generate l=4,6,... using that amplitude ratio
    if iteration == 0:
      RF_in_path = 'init_RF.txt'
      mask_in_path = 'mask.mif'
      runCommand('dwiextract dwi.mif shell.mif')
      # TODO This can be changed once #71 is implemented (mrstats statistics across volumes)
      volume_means = [float(x) for x in getImageStat('shell.mif', 'mean', 'mask.mif').split()]
      mean = sum(volume_means) / float(len(volume_means))
      volume_stds = [float(x) for x in getImageStat('shell.mif', 'std', 'mask.mif').split()]
      std = sum(volume_stds) / float(len(volume_stds))
      # Scale these to reflect the fact that we're moving to the SH basis
      mean *= math.sqrt(4.0 * math.pi)
      std  *= math.sqrt(4.0 * math.pi)
      # Now produce the initial response function
      # Let's only do it to lmax 4
      init_RF = [ str(mean), str(-0.5*std), str(0.25*std*std/mean) ]
      with open('init_RF.txt', 'w') as f:
        f.write(' '.join(init_RF))
    else:
      RF_in_path = 'iter' + str(iteration-1) + '_RF.txt'
      mask_in_path = 'iter' + str(iteration-1) + '_SF.mif'
  
    # Run CSD
    runCommand('dwi2fod dwi.mif ' + RF_in_path + ' ' + prefix + 'FOD.mif -mask ' + mask_in_path)
    # Get amplitudes of two largest peaks, and directions of largest
    runCommand('fod2fixel ' + prefix + 'FOD.mif -peak ' + prefix + 'peaks.msf -mask ' + mask_in_path + ' -fmls_no_thresholds')
    runCommand('fixel2voxel ' + prefix + 'peaks.msf split_value ' + prefix + 'amps.mif')
    runCommand('mrconvert ' + prefix + 'amps.mif ' + prefix + 'first_peaks.mif -coord 3 0 -axes 0,1,2')
    runCommand('mrconvert ' + prefix + 'amps.mif ' + prefix + 'second_peaks.mif -coord 3 1 -axes 0,1,2')
    runCommand('fixel2voxel ' + prefix + 'peaks.msf split_dir ' + prefix + 'all_dirs.mif')
    runCommand('mrconvert ' + prefix + 'all_dirs.mif ' + prefix + 'first_dir.mif -coord 3 0:2')
    # Revise single-fibre voxel selection based on ratio of tallest to second-tallest peak
    runCommand('mrcalc ' + prefix + 'second_peaks.mif ' + prefix + 'first_peaks.mif -div ' + prefix + 'peak_ratio.mif')
    runCommand('mrcalc ' + prefix + 'peak_ratio.mif ' + str(lib.app.args.peak_ratio) + ' -lt ' + mask_in_path + ' -mult ' + prefix + 'SF.mif')
    # Make sure image isn't empty
    SF_voxel_count = int(getImageStat(prefix + 'SF.mif', 'count', prefix + 'SF.mif'))
    if not SF_voxel_count:
      errorMessage('Aborting: All voxels have been excluded from single-fibre selection')
    # Generate a new response function
    runCommand('sh2response dwiSH.mif ' + prefix + 'SF.mif ' + prefix + 'first_dir.mif ' + prefix + 'RF.txt' + lmax_option)
    
    # Detect convergence
    # Look for a change > some percentage - don't bother looking at the masks
    if iteration > 0:
      old_RF_file = open(RF_in_path, 'r')
      old_RF = [ float(x) for x in old_RF_file.read().split() ]
      new_RF_file = open(prefix + 'RF.txt', 'r')
      new_RF = [ float(x) for x in new_RF_file.read().split() ]
      reiterate = False
      for index in range(0, len(old_RF)):
        mean = 0.5 * (old_RF[index] + new_RF[index])
        diff = math.fabs(0.5 * (old_RF[index] - new_RF[index]))
        ratio = diff / mean
        if ratio > convergence_change:
          reiterate = True
      if not reiterate:
        printMessage('Exiting at iteration ' + str(iteration) + ' with ' + str(SF_voxel_count) + ' SF voxels due to unchanged response function coefficients')
        shutil.copyfile(prefix + 'RF.txt', 'response.txt')
        shutil.copyfile(prefix + 'SF.mif', 'voxels.mif')
        break
        
  # Go to the next iteration

  # If we've terminated due to hitting the iteration limiter, we still need to copy the output file(s) to the correct location
  if not os.path.exists('response.txt'):
    printMessage('Exiting after maximum ' + str(lib.app.args.max_iters-1) + ' iterations with ' + str(SF_voxel_count) + ' SF voxels')
    shutil.copyfile('iter' + str(lib.app.args.max_iters-1) + '_RF.txt', 'response.txt')
    shutil.copyfile('iter' + str(lib.app.args.max_iters-1) + '_SF.mif', 'voxels.mif')

  shutil.copyfile('response.txt', os.path.join(lib.app.workingDir, lib.app.args.output))
Пример #13
0
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 -')
Пример #14
0
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 -')
Пример #15
0
    gm_response_file = os.path.join(subject_dir, 'gm_response.txt')
    lib.app.checkOutputFile(gm_response_file)
    csf_response_file = os.path.join(subject_dir, 'csf_response.txt')
    lib.app.checkOutputFile(csf_response_file)

    # DW gradient files
    grad_prefix = os.path.join(lib.app.args.bids_dir, label, 'dwi', label + '_dwi')
    if not (os.path.isfile(grad_prefix + '.bval') and os.path.isfile(grad_prefix + '.bvec')):
      grad_prefix = os.path.join(lib.app.args.bids_dir, 'dwi')
      if not (os.path.isfile(grad_prefix + '.bval') and os.path.isfile(grad_prefix + '.bvec')):
        errorMessage('Unable to locate valid diffusion gradient table');
    grad_import_option = ' -fslgrad ' + grad_prefix + '.bvec ' + grad_prefix + '.bval'


    # Stuff DWI gradients in *.mif file
    runCommand('mrconvert ' + all_dwi_images[0] + grad_import_option + ' ' + dwi_preproc_file)

    # Estimate WM, GM, CSF response functions
    runCommand('dwi2response ' + lib.app.mrtrixNThreads + ' dhollander ' + dwi_preproc_file + ' ' + wm_response_file + ' '
                                                                                                  + gm_response_file + ' '
                                                                                                  + csf_response_file + lib.app.mrtrixForce)

# running group level 1 (average response functions) TODO check for user supplied subset to ensure response is not biased
elif lib.app.args.analysis_level == "group1":
  printMessage('averaging response functions')



  # Check output files exist
  wm_response_file = os.path.join(lib.app.args.output_dir, 'average_wm_response.txt')
  lib.app.checkOutputFile(wm_response_file)
Пример #16
0
      grad_prefix = os.path.join(lib.app.args.bids_dir, 'dwi')
      if not (os.path.isfile(grad_prefix + '.bval') and os.path.isfile(grad_prefix + '.bvec')):
        errorMessage('Unable to locate valid diffusion gradient table');
    grad_import_option = ' -fslgrad ' + grad_prefix + '.bvec ' + grad_prefix + '.bval'

    # Json file
    json_path = os.path.join(lib.app.args.bids_dir, label, 'dwi', label + '_dwi.json')
    if os.path.isfile(json_path):
      json_import_option = ' -json_import ' + json_path
    else:
      json_import_option = ''


    # Stuff DWI gradients in *.mif file
    dwi_mrtrix_file = os.path.join(lib.app.tempDir, subject_label + 'dwi.mif')
    runCommand('mrconvert ' + all_dwi_images[0] + grad_import_option + json_import_option + ' ' + dwi_mrtrix_file)

    # Denoise
    dwi_denoised_file = os.path.join(lib.app.tempDir, subject_label + 'dwi_denoised.mif')
    runCommand('dwidenoise ' + dwi_mrtrix_file + ' ' + dwi_denoised_file)

    # Topup and eddy TODO add reverse phase encode capability
    runCommand('dwipreproc -rpe_none -pe_dir AP ' + lib.app.mrtrixNThreads + dwi_denoised_file + ' ' + dwi_preproc_file + lib.app.mrtrixForce)

    # Estimate WM, GM, CSF response functions
    runCommand('dwi2response ' + lib.app.mrtrixNThreads + ' dhollander ' + dwi_preproc_file + ' ' + wm_response_file + ' '
                                                                                                  + gm_response_file + ' '
                                                                                                  + csf_response_file + lib.app.mrtrixForce)

# running group level 1 (average response functions) TODO check for user supplied subset to ensure response is not biased
elif lib.app.args.analysis_level == "group1":
Пример #17
0
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'
    )
Пример #18
0
def execute():
    import math, os, shutil
    import lib.app
    from lib.getImageStat import getImageStat
    from lib.getUserPath import getUserPath
    from lib.printMessage import printMessage
    from lib.runCommand import runCommand

    lmax_option = ''
    if lib.app.args.lmax:
        lmax_option = ' -lmax ' + lib.app.args.lmax

    runCommand('amp2sh dwi.mif dwiSH.mif' + lmax_option)
    convergence_change = 0.01 * lib.app.args.convergence

    for iteration in range(0, lib.app.args.max_iters):
        prefix = 'iter' + str(iteration) + '_'

        # How to initialise response function?
        # old dwi2response command used mean & standard deviation of DWI data; however
        #   this may force the output FODs to lmax=2 at the first iteration
        # Chantal used a tensor with low FA, but it'd be preferable to get the scaling right
        # Other option is to do as before, but get the ratio between l=0 and l=2, and
        #   generate l=4,6,... using that amplitude ratio
        if iteration == 0:
            RF_in_path = 'init_RF.txt'
            mask_in_path = 'mask.mif'
            runCommand('dwiextract dwi.mif shell.mif')
            # TODO This can be changed once #71 is implemented (mrstats statistics across volumes)
            volume_means = [
                float(x)
                for x in getImageStat('shell.mif', 'mean', 'mask.mif').split()
            ]
            mean = sum(volume_means) / float(len(volume_means))
            volume_stds = [
                float(x)
                for x in getImageStat('shell.mif', 'std', 'mask.mif').split()
            ]
            std = sum(volume_stds) / float(len(volume_stds))
            # Scale these to reflect the fact that we're moving to the SH basis
            mean *= math.sqrt(4.0 * math.pi)
            std *= math.sqrt(4.0 * math.pi)
            # Now produce the initial response function
            # Let's only do it to lmax 4
            init_RF = [
                str(mean),
                str(-0.5 * std),
                str(0.25 * std * std / mean)
            ]
            with open('init_RF.txt', 'w') as f:
                f.write(' '.join(init_RF))
        else:
            RF_in_path = 'iter' + str(iteration - 1) + '_RF.txt'
            mask_in_path = 'iter' + str(iteration - 1) + '_SF.mif'

        # Run CSD
        runCommand('dwi2fod csd dwi.mif ' + RF_in_path + ' ' + prefix +
                   'FOD.mif -mask ' + mask_in_path)
        # Get amplitudes of two largest peaks, and directions of largest
        runCommand('fod2fixel ' + prefix + 'FOD.mif -peak ' + prefix +
                   'peaks.msf -mask ' + mask_in_path + ' -fmls_no_thresholds')
        runCommand('fixel2voxel ' + prefix + 'peaks.msf split_value ' +
                   prefix + 'amps.mif')
        runCommand('mrconvert ' + prefix + 'amps.mif ' + prefix +
                   'first_peaks.mif -coord 3 0 -axes 0,1,2')
        runCommand('mrconvert ' + prefix + 'amps.mif ' + prefix +
                   'second_peaks.mif -coord 3 1 -axes 0,1,2')
        runCommand('fixel2voxel ' + prefix + 'peaks.msf split_dir ' + prefix +
                   'all_dirs.mif')
        runCommand('mrconvert ' + prefix + 'all_dirs.mif ' + prefix +
                   'first_dir.mif -coord 3 0:2')
        # Revise single-fibre voxel selection based on ratio of tallest to second-tallest peak
        runCommand('mrcalc ' + prefix + 'second_peaks.mif ' + prefix +
                   'first_peaks.mif -div ' + prefix + 'peak_ratio.mif')
        runCommand('mrcalc ' + prefix + 'peak_ratio.mif ' +
                   str(lib.app.args.peak_ratio) + ' -lt ' + mask_in_path +
                   ' -mult ' + prefix + 'SF.mif')
        # Make sure image isn't empty
        SF_voxel_count = int(
            getImageStat(prefix + 'SF.mif', 'count', prefix + 'SF.mif'))
        if not SF_voxel_count:
            errorMessage(
                'Aborting: All voxels have been excluded from single-fibre selection'
            )
        # Generate a new response function
        runCommand('sh2response dwiSH.mif ' + prefix + 'SF.mif ' + prefix +
                   'first_dir.mif ' + prefix + 'RF.txt' + lmax_option)

        # Detect convergence
        # Look for a change > some percentage - don't bother looking at the masks
        if iteration > 0:
            old_RF_file = open(RF_in_path, 'r')
            old_RF = [float(x) for x in old_RF_file.read().split()]
            new_RF_file = open(prefix + 'RF.txt', 'r')
            new_RF = [float(x) for x in new_RF_file.read().split()]
            reiterate = False
            for index in range(0, len(old_RF)):
                mean = 0.5 * (old_RF[index] + new_RF[index])
                diff = math.fabs(0.5 * (old_RF[index] - new_RF[index]))
                ratio = diff / mean
                if ratio > convergence_change:
                    reiterate = True
            if not reiterate:
                printMessage(
                    'Exiting at iteration ' + str(iteration) + ' with ' +
                    str(SF_voxel_count) +
                    ' SF voxels due to unchanged response function coefficients'
                )
                shutil.copyfile(prefix + 'RF.txt', 'response.txt')
                shutil.copyfile(prefix + 'SF.mif', 'voxels.mif')
                break

    # Go to the next iteration

    # If we've terminated due to hitting the iteration limiter, we still need to copy the output file(s) to the correct location
    if not os.path.exists('response.txt'):
        printMessage('Exiting after maximum ' +
                     str(lib.app.args.max_iters - 1) + ' iterations with ' +
                     str(SF_voxel_count) + ' SF voxels')
        shutil.copyfile('iter' + str(lib.app.args.max_iters - 1) + '_RF.txt',
                        'response.txt')
        shutil.copyfile('iter' + str(lib.app.args.max_iters - 1) + '_SF.mif',
                        'voxels.mif')

    shutil.copyfile('response.txt', getUserPath(lib.app.args.output, False))
Пример #19
0
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'
    )
Пример #20
0
def execute():
  import os, shutil
  import lib.app
  from lib.getUserPath import getUserPath
  from lib.runCommand  import runCommand
  lmax_option = ''
  if lib.app.args.lmax:
    lmax_option = ' -lmax ' + lib.app.args.lmax
  if not lib.app.args.mask:
    runCommand('maskfilter mask.mif erode mask_eroded.mif -npass ' + str(lib.app.args.erode))
    mask_path = 'mask_eroded.mif'
  else:
    mask_path = 'mask.mif'
  runCommand('dwi2tensor dwi.mif -mask ' + mask_path + ' tensor.mif')
  runCommand('tensor2metric tensor.mif -fa fa.mif -vector vector.mif -mask ' + mask_path)
  if lib.app.args.threshold:
    runCommand('mrthreshold fa.mif voxels.mif -abs ' + str(lib.app.args.threshold))
  else:
    runCommand('mrthreshold fa.mif voxels.mif -top ' + str(lib.app.args.number))
  runCommand('amp2sh dwi.mif dwiSH.mif' + lmax_option)
  runCommand('sh2response dwiSH.mif voxels.mif vector.mif response.txt' + lmax_option)

  shutil.copyfile('response.txt', getUserPath(lib.app.args.output, False))
Пример #21
0
def execute():
  import os, sys
  import lib.app
  from lib.errorMessage import errorMessage
  from lib.runCommand import runCommand

  lut_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_path = os.path.join(freesurfer_home, 'FreeSurferColorLUT.txt')
    if not os.path.isfile(lut_path):
      errorMessage('Could not find FreeSurfer lookup table file\n(Expected location: ' + freesurfer_lut + ')')

  if lib.app.args.sgm_amyg_hipp:
    config_file_name = 'FreeSurfer2ACT_sgm_amyg_hipp.txt'
  else:
    config_file_name = 'FreeSurfer2ACT.txt'
  config_path = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), 'data', config_file_name);
  if not os.path.isfile(config_path):
    errorMessage('Could not find config file for converting FreeSurfer parcellation output to tissues\n(Expected location: ' + config_path + ')')

  # Initial conversion from FreeSurfer parcellation to five principal tissue types
  runCommand('labelconfig input.mif ' + config_path + ' indices.mif -lut_freesurfer ' + lut_path)

  # 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')
Пример #22
0
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')
Пример #23
0
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')
Пример #24
0
def getInputFiles():
  import os
  import lib.app
  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')
Пример #25
0
def execute():
    import os, shutil
    import lib.app
    from lib.getImageStat import getImageStat
    from lib.printMessage import printMessage
    from lib.runCommand import runCommand

    lmax_option = ''
    if lib.app.args.lmax:
        lmax_option = ' -lmax ' + lib.app.args.lmax

    if lib.app.args.max_iters < 2:
        errorMessage('Number of iterations must be at least 2')

    runCommand('amp2sh dwi.mif dwiSH.mif' + lmax_option)

    for iteration in range(0, lib.app.args.max_iters):
        prefix = 'iter' + str(iteration) + '_'

        if iteration == 0:
            RF_in_path = 'init_RF.txt'
            mask_in_path = 'mask.mif'
            init_RF = '1 -1 1'
            with open('init_RF.txt', 'w') as f:
                f.write(init_RF)
            iter_lmax_option = ' -lmax 4'
        else:
            RF_in_path = 'iter' + str(iteration - 1) + '_RF.txt'
            mask_in_path = 'iter' + str(iteration - 1) + '_SF_dilated.mif'
            iter_lmax_option = lmax_option

        # Run CSD
        runCommand('dwi2fod dwi.mif ' + RF_in_path + ' ' + prefix +
                   'FOD.mif -mask ' + mask_in_path + iter_lmax_option)
        # Get amplitudes of two largest peaks, and direction of largest
        # TODO Speed-test fod2fixel against sh2peaks
        # TODO Add maximum number of fixels per voxel option to fod2fixel?
        runCommand('fod2fixel ' + prefix + 'FOD.mif -peak ' + prefix +
                   'peaks.msf -mask ' + mask_in_path + ' -fmls_no_thresholds')
        runCommand('fixel2voxel ' + prefix + 'peaks.msf split_value ' +
                   prefix + 'amps.mif')
        runCommand('mrconvert ' + prefix + 'amps.mif ' + prefix +
                   'first_peaks.mif -coord 3 0 -axes 0,1,2')
        runCommand('mrconvert ' + prefix + 'amps.mif ' + prefix +
                   'second_peaks.mif -coord 3 1 -axes 0,1,2')
        runCommand('fixel2voxel ' + prefix + 'peaks.msf split_dir ' + prefix +
                   'all_dirs.mif')
        runCommand('mrconvert ' + prefix + 'all_dirs.mif ' + prefix +
                   'first_dir.mif -coord 3 0:2')
        # Calculate the 'cost function' Donald derived for selecting single-fibre voxels
        # https://github.com/MRtrix3/mrtrix3/pull/426
        #  sqrt(|peak1|) * (1 - |peak2| / |peak1|)^2
        runCommand('mrcalc ' + prefix + 'first_peaks.mif -sqrt 1 ' + prefix +
                   'second_peaks.mif ' + prefix +
                   'first_peaks.mif -div -sub 2 -pow -mult ' + prefix +
                   'CF.mif')
        # Select the top-ranked voxels
        runCommand('mrthreshold ' + prefix + 'CF.mif -top ' +
                   str(lib.app.args.sf_voxels) + ' ' + prefix + 'SF.mif')
        # Generate a new response function based on this selection
        runCommand('sh2response dwiSH.mif ' + prefix + 'SF.mif ' + prefix +
                   'first_dir.mif ' + prefix + 'RF.txt' + iter_lmax_option)
        # Should we terminate?
        if iteration > 0:
            runCommand('mrcalc ' + prefix + 'SF.mif iter' +
                       str(iteration - 1) + '_SF.mif -sub ' + prefix +
                       'SF_diff.mif')
            max_diff = getImageStat(prefix + 'SF_diff.mif', 'max')
            if int(max_diff) == 0:
                printMessage(
                    'Convergence of SF voxel selection detected at iteration '
                    + str(iteration))
                shutil.copyfile(prefix + 'RF.txt', 'response.txt')
                shutil.copyfile(prefix + 'SF.mif', 'voxels.mif')
                break

        # Select a greater number of top single-fibre voxels, and dilate;
        #   these are the voxels that will be re-tested in the next iteration
        runCommand('mrthreshold ' + prefix + 'CF.mif -top ' +
                   str(lib.app.args.iter_voxels) + ' - | maskfilter ' +
                   prefix + 'SF.mif dilate ' + prefix +
                   'SF_dilated.mif -npass ' + str(lib.app.args.dilate))

    # Commence the next iteration

    # If terminating due to running out of iterations, still need to put the results in the appropriate location
    if not os.path.exists('response.txt'):
        printMessage('Exiting after maximum ' +
                     str(lib.app.args.max_iters - 1) + ' iterations')
        shutil.copyfile('iter' + str(lib.app.args.max_iters - 1) + '_RF.txt',
                        'response.txt')
        shutil.copyfile('iter' + str(lib.app.args.max_iters - 1) + '_SF.mif',
                        'voxels.mif')

    shutil.copyfile('response.txt',
                    os.path.join(lib.app.workingDir, lib.app.args.output))
Пример #26
0
def execute():
  import os, shutil
  import lib.app
  from lib.runCommand import runCommand
  lmax_option = ''
  if lib.app.args.lmax:
    lmax_option = ' -lmax ' + lib.app.args.lmax
  if not lib.app.args.mask:
    runCommand('maskfilter mask.mif erode mask_eroded.mif -npass ' + str(lib.app.args.erode))
    mask_path = 'mask_eroded.mif'
  else:
    mask_path = 'mask.mif'
  runCommand('dwi2tensor dwi.mif -mask ' + mask_path + ' tensor.mif')
  runCommand('tensor2metric tensor.mif -fa fa.mif -vector vector.mif -mask ' + mask_path)
  if lib.app.args.threshold:
    runCommand('mrthreshold fa.mif voxels.mif -abs ' + str(lib.app.args.threshold))
  else:
    runCommand('mrthreshold fa.mif voxels.mif -top ' + str(lib.app.args.number))
  runCommand('amp2sh dwi.mif dwiSH.mif' + lmax_option)
  runCommand('sh2response dwiSH.mif voxels.mif vector.mif response.txt' + lmax_option)

  shutil.copyfile('response.txt', os.path.join(lib.app.workingDir, lib.app.args.output))