def _shebang(item): from mrtrix3 import app, utils #pylint: disable=import-outside-toplevel # If a complete path has been provided rather than just a file name, don't perform any additional file search if os.sep in item: path = item else: path = version_match(item) if path == item: path = find_executable(exe_name(item)) if not path: app.debug('File \"' + item + '\": Could not find file to query') return [] # Read the first 1024 bytes of the file with open(path, 'rb') as file_in: data = file_in.read(1024) # Try to find the shebang line for line in data.splitlines(): # Are there any non-text characters? If so, it's a binary file, so no need to looking for a shebang try: line = str(line.decode('utf-8')) except: app.debug('File \"' + item + '\": Not a text file') return [] line = line.strip() if len(line) > 2 and line[0:2] == '#!': # Need to strip first in case there's a gap between the shebang symbol and the interpreter path shebang = line[2:].strip().split(' ') # On Windows, /usr/bin/env can't be easily found, and any direct interpreter path will have a similar issue. # Instead, manually find the right interpreter to call using distutils # Also if script is written in Python, try to execute it using the same interpreter as that currently running if os.path.basename(shebang[0]) == 'env': if len(shebang) < 2: app.warn('Invalid shebang in script file \"' + item + '\" (missing interpreter after \"env\")') return [] if shebang[1] == 'python': if not sys.executable: app.warn( 'Unable to self-identify Python interpreter; file \"' + item + '\" not guaranteed to execute on same version') return [] shebang = [sys.executable] + shebang[2:] app.debug('File \"' + item + '\": Using current Python interpreter') elif utils.is_windows(): shebang = [ os.path.abspath(find_executable(exe_name(shebang[1]))) ] + shebang[2:] elif utils.is_windows(): shebang = [ os.path.abspath( find_executable(exe_name(os.path.basename( shebang[0])))) ] + shebang[1:] app.debug('File \"' + item + '\": string \"' + line + '\": ' + str(shebang)) return shebang app.debug('File \"' + item + '\": No shebang found') return []
def execute(): #pylint: disable=unused-variable if utils.is_windows(): raise MRtrixError('Script cannot run using FSL on Windows due to FSL dependency') if not os.environ.get('FSLDIR', ''): raise MRtrixError('Environment variable FSLDIR is not set; please run appropriate FSL configuration script') fast_cmd = fsl.exe_name('fast') app.warn('Use of fsl algorithm in dwibiascorrect script is discouraged due to its strong dependence ' + \ 'on brain masking (specifically its inability to correct voxels outside of this mask).' + \ 'Use of the ants algorithm is recommended for quantitative DWI analyses.') # Generate a mean b=0 image run.command('dwiextract in.mif - -bzero | mrmath - mean mean_bzero.mif -axis 3') # FAST doesn't accept a mask input; therefore need to explicitly mask the input image run.command('mrcalc mean_bzero.mif mask.mif -mult - | mrconvert - mean_bzero_masked.nii -strides -1,+2,+3') run.command(fast_cmd + ' -t 2 -o fast -n 3 -b mean_bzero_masked.nii') bias_path = fsl.find_image('fast_bias') # Rather than using a bias field estimate of 1.0 outside the brain mask, zero-fill the # output image outside of this mask run.command('mrcalc in.mif ' + bias_path + ' -div mask.mif -mult result.mif') run.command('mrconvert result.mif ' + path.from_user(app.ARGS.output), mrconvert_keyval=path.from_user(app.ARGS.input, False), force=app.FORCE_OVERWRITE) if app.ARGS.bias: run.command('mrconvert ' + bias_path + ' ' + path.from_user(app.ARGS.bias), mrconvert_keyval=path.from_user(app.ARGS.input, False), force=app.FORCE_OVERWRITE)
def is_hidden(directory, filename): if utils.is_windows(): try: attrs = ctypes.windll.kernel32.GetFileAttributesW(u"%s" % str(os.path.join(directory, filename))) assert attrs != -1 return bool(attrs & 2) except (AttributeError, AssertionError): return filename.startswith('.') return filename.startswith('.')
def _shebang(item): import os from distutils.spawn import find_executable from mrtrix3 import app, MRtrixError, utils # If a complete path has been provided rather than just a file name, don't perform any additional file search if os.sep in item: path = item else: path = version_match(item) if path == item: path = find_executable(exe_name(item)) if not path: app.debug('File \"' + item + '\": Could not find file to query') return [] # Read the first 1024 bytes of the file with open(path, 'rb') as file_in: data = file_in.read(1024) # Try to find the shebang line for line in data.splitlines(): # Are there any non-text characters? If so, it's a binary file, so no need to looking for a shebang try: line = str(line.decode('utf-8')) except: app.debug('File \"' + item + '\": Not a text file') return [] line = line.strip() if len(line) > 2 and line[0:2] == '#!': # Need to strip first in case there's a gap between the shebang symbol and the interpreter path shebang = line[2:].strip().split(' ') if utils.is_windows(): # On Windows, /usr/bin/env can't be easily found, and any direct interpreter path will have a similar issue. # Instead, manually find the right interpreter to call using distutils if os.path.basename(shebang[0]) == 'env': new_shebang = [ os.path.abspath(find_executable(exe_name(shebang[1]))) ] new_shebang.extend(shebang[2:]) shebang = new_shebang else: new_shebang = [ os.path.abspath( find_executable( exe_name(os.path.basename(shebang[0])))) ] new_shebang.extend(shebang[1:]) shebang = new_shebang if not shebang or not shebang[0]: raise MRtrixError('malformed shebang in file \"' + item + '\": \"' + line + '\"') app.debug('File \"' + item + '\": string \"' + line + '\": ' + str(shebang)) return shebang app.debug('File \"' + item + '\": No shebang found') return []
def in_use(path): if not os.path.isfile(path): return None if utils.is_windows(): if not os.access(path, os.W_OK): return None try: with open(path, 'rb+') as dummy_f: pass return False except: return True if not find_executable('fuser'): return None # fuser returns zero if there IS at least one process accessing the file # A fatal error will result in a non-zero code -> in_use() = False, so wait_for() can return return not subprocess.call(['fuser', '-s', path], shell=False, stdin=None, stdout=None, stderr=None)
def exe_name(item): from mrtrix3 import app, utils #pylint: disable=import-outside-toplevel if not utils.is_windows(): path = item elif item.endswith('.exe'): path = item elif os.path.isfile(os.path.join(BIN_PATH, item)): path = item elif os.path.isfile(os.path.join(BIN_PATH, item + '.exe')): path = item + '.exe' elif find_executable(item) is not None: path = item elif find_executable(item + '.exe') is not None: path = item + '.exe' # If it can't be found, return the item as-is; find_executable() fails to identify Python scripts else: path = item app.debug(item + ' -> ' + path) return path
'SIGBUS' : 'Bus error: Accessing invalid address (out of storage space?)', 'SIGFPE' : 'Floating-point arithmetic exception', 'SIGHUP' : 'Disconnection of terminal', 'SIGILL' : 'Illegal instruction (corrupt binary command file?)', 'SIGINT' : 'Program manually interrupted by terminal', 'SIGPIPE': 'Nothing on receiving end of pipe', 'SIGPWR' : 'Power failure restart', 'SIGQUIT': 'Received terminal quit signal', 'SIGSEGV': 'Segmentation fault: Invalid memory reference', 'SIGSYS' : 'Bad system call', 'SIGXCPU': 'CPU time limit exceeded', 'SIGXFSZ': 'File size limit exceeded' } # Can't be handled; see https://bugs.python.org/issue9524 # 'CTRL_C_EVENT': 'Terminated by user Ctrl-C input', # 'CTRL_BREAK_EVENT': 'Terminated by user Ctrl-Break input' if utils.is_windows(): _SIGNALS['SIGBREAK'] = 'Received Windows \'break\' signal' else: _SIGNALS['SIGTERM'] = 'Received termination signal' # Generally preferable to use: # "import mrtrix3" # "mrtrix3.execute()" # , rather than executing this function directly def _execute(module): #pylint: disable=unused-variable from mrtrix3 import run #pylint: disable=import-outside-toplevel global ARGS, CMDLINE, CONTINUE_OPTION, DO_CLEANUP, EXEC_NAME, FORCE_OVERWRITE, NUM_THREADS, SCRATCH_DIR, VERBOSITY, WORKING_DIR # Set up signal handlers
def execute(): #pylint: disable=unused-variable import math, os from distutils.spawn import find_executable from mrtrix3 import app, fsl, image, MRtrixError, path, run, utils if utils.is_windows(): raise MRtrixError( '\'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: raise MRtrixError( 'Environment variable FSLDIR is not set; please run appropriate FSL configuration script' ) bet_cmd = fsl.exe_name('bet') fast_cmd = fsl.exe_name('fast') first_cmd = fsl.exe_name('run_first_all') ssroi_cmd = fsl.exe_name('standard_space_roi') first_atlas_path = os.path.join(fsl_path, 'data', 'first', 'models_336_bin') if not os.path.isdir(first_atlas_path): raise MRtrixError( 'Atlases required for FSL\'s FIRST program not installed; please install fsl-first-data using your relevant package manager' ) fsl_suffix = fsl.suffix() sgm_structures = [ 'L_Accu', 'R_Accu', 'L_Caud', 'R_Caud', 'L_Pall', 'R_Pall', 'L_Puta', 'R_Puta', 'L_Thal', 'R_Thal' ] if app.ARGS.sgm_amyg_hipp: sgm_structures.extend(['L_Amyg', 'R_Amyg', 'L_Hipp', 'R_Hipp']) t1_spacing = image.Header('input.mif').spacing() upsample_for_first = False # If voxel size is 1.25mm or larger, make a guess that the user has erroneously re-gridded their data if math.pow(t1_spacing[0] * t1_spacing[1] * t1_spacing[2], 1.0 / 3.0) > 1.225: app.warn( 'Voxel size larger than expected for T1-weighted images (' + str(t1_spacing) + '); ' 'note that ACT does not require re-gridding of T1 image to DWI space, and indeed ' 'retaining the original higher resolution of the T1 image is preferable' ) upsample_for_first = True run.command('mrconvert input.mif T1.nii -strides -1,+2,+3') fast_t1_input = 'T1.nii' fast_t2_input = '' # Decide whether or not we're going to do any brain masking if os.path.exists('mask.mif'): fast_t1_input = 'T1_masked' + fsl_suffix # Check to see if the mask matches the T1 image if image.match('T1.nii', 'mask.mif'): run.command('mrcalc T1.nii mask.mif -mult ' + fast_t1_input) mask_path = 'mask.mif' else: app.warn('Mask image does not match input image - re-gridding') run.command( 'mrtransform mask.mif mask_regrid.mif -template T1.nii -datatype bit' ) run.command('mrcalc T1.nii mask_regrid.mif -mult ' + fast_t1_input) mask_path = 'mask_regrid.mif' if os.path.exists('T2.nii'): fast_t2_input = 'T2_masked' + fsl_suffix run.command('mrcalc T2.nii ' + mask_path + ' -mult ' + fast_t2_input) elif app.ARGS.premasked: fast_t1_input = 'T1.nii' if os.path.exists('T2.nii'): fast_t2_input = 'T2.nii' else: # Use FSL command standard_space_roi to do an initial masking of the image before BET # Also reduce the FoV of the image # Using MNI 1mm dilated brain mask rather than the -b option in standard_space_roi (which uses the 2mm mask); the latter looks 'buggy' to me... Unfortunately even with the 1mm 'dilated' mask, it can still cut into some brain areas, hence the explicit dilation mni_mask_path = os.path.join(fsl_path, 'data', 'standard', 'MNI152_T1_1mm_brain_mask_dil.nii.gz') mni_mask_dilation = 0 if os.path.exists(mni_mask_path): mni_mask_dilation = 4 else: mni_mask_path = os.path.join( fsl_path, 'data', 'standard', 'MNI152_T1_2mm_brain_mask_dil.nii.gz') if os.path.exists(mni_mask_path): mni_mask_dilation = 2 try: if mni_mask_dilation: run.command('maskfilter ' + mni_mask_path + ' dilate mni_mask.nii -npass ' + str(mni_mask_dilation)) if app.ARGS.nocrop: ssroi_roi_option = ' -roiNONE' else: ssroi_roi_option = ' -roiFOV' run.command(ssroi_cmd + ' T1.nii T1_preBET' + fsl_suffix + ' -maskMASK mni_mask.nii' + ssroi_roi_option) else: run.command(ssroi_cmd + ' T1.nii T1_preBET' + fsl_suffix + ' -b') except run.MRtrixCmdError: pass try: pre_bet_image = fsl.find_image('T1_preBET') except MRtrixError: app.warn('FSL script \'standard_space_roi\' did not complete successfully' + \ ('' if find_executable('dc') else ' (possibly due to program \'dc\' not being installed') + '; ' + \ 'attempting to continue by providing un-cropped image to BET') pre_bet_image = 'T1.nii' # BET run.command(bet_cmd + ' ' + pre_bet_image + ' T1_BET' + fsl_suffix + ' -f 0.15 -R') fast_t1_input = fsl.find_image('T1_BET' + fsl_suffix) if os.path.exists('T2.nii'): if app.ARGS.nocrop: fast_t2_input = 'T2.nii' else: # Just a reduction of FoV, no sub-voxel interpolation going on run.command('mrtransform T2.nii T2_cropped.nii -template ' + fast_t1_input + ' -interp nearest') fast_t2_input = 'T2_cropped.nii' # Finish branching based on brain masking # FAST if fast_t2_input: run.command(fast_cmd + ' -S 2 ' + fast_t2_input + ' ' + fast_t1_input) else: run.command(fast_cmd + ' ' + fast_t1_input) # FIRST first_input = 'T1.nii' if upsample_for_first: app.warn( 'Generating 1mm isotropic T1 image for FIRST in hope of preventing failure, since input image is of lower resolution' ) run.command('mrgrid T1.nii regrid T1_1mm.nii -voxel 1.0 -interp sinc') first_input = 'T1_1mm.nii' first_brain_extracted_option = '' if app.ARGS.premasked: first_brain_extracted_option = ' -b' first_debug_option = '' if not app.DO_CLEANUP: first_debug_option = ' -d' first_verbosity_option = '' if app.VERBOSITY == 3: first_verbosity_option = ' -v' run.command(first_cmd + ' -m none -s ' + ','.join(sgm_structures) + ' -i ' + first_input + ' -o first' + first_brain_extracted_option + first_debug_option + first_verbosity_option) fsl.check_first('first', sgm_structures) # Convert FIRST meshes to partial volume images pve_image_list = [] progress = app.ProgressBar( 'Generating partial volume images for SGM structures', len(sgm_structures)) for struct in sgm_structures: pve_image_path = 'mesh2voxel_' + struct + '.mif' vtk_in_path = 'first-' + struct + '_first.vtk' vtk_temp_path = struct + '.vtk' run.command('meshconvert ' + vtk_in_path + ' ' + vtk_temp_path + ' -transform first2real ' + first_input) run.command('mesh2voxel ' + vtk_temp_path + ' ' + fast_t1_input + ' ' + pve_image_path) pve_image_list.append(pve_image_path) progress.increment() progress.done() run.command(['mrmath', pve_image_list, 'sum', '-', '|', \ 'mrcalc', '-', '1.0', '-min', 'all_sgms.mif']) # Combine the tissue images into the 5TT format within the script itself fast_output_prefix = fast_t1_input.split('.')[0] fast_csf_output = fsl.find_image(fast_output_prefix + '_pve_0') fast_gm_output = fsl.find_image(fast_output_prefix + '_pve_1') fast_wm_output = fsl.find_image(fast_output_prefix + '_pve_2') # Step 1: Run LCC on the WM image run.command( 'mrthreshold ' + fast_wm_output + ' - -abs 0.001 | maskfilter - connect - -connectivity | mrcalc 1 - 1 -gt -sub remove_unconnected_wm_mask.mif -datatype bit' ) # Step 2: Generate the images in the same fashion as the old 5ttgen binary used to: # - Preserve CSF as-is # - Preserve SGM, unless it results in a sum of volume fractions greater than 1, in which case clamp # - Multiply the FAST volume fractions of GM and CSF, so that the sum of CSF, SGM, CGM and WM is 1.0 run.command('mrcalc ' + fast_csf_output + ' remove_unconnected_wm_mask.mif -mult csf.mif') run.command('mrcalc 1.0 csf.mif -sub all_sgms.mif -min sgm.mif') run.command('mrcalc 1.0 csf.mif sgm.mif -add -sub ' + fast_gm_output + ' ' + fast_wm_output + ' -add -div multiplier.mif') run.command( 'mrcalc multiplier.mif -finite multiplier.mif 0.0 -if multiplier_noNAN.mif' ) run.command( 'mrcalc ' + fast_gm_output + ' multiplier_noNAN.mif -mult remove_unconnected_wm_mask.mif -mult cgm.mif' ) run.command( 'mrcalc ' + fast_wm_output + ' multiplier_noNAN.mif -mult remove_unconnected_wm_mask.mif -mult wm.mif' ) run.command('mrcalc 0 wm.mif -min path.mif') run.command( 'mrcat cgm.mif sgm.mif wm.mif csf.mif path.mif - -axis 3 | mrconvert - combined_precrop.mif -strides +2,+3,+4,+1' ) # Crop to reduce file size (improves caching of image data during tracking) if app.ARGS.nocrop: run.function(os.rename, 'combined_precrop.mif', 'result.mif') else: run.command( 'mrmath combined_precrop.mif sum - -axis 3 | mrthreshold - - -abs 0.5 | mrgrid combined_precrop.mif crop result.mif -mask -' ) run.command('mrconvert result.mif ' + path.from_user(app.ARGS.output), mrconvert_keyval=path.from_user(app.ARGS.input), force=app.FORCE_OVERWRITE)