def ThresholdImage(volume, output_file='', threshlo=1, threshhi=10000): """ Use the ThresholdImage function in ANTs to threshold image volume. Usage: ThresholdImage ImageDimension ImageIn.ext outImage.ext threshlo threshhi <insideValue> <outsideValue> Parameters ---------- volume : string nibabel-readable image volume output_file : string nibabel-readable image volume threshlo : integer lower threshold threshhi : integer upper threshold Returns ------- output_file : string name of output nibabel-readable image volume Examples -------- >>> import os >>> from mindboggle.thirdparty.ants import ThresholdImage >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> volume = fetch_data(urls['T1_001'], '', '.nii.gz') >>> os.rename(volume, volume + '.nii.gz') >>> volume += '.nii.gz' >>> output_file = '' >>> threshlo = 500 >>> threshhi = 10000 >>> output_file = ThresholdImage(volume, output_file, threshlo, threshhi) # doctest: +SKIP View result (skip test): >>> from mindboggle.mio.plots import plot_volumes >>> plot_volumes(output_file) # doctest: +SKIP """ import os from mindboggle.guts.utilities import execute if not output_file: output_file = os.path.join(os.getcwd(), 'threshold_' + os.path.basename(volume)) cmd = [ 'ThresholdImage', '3', volume, output_file, str(threshlo), str(threshhi) ] execute(cmd, 'os') if not os.path.exists(output_file): raise IOError("ThresholdImage did not create " + output_file + ".") return output_file
def ThresholdImage(volume, output_file='', threshlo=1, threshhi=10000): """ Use the ThresholdImage function in ANTs to threshold image volume. Usage: ThresholdImage ImageDimension ImageIn.ext outImage.ext threshlo threshhi <insideValue> <outsideValue> Parameters ---------- volume : string nibabel-readable image volume output_file : string nibabel-readable image volume threshlo : integer lower threshold threshhi : integer upper threshold Returns ------- output_file : string name of output nibabel-readable image volume Examples -------- >>> import os >>> from mindboggle.thirdparty.ants import ThresholdImage >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> volume = fetch_data(urls['T1_001']) >>> os.rename(volume, volume + '.nii.gz') >>> volume += '.nii.gz' >>> output_file = '' >>> threshlo = 500 >>> threshhi = 10000 >>> output_file = ThresholdImage(volume, output_file, threshlo, threshhi) # doctest: +SKIP View result (skip test): >>> from mindboggle.mio.plots import plot_volumes >>> plot_volumes(output_file) # doctest: +SKIP """ import os from mindboggle.guts.utilities import execute if not output_file: output_file = os.path.join(os.getcwd(), 'threshold_' + os.path.basename(volume)) cmd = ['ThresholdImage', '3', volume, output_file, str(threshlo), str(threshhi)] execute(cmd, 'os') if not os.path.exists(output_file): raise IOError("ThresholdImage did not create " + output_file + ".") return output_file
def ThresholdImage(volume, output_file='', threshlo=1, threshhi=10000): """ Use the ThresholdImage function in ANTs to threshold image volume:: Usage: ThresholdImage ImageDimension ImageIn.ext outImage.ext threshlo threshhi <insideValue> <outsideValue> Parameters ---------- volume : string nibabel-readable image volume output_file : string nibabel-readable image volume threshlo : integer lower threshold threshhi : integer upper threshold Returns ------- output_file : string name of output nibabel-readable image volume Examples -------- >>> import os >>> from mindboggle.thirdparty.ants import ThresholdImage >>> from mindboggle.mio.plots import plot_volumes >>> path = os.path.join(os.environ['MINDBOGGLE_DATA']) >>> volume = os.path.join(path, 'arno', 'mri', 't1weighted.nii.gz') >>> output_file = '' >>> threshlo = 500 >>> threshhi = 10000 >>> output_file = ThresholdImage(volume, output_file, threshlo, threshhi) >>> # View >>> plot_volumes(output_file) """ import os from mindboggle.guts.utilities import execute if not output_file: output_file = os.path.join(os.getcwd(), 'threshold_' + os.path.basename(volume)) cmd = 'ThresholdImage 3 {0} {1} {2} {3}'.format(volume, output_file, threshlo, threshhi) execute(cmd, 'os') if not os.path.exists(output_file): raise (IOError("ThresholdImage did not create " + output_file + ".")) return output_file
def ThresholdImage(volume, output_file='', threshlo=1, threshhi=10000): """ Use the ThresholdImage function in ANTs to threshold image volume:: Usage: ThresholdImage ImageDimension ImageIn.ext outImage.ext threshlo threshhi <insideValue> <outsideValue> Parameters ---------- volume : string nibabel-readable image volume output_file : string nibabel-readable image volume threshlo : integer lower threshold threshhi : integer upper threshold Returns ------- output_file : string name of output nibabel-readable image volume Examples -------- >>> import os >>> from mindboggle.thirdparty.ants import ThresholdImage >>> from mindboggle.mio.plots import plot_volumes >>> path = os.path.join(os.environ['MINDBOGGLE_DATA']) >>> volume = os.path.join(path, 'arno', 'mri', 't1weighted.nii.gz') >>> output_file = '' >>> threshlo = 500 >>> threshhi = 10000 >>> output_file = ThresholdImage(volume, output_file, threshlo, threshhi) >>> # View >>> plot_volumes(output_file) """ import os from mindboggle.guts.utilities import execute if not output_file: output_file = os.path.join(os.getcwd(), 'threshold_' + os.path.basename(volume)) cmd = 'ThresholdImage 3 {0} {1} {2} {3}'.format(volume, output_file, threshlo, threshhi) execute(cmd, 'os') if not os.path.exists(output_file): raise(IOError("ThresholdImage did not create " + output_file + ".")) return output_file
def plot_volumes(volume_files, command='fslview'): """ Use fslview to visualize image volume data. Parameters ---------- volume_files : list of strings names of image volume files command : string plotting software command Examples -------- >>> import os >>> from mindboggle.mio.plots import plot_volumes >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> label_file1 = fetch_data(urls['freesurfer_labels'], '', '.vtk') >>> label_file2 = fetch_data(urls['freesurfer_labels'], '', '.vtk') >>> volume_files = [label_file1, label_file2] >>> command = 'fslview' >>> command = '/Applications/ITK-SNAP.app/Contents/MacOS/InsightSNAP' >>> plot_volumes(volume_files, command=command) # doctest: +SKIP """ from mindboggle.guts.utilities import execute if isinstance(volume_files, str): volume_files = [volume_files] elif not isinstance(volume_files, list): raise IOError('plot_volumes() requires volume_files to be a list ' 'or string.') if not isinstance(command, str): raise IOError('plot_volumes() requires command to be a string.') else: command = [command] command.extend(volume_files) command.extend('&') execute(command, 'os')
def plot_volumes(volume_files, command='fslview'): """ Use fslview to visualize image volume data. Parameters ---------- volume_files : list of strings names of image volume files command : string plotting software command Examples -------- >>> import os >>> from mindboggle.mio.plots import plot_volumes >>> path = os.environ['MINDBOGGLE_DATA'] >>> volume_file1 = os.path.join(path, 'Twins-2-1', 'mri', 't1weighted.nii.gz') >>> volume_file2 = os.path.join(path, 'Twins-2-1', 'mri', 't1weighted_brain.nii.gz') >>> volume_files = [volume_file1, volume_file2] >>> command = 'fslview' >>> command = '/Applications/ITK-SNAP.app/Contents/MacOS/InsightSNAP' >>> plot_volumes(volume_files, command=command) """ from mindboggle.guts.utilities import execute if isinstance(volume_files, str): volume_files = [volume_files] elif not isinstance(volume_files, list): raise(IOError('plot_volumes() requires volume_files to be a list or string.')) if not isinstance(command, str): raise(IOError('plot_volumes() requires command to be a string.')) else: command = [command] command.extend(volume_files) command.extend('&') execute(command, 'os')
def ResampleImageBySpacing(volume, output_file='', outxspc=1, outyspc=1, outzspc=1, dosmooth=0, addvox=0, nninterp=1): """ Use the ResampleImageBySpacing function in ANTs to resample image volume. Usage: ResampleImageBySpacing ImageDimension ImageIn.ext outImage.ex outxspc outyspc <outzspc> <dosmooth?> <addvox> <nninterp?> Parameters ---------- volume : string nibabel-readable image volume output_file : string nibabel-readable image volume outxspc : integer output x-spacing outyspc : integer output y-spacing outzspc : integer output z-spacing dosmooth : bool smooth? addvox : integer pad each dimension by addvox nninterp : bool nearest-neighbor interpolation? Returns ------- output_file : string name of output nibabel-readable image volume Examples -------- >>> # Resample image so that 1mm voxels are 0.5mm voxels: >>> import os >>> from mindboggle.thirdparty.ants import ResampleImageBySpacing >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> volume = fetch_data(urls['T1_001'], '', '.nii.gz') >>> os.rename(volume, volume + '.nii.gz') >>> volume += '.nii.gz' >>> output_file = '' >>> outxspc = 1/2.0 >>> outyspc = 1/2.0 >>> outzspc = 1/2.0 >>> dosmooth = 0 >>> addvox = 0 >>> nninterp = 1 >>> output_file = ResampleImageBySpacing(volume, output_file, outxspc, ... outxspc, outzspc, dosmooth, addvox, nninterp) # doctest: +SKIP View result (skip test): >>> from mindboggle.mio.plots import plot_volumes >>> plot_volumes(output_file) # doctest: +SKIP """ import os from mindboggle.guts.utilities import execute if not output_file: output_file = os.path.join(os.getcwd(), 'resampled_' + os.path.basename(volume)) cmd = [ 'ResampleImageBySpacing', '3', volume, output_file, str(outxspc), str(outyspc), str(outzspc) ] execute(cmd, 'os') if not os.path.exists(output_file): raise IOError( "ResampleImageBySpacing did not create {0).".format(output_file)) return output_file
def PropagateLabelsThroughMask(mask, labels, mask_index=None, output_file='', binarize=True, stopvalue=''): """ Use ANTs to fill a binary volume mask with initial labels. This program uses ThresholdImage and the ImageMath PropagateLabelsThroughMask functions in ANTs. ThresholdImage ImageDimension ImageIn.ext outImage.ext threshlo threshhi <insideValue> <outsideValue> PropagateLabelsThroughMask: Final output is the propagated label image. ImageMath ImageDimension Out.ext PropagateLabelsThroughMask speed/binaryimagemask.nii.gz initiallabelimage.nii.gz ... Parameters ---------- mask : string nibabel-readable image volume labels : string nibabel-readable image volume with integer labels mask_index : integer (optional) mask with just voxels having this value output_file : string (optional) nibabel-readable labeled image volume binarize : bool (optional) binarize mask? stopvalue : integer (optional) stopping value Returns ------- output_file : string name of labeled output nibabel-readable image volume Examples -------- >>> # Propagate FreeSurfer labels through brain mask: >>> import os >>> from mindboggle.thirdparty.ants import PropagateLabelsThroughMask >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> labels = fetch_data(urls['freesurfer_labels'], '', '.nii.gz') >>> mask = fetch_data(urls['ants_mask'], '', '.nii.gz') >>> mask_index = None >>> output_file = '' >>> binarize = True >>> stopvalue = None >>> output_file = PropagateLabelsThroughMask(mask, labels, mask_index, ... output_file, binarize, stopvalue) # doctest: +SKIP View result (skip test): >>> from mindboggle.mio.plots import plot_volumes >>> plot_volumes(output_file) # doctest: +SKIP """ import os from mindboggle.guts.utilities import execute if not output_file: #output_file = os.path.join(os.getcwd(), # 'PropagateLabelsThroughMask.nii.gz') output_file = os.path.join( os.getcwd(), os.path.basename(labels) + '_through_' + os.path.basename(mask)) print('mask: {0}, labels: {1}'.format(mask, labels)) # Binarize image volume: if binarize: temp_file = os.path.join(os.getcwd(), 'PropagateLabelsThroughMask.nii.gz') cmd = ['ThresholdImage', '3', mask, temp_file, '0 1 0 1'] execute(cmd, 'os') mask = temp_file # Mask with just voxels having mask_index value: if mask_index: mask2 = os.path.join(os.getcwd(), 'temp.nii.gz') cmd = [ 'ThresholdImage', '3', mask, mask2, str(mask_index), str(mask_index) ] execute(cmd, 'os') else: mask2 = mask # Propagate labels: cmd = [ 'ImageMath', '3', output_file, 'PropagateLabelsThroughMask', mask2, labels ] if stopvalue: cmd.extend(str(stopvalue)) execute(cmd, 'os') if not os.path.exists(output_file): raise IOError("ImageMath did not create " + output_file + ".") return output_file
def antsApplyTransformsToPoints(points, transform_files, inverse_booleans=[0]): """ Run ANTs antsApplyTransformsToPoints function to transform points. (Creates pre- and post-transformed .csv points files for ANTs.) Parameters ---------- points : list of lists of three integers point coordinate data transform_files : list transform file names inverse_booleans : list for each transform, one to apply inverse of transform (otherwise zero) Returns ------- transformed_points : list of lists of three integers transformed point coordinate data Examples -------- >>> import numpy as np >>> from mindboggle.thirdparty.ants import antsApplyTransformsToPoints >>> from mindboggle.mio.vtks import read_points >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> xfm1 = fetch_data(urls['ants_affine_template2subject'], '', '.mat') >>> xfm2 = fetch_data(urls['ants_warp_template2subject'], '', '.nii.gz') >>> xfm3 = fetch_data(urls['OASIS-30_Atropos_template_to_MNI152_affine'], '', '.txt') >>> transform_files = [xfm1, xfm2, xfm3] >>> vtk_file = fetch_data(urls['left_pial'], '', '.vtk') >>> points = read_points(vtk_file) >>> inverse_booleans = [0,0,1] >>> transformed_points = antsApplyTransformsToPoints(points, ... transform_files, inverse_booleans) # doctest: +SKIP >>> print(np.array_str(np.array(transformed_points[0:5]), ... precision=5, suppress_small=True)) # doctest: +SKIP [[-11.23189 -46.78223 -39.88869] [-11.71384 -46.87075 -40.13328] [-12.56237 -46.99126 -40.04564] [ -9.66693 -46.0446 -41.36334] [-10.67998 -46.45458 -40.7572 ]] """ import os from io import open from mindboggle.guts.utilities import execute # ------------------------------------------------------------------------ # Write points (x,y,z,1) to a .csv file: # ------------------------------------------------------------------------ points_file = os.path.join(os.getcwd(), 'points.csv') fid = open(points_file, 'w') fid.write('x,y,z,t\n') fid.close() fid = open(points_file, 'a') for point in points: string_of_zeros = (4 - len(point)) * ',0' fid.write(','.join([str(x) for x in point]) + string_of_zeros + '\n') fid.close() # ------------------------------------------------------------------------ # Apply transforms to points in .csv file: # ------------------------------------------------------------------------ transformed_points_file = os.path.join(os.getcwd(), 'transformed_points.csv') transform_string = '' for ixfm, transform_file in enumerate(transform_files): transform_string += " --t [{0},{1}]".\ format(transform_file, str(inverse_booleans[ixfm])) cmd = [ 'antsApplyTransformsToPoints', '-d', '3', '-i', points_file, '-o', transformed_points_file, transform_string ] try: execute(cmd, 'os') except: raise Exception("Cannot find antsApplyTransformsToPoints command.") if not os.path.exists(transformed_points_file): raise IOError("antsApplyTransformsToPoints did not create {0}.".format( transformed_points_file)) # ------------------------------------------------------------------------ # Return transformed points: # ------------------------------------------------------------------------ fid = open(transformed_points_file, 'r') lines = fid.readlines() fid.close() transformed_points = [] for iline, line in enumerate(lines): if iline > 0: point_xyz1 = [float(x) for x in line.split(',')] transformed_points.append(point_xyz1[0:3]) return transformed_points
def PropagateLabelsThroughMask(mask, labels, mask_index=None, output_file='', binarize=True, stopvalue=''): """ Use ANTs to fill a binary volume mask with initial labels. This program uses ThresholdImage and the ImageMath PropagateLabelsThroughMask functions in ANTs. ThresholdImage ImageDimension ImageIn.ext outImage.ext threshlo threshhi <insideValue> <outsideValue> PropagateLabelsThroughMask: Final output is the propagated label image. ImageMath ImageDimension Out.ext PropagateLabelsThroughMask speed/binaryimagemask.nii.gz initiallabelimage.nii.gz ... Parameters ---------- mask : string nibabel-readable image volume labels : string nibabel-readable image volume with integer labels mask_index : integer (optional) mask with just voxels having this value output_file : string nibabel-readable labeled image volume binarize : Boolean binarize mask? stopvalue : integer stopping value Returns ------- output_file : string name of labeled output nibabel-readable image volume Examples -------- >>> import os >>> from mindboggle.thirdparty.ants import PropagateLabelsThroughMask >>> from mindboggle.mio.plots import plot_volumes >>> path = os.path.join(os.environ['MINDBOGGLE_DATA']) >>> labels = os.path.join(path, 'arno', 'labels', 'labels.DKT25.manual.nii.gz') >>> mask = os.path.join(path, 'arno', 'mri', 't1weighted_brain.nii.gz') >>> mask_index = None >>> output_file = '' >>> binarize = True >>> stopvalue = None >>> output_file = PropagateLabelsThroughMask(mask, labels, mask_index, output_file, binarize, stopvalue) >>> # View >>> plot_volumes(output_file) """ import os from mindboggle.guts.utilities import execute if not output_file: #output_file = os.path.join(os.getcwd(), # 'PropagateLabelsThroughMask.nii.gz') output_file = os.path.join( os.getcwd(), os.path.basename(labels) + '_through_' + os.path.basename(mask)) print('mask: {0}, labels: {1}'.format(mask, labels)) # Binarize image volume: if binarize: temp_file = os.path.join(os.getcwd(), 'PropagateLabelsThroughMask.nii.gz') cmd = ['ThresholdImage', '3', mask, temp_file, '0 1 0 1'] execute(cmd, 'os') mask = temp_file # Mask with just voxels having mask_index value: if mask_index: mask2 = os.path.join(os.getcwd(), 'temp.nii.gz') cmd = 'ThresholdImage 3 {0} {1} {2} {3} 1 0'.format( mask, mask2, mask_index, mask_index) execute(cmd) else: mask2 = mask # Propagate labels: cmd = [ 'ImageMath', '3', output_file, 'PropagateLabelsThroughMask', mask2, labels ] if stopvalue: cmd.extend(stopvalue) execute(cmd, 'os') if not os.path.exists(output_file): raise (IOError("ImageMath did not create " + output_file + ".")) return output_file
def ImageMath(volume1, volume2, operator='m', output_file=''): """ Use the ImageMath function in ANTs to perform operation on two volumes:: m : Multiply --- use vm for vector multiply + : Add --- use v+ for vector add - : Subtract --- use v- for vector subtract / : Divide ^ : Power exp : Take exponent exp(imagevalue*value) addtozero : add image-b to image-a only over points where image-a has zero values overadd : replace image-a pixel with image-b pixel if image-b pixel is non-zero abs : absolute value total : Sums up values in an image or in image1*image2 (img2 is the probability mask) mean : Average of values in an image or in image1*image2 (img2 is the probability mask) vtotal : Sums up volumetrically weighted values in an image or in image1*image2 (img2 is the probability mask) Decision : Computes result=1./(1.+exp(-1.0*( pix1-0.25)/pix2)) Neg : Produce image negative Parameters ---------- volume1 : string nibabel-readable image volume volume2 : string nibabel-readable image volume operator : string ImageMath string corresponding to mathematical operator output_file : string nibabel-readable image volume Returns ------- output_file : string name of output nibabel-readable image volume Examples -------- >>> # Mask head with brain mask: >>> import os >>> from mindboggle.thirdparty.ants import ImageMath >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> volume1 = fetch_data(urls['T1_001'], '', '.nii.gz') >>> volume2 = fetch_data(urls['ants_mask'], '', '.nii.gz') >>> operator = 'm' >>> output_file = '' >>> output_file = ImageMath(volume1, volume2, operator, output_file) # doctest: +SKIP View result (skip test): >>> from mindboggle.mio.plots import plot_volumes >>> plot_volumes(output_file) # doctest: +SKIP """ import os from mindboggle.guts.utilities import execute if not output_file: output_file = os.path.join( os.getcwd(), os.path.basename(volume1) + '_' + os.path.basename(volume2)) cmd = ['ImageMath', '3', output_file, operator, volume1, volume2] execute(cmd, 'os') if not os.path.exists(output_file): raise IOError("ImageMath did not create " + output_file + ".") return output_file
def ResampleImageBySpacing(volume, output_file='', outxspc=1, outyspc=1, outzspc=1, dosmooth=0, addvox=0, nninterp=1): """ Use the ResampleImageBySpacing function in ANTs to resample image volume. Usage: ResampleImageBySpacing ImageDimension ImageIn.ext outImage.ex outxspc outyspc <outzspc> <dosmooth?> <addvox> <nninterp?> Parameters ---------- volume : string nibabel-readable image volume output_file : string nibabel-readable image volume outxspc : integer output x-spacing outyspc : integer output y-spacing outzspc : integer output z-spacing dosmooth : bool smooth? addvox : integer pad each dimension by addvox nninterp : bool nearest-neighbor interpolation? Returns ------- output_file : string name of output nibabel-readable image volume Examples -------- >>> # Resample image so that 1mm voxels are 0.5mm voxels: >>> import os >>> from mindboggle.thirdparty.ants import ResampleImageBySpacing >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> volume = fetch_data(urls['T1_001']) >>> os.rename(volume, volume + '.nii.gz') >>> volume += '.nii.gz' >>> output_file = '' >>> outxspc = 1/2.0 >>> outyspc = 1/2.0 >>> outzspc = 1/2.0 >>> dosmooth = 0 >>> addvox = 0 >>> nninterp = 1 >>> output_file = ResampleImageBySpacing(volume, output_file, outxspc, ... outxspc, outzspc, dosmooth, addvox, nninterp) # doctest: +SKIP View result (skip test): >>> from mindboggle.mio.plots import plot_volumes >>> plot_volumes(output_file) # doctest: +SKIP """ import os from mindboggle.guts.utilities import execute if not output_file: output_file = os.path.join(os.getcwd(), 'resampled_' + os.path.basename(volume)) cmd = ['ResampleImageBySpacing', '3', volume, output_file, str(outxspc), str(outyspc), str(outzspc)] execute(cmd, 'os') if not os.path.exists(output_file): raise IOError("ResampleImageBySpacing did not create {0).". format(output_file)) return output_file
def plot_mask_surface(vtk_file, mask_file='', nonmask_value=-1, masked_output='', remove_nonmask=False, program='vtkviewer', use_colormap=False, colormap_file=''): """ Use vtkviewer or mayavi2 to visualize VTK surface mesh data. If a mask_file is provided, a temporary masked file is saved, and it is this file that is viewed. If using vtkviewer, can optionally provide colormap file or set $COLORMAP environment variable. Parameters ---------- vtk_file : string name of VTK surface mesh file mask_file : string name of VTK surface mesh file to mask vtk_file vertices nonmask_value : integer nonmask (usually background) value masked_output : string temporary masked output file name remove_nonmask : Boolean remove vertices that are not in mask? (otherwise assign nonmask_value) program : string {'vtkviewer', 'mayavi2'} program to visualize VTK file use_colormap : Boolean use Paraview-style XML colormap file set by $COLORMAP env variable? colormap_file : string use colormap in given file if use_colormap==True? if empty and use_colormap==True, use file set by $COLORMAP environment variable Examples -------- >>> import os >>> from mindboggle.mio.plots import plot_mask_surface >>> path = os.environ['MINDBOGGLE_DATA'] >>> vtk_file = os.path.join(path, 'arno', 'labels', 'lh.labels.DKT31.manual.vtk') >>> mask_file = os.path.join(path, 'test_one_label.vtk') >>> nonmask_value = 0 #-1 >>> masked_output = '' >>> remove_nonmask = True >>> program = 'vtkviewer' >>> use_colormap = True >>> colormap_file = '' #'/software/surface_cpp_tools/colormap.xml' >>> plot_mask_surface(vtk_file, mask_file, nonmask_value, masked_output, remove_nonmask, program, use_colormap, colormap_file) """ import os import numpy as np from mindboggle.guts.mesh import remove_faces, reindex_faces_points from mindboggle.guts.utilities import execute from mindboggle.mio.plots import plot_surfaces from mindboggle.mio.vtks import read_scalars, rewrite_scalars, \ read_vtk, write_vtk #------------------------------------------------------------------------- # Filter mesh with non-background values from a second (same-size) mesh: #------------------------------------------------------------------------- if mask_file: mask, name = read_scalars(mask_file, True, True) if not masked_output: masked_output = os.path.join(os.getcwd(), 'temp.vtk') file_to_plot = masked_output #--------------------------------------------------------------------- # Remove nonmask-valued vertices: #--------------------------------------------------------------------- if remove_nonmask: #----------------------------------------------------------------- # Load VTK files: #----------------------------------------------------------------- points, indices, lines, faces, scalars, scalar_names, npoints, \ input_vtk = read_vtk(vtk_file, True, True) #----------------------------------------------------------------- # Find mask indices, remove nonmask faces, and reindex: #----------------------------------------------------------------- Imask = [i for i,x in enumerate(mask) if x != nonmask_value] mask_faces = remove_faces(faces, Imask) mask_faces, points, \ original_indices = reindex_faces_points(mask_faces, points) #----------------------------------------------------------------- # Write VTK file with scalar values: #----------------------------------------------------------------- if np.ndim(scalars) == 1: scalar_type = type(scalars[0]).__name__ elif np.ndim(scalars) == 2: scalar_type = type(scalars[0][0]).__name__ else: print("Undefined scalar type!") write_vtk(file_to_plot, points, [], [], mask_faces, scalars[original_indices].tolist(), scalar_names, scalar_type=scalar_type) else: scalars, name = read_scalars(vtk_file, True, True) scalars[mask == nonmask_value] = nonmask_value rewrite_scalars(vtk_file, file_to_plot, scalars) else: file_to_plot = vtk_file #------------------------------------------------------------------------- # Display with vtkviewer.py: #------------------------------------------------------------------------- if program == 'vtkviewer': plot_surfaces(file_to_plot, use_colormap=use_colormap, colormap_file=colormap_file) #------------------------------------------------------------------------- # Display with mayavi2: #------------------------------------------------------------------------- elif program == 'mayavi2': cmd = ["mayavi2", "-d", file_to_plot, "-m", "Surface", "&"] execute(cmd, 'os')
def thickinthehead(segmented_file, labeled_file, cortex_value=2, noncortex_value=3, labels=[], names=[], resize=True, propagate=True, output_dir='', save_table=False, output_table=''): """ Compute a simple thickness measure for each labeled cortex region volume. Note:: - Cortex, noncortex, & label files are from the same coregistered brain. - Calls ANTs functions: ImageMath, Threshold, ResampleImageBySpacing - There may be slight discrepancies between volumes computed by thickinthehead() and volumes computed by volume_for_each_label(); in 31 of 600+ ADNI 1.5T images, some volume_for_each_label() volumes were slightly larger (in the third decimal place), presumably due to label propagation through the cortex in thickinthehead(). This is more pronounced in ANTs vs. FreeSurfer-labeled volumes. Example preprocessing steps :: 1. Run Freesurfer and antsCorticalThickness.sh on T1-weighted image. 2. Convert FreeSurfer volume labels (e.g., wmparc.mgz or aparc+aseg.mgz) to cortex (2) and noncortex (3) segments using relabel_volume() function [refer to LABELS.rst or FreeSurferColorLUT labels file]. 3. Convert ANTs Atropos-segmented volume (tmpBrainSegmentation.nii.gz) to cortex and noncortex segments, by converting 1-labels to 0 and 4-labels to 3 with the relabel_volume() function (the latter is to include deep-gray matter with noncortical tissues). 4. Combine FreeSurfer and ANTs segmentation volumes to obtain a single cortex (2) and noncortex (3) segmentation file using the function combine_2labels_in_2volumes(). This function takes the union of cortex voxels from the segmentations, the union of the noncortex voxels from the segmentations, and overwrites intersecting cortex and noncortex voxels with noncortex (3) labels. ANTs tends to include more cortical gray matter at the periphery of the brain than Freesurfer, and FreeSurfer tends to include more white matter that extends deep into gyral folds than ANTs, so the above attempts to remedy their differences by overlaying ANTs cortical gray with FreeSurfer white matter. 5. Optional, see Step 2 below: Fill segmented cortex with cortex labels and noncortex with noncortex labels using the PropagateLabelsThroughMask() function (which calls ImageMath ... PropagateLabelsThroughMask in ANTs). The labels can be initialized using FreeSurfer (e.g. wmparc.mgz) or ANTs (by applying the nonlinear inverse transform generated by antsCorticalThickness.sh to labels in the Atropos template space). [Note: Any further labeling steps may be applied, such as overwriting cerebrum with intersecting cerebellum labels.] Steps :: 1. Extract noncortex and cortex. 2. Either mask labels with cortex or fill cortex with labels. 3. Resample cortex and noncortex files from 1x1x1 to 0.5x0.5x0.5 to better represent the contours of the boundaries of the cortex. 4. Extract outer and inner boundary voxels of the cortex, by eroding 1 (resampled) voxel for cortex voxels (2) bordering the outside of the brain (0) and bordering noncortex (3). 5. Estimate middle cortical surface area by the average volume of the outer and inner boundary voxels of the cortex. 6. Compute the volume of a labeled region of cortex. 7. Estimate the thickness of the labeled cortical region as the volume of the labeled region (#6) divided by the surface area (#5). Parameters ---------- segmented_file : string image volume with cortex and noncortex (and any other) labels labeled_file : string corresponding image volume with index labels cortex_value : integer cortex label value in segmented_file noncortex_value : integer noncortex label value in segmented_file labels : list of integers label indices names : list of strings label names resize : Boolean resize (2x) segmented_file for more accurate thickness estimates? propagate : Boolean propagate labels through cortex? output_dir : string output directory save_table : Boolean save output table file with label volumes and thickness values? output_table : string name of output table file with label volumes and thickness values Returns ------- label_volume_thickness : list of lists of integers and floats label indices, volumes, and thickness values (default -1) output_table : string name of output table file with label volumes and thickness values Examples -------- >>> from mindboggle.shapes.volume_shapes import thickinthehead >>> segmented_file = '/Users/arno/Data/antsCorticalThickness/OASIS-TRT-20-1/antsBrainSegmentation.nii.gz' >>> labeled_file = '/appsdir/freesurfer/subjects/OASIS-TRT-20-1/mri/labels.DKT31.manual.nii.gz' >>> cortex_value = 2 >>> noncortex_value = 3 >>> #labels = [2] >>> labels = range(1002,1036) + range(2002,2036) >>> labels.remove(1004) >>> labels.remove(2004) >>> labels.remove(1032) >>> labels.remove(2032) >>> labels.remove(1033) >>> labels.remove(2033) >>> names = [] >>> resize = True >>> propagate = False >>> output_dir = '' >>> save_table = True >>> output_table = '' >>> label_volume_thickness, output_table = thickinthehead(segmented_file, labeled_file, cortex_value, noncortex_value, labels, names, resize, propagate, output_dir, save_table, output_table) """ import os import numpy as np import nibabel as nb from mindboggle.guts.utilities import execute #------------------------------------------------------------------------- # Output files: #------------------------------------------------------------------------- if output_dir: if not os.path.exists(output_dir): os.mkdir(output_dir) else: output_dir = os.getcwd() cortex = os.path.join(output_dir, 'cortex.nii.gz') noncortex = os.path.join(output_dir, 'noncortex.nii.gz') temp = os.path.join(output_dir, 'temp.nii.gz') inner_edge = os.path.join(output_dir, 'cortex_inner_edge.nii.gz') use_outer_edge = True if use_outer_edge: outer_edge = os.path.join(output_dir, 'cortex_outer_edge.nii.gz') if save_table: if output_table: output_table = os.path.join(os.getcwd(), output_table) else: output_table = os.path.join(os.getcwd(), 'thickinthehead_for_each_label.csv') fid = open(output_table, 'w') if names: fid.write("name, ID, thickness (thickinthehead)\n") else: fid.write("ID, thickness (thickinthehead)\n") else: output_table = '' #------------------------------------------------------------------------- # Extract noncortex and cortex: #------------------------------------------------------------------------- cmd = [ 'ThresholdImage 3', segmented_file, noncortex, str(noncortex_value), str(noncortex_value), '1 0' ] execute(cmd) cmd = [ 'ThresholdImage 3', segmented_file, cortex, str(cortex_value), str(cortex_value), '1 0' ] execute(cmd) #------------------------------------------------------------------------- # Either mask labels with cortex or fill cortex with labels: #------------------------------------------------------------------------- if propagate: cmd = [ 'ImageMath', '3', cortex, 'PropagateLabelsThroughMask', cortex, labeled_file ] execute(cmd) else: cmd = ['ImageMath 3', cortex, 'm', cortex, labeled_file] execute(cmd) #------------------------------------------------------------------------- # Load data and dimensions: #------------------------------------------------------------------------- if resize: rescale = 2.0 else: rescale = 1.0 compute_real_volume = True if compute_real_volume: img = nb.load(cortex) hdr = img.get_header() vv_orig = np.prod(hdr.get_zooms()) vv = np.prod([x / rescale for x in hdr.get_zooms()]) cortex_data = img.get_data().ravel() else: vv = 1 / rescale cortex_data = nb.load(cortex).get_data().ravel() #------------------------------------------------------------------------- # Resample cortex and noncortex files from 1x1x1 to 0.5x0.5x0.5 # to better represent the contours of the boundaries of the cortex: #------------------------------------------------------------------------- if resize: dims = ' '.join([str(1 / rescale), str(1 / rescale), str(1 / rescale)]) cmd = ['ResampleImageBySpacing 3', cortex, cortex, dims, '0 0 1'] execute(cmd) cmd = ['ResampleImageBySpacing 3', noncortex, noncortex, dims, '0 0 1'] execute(cmd) #------------------------------------------------------------------------- # Extract outer and inner boundary voxels of the cortex, # by eroding 1 (resampled) voxel for cortex voxels (2) bordering # the outside of the brain (0) and bordering noncortex (3): #------------------------------------------------------------------------- cmd = ['ImageMath 3', inner_edge, 'MD', noncortex, '1'] execute(cmd) cmd = ['ImageMath 3', inner_edge, 'm', cortex, inner_edge] execute(cmd) if use_outer_edge: cmd = ['ThresholdImage 3', cortex, outer_edge, '1 10000 1 0'] execute(cmd) cmd = ['ImageMath 3', outer_edge, 'ME', outer_edge, '1'] execute(cmd) cmd = ['ThresholdImage 3', outer_edge, outer_edge, '1 1 0 1'] execute(cmd) cmd = ['ImageMath 3', outer_edge, 'm', cortex, outer_edge] execute(cmd) cmd = ['ThresholdImage 3', inner_edge, temp, '1 10000 1 0'] execute(cmd) cmd = ['ThresholdImage 3', temp, temp, '1 1 0 1'] execute(cmd) cmd = ['ImageMath 3', outer_edge, 'm', temp, outer_edge] execute(cmd) #------------------------------------------------------------------------- # Load data: #------------------------------------------------------------------------- inner_edge_data = nb.load(inner_edge).get_data().ravel() if use_outer_edge: outer_edge_data = nb.load(outer_edge).get_data().ravel() #------------------------------------------------------------------------- # Loop through labels: #------------------------------------------------------------------------- if not labels: labeled_data = nb.load(labeled_file).get_data().ravel() labels = np.unique(labeled_data) labels = [int(x) for x in labels] label_volume_thickness = -1 * np.ones((len(labels), 3)) label_volume_thickness[:, 0] = labels for ilabel, label in enumerate(labels): if names: name = names[ilabel] #--------------------------------------------------------------------- # Compute thickness as a ratio of label volume and edge volume: # - Estimate middle cortical surface area by the average volume # of the outer and inner boundary voxels of the cortex. # - Compute the volume of a labeled region of cortex. # - Estimate the thickness of the labeled cortical region as the # volume of the labeled region divided by the surface area. #--------------------------------------------------------------------- label_cortex_volume = vv_orig * len(np.where(cortex_data == label)[0]) label_inner_edge_volume = vv * len( np.where(inner_edge_data == label)[0]) if label_inner_edge_volume: if use_outer_edge: label_outer_edge_volume = \ vv * len(np.where(outer_edge_data==label)[0]) label_area = (label_inner_edge_volume + label_outer_edge_volume) / 2.0 else: label_area = label_inner_edge_volume thickness = label_cortex_volume / label_area label_volume_thickness[ilabel, 1] = label_cortex_volume label_volume_thickness[ilabel, 2] = thickness #print('label {0} volume: cortex={1:2.2f}, inner={2:2.2f}, ' # 'outer={3:2.2f}, area51={4:2.2f}, thickness={5:2.2f}mm'. # format(name, label, label_cortex_volume, label_inner_edge_volume, # label_outer_edge_volume, label_area, thickness)) if names: print('{0} ({1}) thickness={2:2.2f}mm'.format( name, label, thickness)) else: print('{0}, thickness={1:2.2f}mm'.format(label, thickness)) if save_table: if names: fid.write('{0}, {1}, {2:2.3f}\n'.format( name, label, thickness)) else: fid.write('{0}, {1:2.3f}\n'.format(label, thickness)) label_volume_thickness = label_volume_thickness.transpose().tolist() return label_volume_thickness, output_table
def thickinthehead(segmented_file, labeled_file, cortex_value=2, noncortex_value=3, labels=[], names=[], propagate=False, output_dir='', save_table=False, output_table='', verbose=False): """ Compute a simple thickness measure for each labeled cortex region volume. Since Mindboggle accepts FreeSurfer data as input, we include FreeSurfer cortical thickness estimates with Mindboggle’s shape measures. However, surface mesh reconstruction from MRI data does not always produce favorable results. For example, we found that at least a quarter of the over one hundred EMBARC brain images we processed through FreeSurfer clipped ventral cortical regions, resulting in bad surface patches in those regions. For comparison, we built this function called thickinthehead which computes a simple thickness measure for each cortical region using a segmentation volume rather than surfaces. We have revised this algorithm from the original published version. We removed upsampling to reduce memory issues for large image volumes, and replaced the estimated volume of middle cortical layer with an estimate of its surface area. We made these revisions to be less susceptible to deviations in voxel size from isometric 1mm^3 voxels for which thickinthehead was originally built. Steps :: 1. Extract noncortex and cortex into separate files. 2. Either mask labels with cortex or fill cortex with labels. 3. Extract outer and inner boundary voxels of the cortex, by morphologically eroding the cortex (=2) by one voxel bordering the outside of the brain (=0) and bordering the inside of the brain (non-cortex=3). 4. Estimate middle cortical layer's surface area by the average surface area of the outer and inner boundary voxels of the cortex, where surface area is roughly estimated as the average face area of a voxel times the number of voxels. 5. Compute the volume of a labeled region of cortex. 6. Estimate the thickness of the labeled cortical region as the volume of the labeled region (#5) divided by the estimate of the middle cortical surface area of that region (#4). Note:: - Cortex, noncortex, & label files are from the same coregistered brain. - Calls ANTs functions: ImageMath and Threshold - There may be slight discrepancies between volumes computed by thickinthehead() and volumes computed by volume_per_label(); in 31 of 600+ ADNI 1.5T images, some volume_per_label() volumes were slightly larger (in the third decimal place), presumably due to label propagation through the cortex in thickinthehead(). This is more pronounced in ANTs vs. FreeSurfer-labeled volumes. Parameters ---------- segmented_file : string image volume with cortex and noncortex (and any other) labels labeled_file : string corresponding image volume with index labels cortex_value : integer cortex label value in segmented_file noncortex_value : integer noncortex label value in segmented_file labels : list of integers label indices names : list of strings label names propagate : bool propagate labels through cortex (or mask labels with cortex)? output_dir : string output directory save_table : bool save output table file with label volumes and thickness values? output_table : string name of output table file with label volumes and thickness values verbose : bool print statements? Returns ------- label_volume_thickness : list of lists of integers and floats label indices, volumes, and thickness values (default -1) output_table : string name of output table file with label volumes and thickness values Examples -------- >>> # Example simply using ants segmentation and labels vs. hybrid segmentation: >>> import os >>> import numpy as np >>> from mindboggle.shapes.volume_shapes import thickinthehead >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> segmented_file = fetch_data(urls['ants_segmentation'], '', '.nii.gz') >>> labeled_file = fetch_data(urls['ants_labels'], '', '.nii.gz') >>> cortex_value = 2 >>> noncortex_value = 3 >>> #labels = [2] >>> labels = list(range(1002,1036)) + list(range(2002,2036)) >>> labels.remove(1004) >>> labels.remove(2004) >>> labels.remove(1032) >>> labels.remove(2032) >>> labels.remove(1033) >>> labels.remove(2033) >>> names = [] >>> propagate = False >>> output_dir = '' >>> save_table = True >>> output_table = '' >>> verbose = False Skip online test because it requires installation of ANTs: >>> label_volume_thickness, output_table = thickinthehead(segmented_file, ... labeled_file, cortex_value, noncortex_value, labels, names, ... propagate, output_dir, save_table, output_table, verbose) # doctest: +SKIP >>> [np.int("{0:.{1}f}".format(x, 5)) label_volume_thickness[0][0:10]] # doctest: +SKIP >>> [np.float("{0:.{1}f}".format(x, 5)) for x in label_volume_thickness[1][0:5]] # doctest: +SKIP >>> [np.float("{0:.{1}f}".format(x, 5)) for x in label_volume_thickness[2][0:5]] # doctest: +SKIP """ import os import numpy as np import nibabel as nb from io import open from mindboggle.guts.utilities import execute # ------------------------------------------------------------------------ # Output files: # ------------------------------------------------------------------------ if output_dir: if not os.path.exists(output_dir): os.mkdir(output_dir) else: output_dir = os.getcwd() cortex = os.path.join(output_dir, 'cortex.nii.gz') noncortex = os.path.join(output_dir, 'noncortex.nii.gz') temp = os.path.join(output_dir, 'temp.nii.gz') inner_edge = os.path.join(output_dir, 'cortex_inner_edge.nii.gz') use_outer_edge = True if use_outer_edge: outer_edge = os.path.join(output_dir, 'cortex_outer_edge.nii.gz') if save_table: if output_table: output_table = os.path.join(os.getcwd(), output_table) else: output_table = os.path.join(os.getcwd(), 'thickinthehead_for_each_label.csv') fid = open(output_table, 'w') if names: fid.write("name, ID, thickness (thickinthehead)\n") else: fid.write("ID, thickness (thickinthehead)\n") else: output_table = '' # ------------------------------------------------------------------------ # Extract noncortex and cortex: # ------------------------------------------------------------------------ cmd = ['ThresholdImage', '3', segmented_file, noncortex, str(noncortex_value), str(noncortex_value), '1 0'] execute(cmd, 'os') cmd = ['ThresholdImage', '3', segmented_file, cortex, str(cortex_value), str(cortex_value), '1 0'] execute(cmd, 'os') # ------------------------------------------------------------------------ # Either mask labels with cortex or fill cortex with labels: # ------------------------------------------------------------------------ if propagate: cmd = ['ImageMath', '3', cortex, 'PropagateLabelsThroughMask', cortex, labeled_file] execute(cmd, 'os') else: cmd = ['ImageMath', '3', cortex, 'm', cortex, labeled_file] execute(cmd, 'os') # ------------------------------------------------------------------------ # Load data and dimensions: # ------------------------------------------------------------------------ img = nb.load(cortex) cortex_data = img.get_data().ravel() voxsize = img.header.get_zooms() voxvol = np.prod(voxsize) voxarea = (voxsize[0] * voxsize[1] + \ voxsize[0] * voxsize[2] + \ voxsize[1] * voxsize[2]) / 3 # ------------------------------------------------------------------------ # Extract outer and inner boundary voxels of the cortex, # by eroding 1 voxel for cortex voxels (=2) bordering # the outside of the brain (=0) and bordering noncortex (=3): # ------------------------------------------------------------------------ cmd = ['ImageMath', '3', inner_edge, 'MD', noncortex, '1'] execute(cmd, 'os') cmd = ['ImageMath', '3', inner_edge, 'm', cortex, inner_edge] execute(cmd, 'os') if use_outer_edge: cmd = ['ThresholdImage', '3', cortex, outer_edge, '1 10000 1 0'] execute(cmd, 'os') cmd = ['ImageMath', '3', outer_edge, 'ME', outer_edge, '1'] execute(cmd, 'os') cmd = ['ThresholdImage', '3', outer_edge, outer_edge, '1 1 0 1'] execute(cmd, 'os') cmd = ['ImageMath', '3', outer_edge, 'm', cortex, outer_edge] execute(cmd, 'os') cmd = ['ThresholdImage', '3', inner_edge, temp, '1 10000 1 0'] execute(cmd, 'os') cmd = ['ThresholdImage', '3', temp, temp, '1 1 0 1'] execute(cmd, 'os') cmd = ['ImageMath', '3', outer_edge, 'm', temp, outer_edge] execute(cmd, 'os') # ------------------------------------------------------------------------ # Load data: # ------------------------------------------------------------------------ inner_edge_data = nb.load(inner_edge).get_data().ravel() if use_outer_edge: outer_edge_data = nb.load(outer_edge).get_data().ravel() # ------------------------------------------------------------------------ # Loop through labels: # ------------------------------------------------------------------------ if not labels: labeled_data = nb.load(labeled_file).get_data().ravel() labels = np.unique(labeled_data) labels = [int(x) for x in labels] label_volume_thickness = -1 * np.ones((len(labels), 3)) label_volume_thickness[:, 0] = labels for ilabel, label in enumerate(labels): if names: name = names[ilabel] # -------------------------------------------------------------------- # Compute thickness as a ratio of label volume and layer surface area: # - Estimate middle cortical surface area by the average area # of the outer and inner boundary voxels of the cortex. # - Surface area is roughly estimated as the average face area # of a voxel times the number of voxels. # - Compute the volume of a labeled region of cortex. # - Estimate the thickness of the labeled cortical region as the # volume of the labeled region divided by the middle surface area. # -------------------------------------------------------------------- label_cortex_volume = voxvol * len(np.where(cortex_data==label)[0]) label_inner_edge_area = voxarea * \ len(np.where(inner_edge_data==label)[0]) if label_inner_edge_area: if use_outer_edge: label_outer_edge_area = \ voxarea * len(np.where(outer_edge_data==label)[0]) label_area = (label_inner_edge_area + label_outer_edge_area) / 2.0 else: label_area = label_inner_edge_area thickness = label_cortex_volume / label_area label_volume_thickness[ilabel, 1] = label_cortex_volume label_volume_thickness[ilabel, 2] = thickness if save_table: if names: if verbose: print('{0} ({1}) thickinthehead thickness = ' '{2:2.2f}mm'.format(name, label, thickness)) fid.write('{0}, {1}, {2:2.3f}\n'.format(name, label, thickness)) else: if verbose: print('{0} thickinthehead thickness = {1:2.2f}mm'. format(label, thickness)) fid.write('{0}, {1:2.3f}\n'.format(label, thickness)) label_volume_thickness = label_volume_thickness.transpose().tolist() return label_volume_thickness, output_table
def ImageMath(volume1, volume2, operator='m', output_file=''): """ Use the ImageMath function in ANTs to perform operation on two volumes:: m : Multiply --- use vm for vector multiply + : Add --- use v+ for vector add - : Subtract --- use v- for vector subtract / : Divide ^ : Power exp : Take exponent exp(imagevalue*value) addtozero : add image-b to image-a only over points where image-a has zero values overadd : replace image-a pixel with image-b pixel if image-b pixel is non-zero abs : absolute value total : Sums up values in an image or in image1*image2 (img2 is the probability mask) mean : Average of values in an image or in image1*image2 (img2 is the probability mask) vtotal : Sums up volumetrically weighted values in an image or in image1*image2 (img2 is the probability mask) Decision : Computes result=1./(1.+exp(-1.0*( pix1-0.25)/pix2)) Neg : Produce image negative Parameters ---------- volume1 : string nibabel-readable image volume volume2 : string nibabel-readable image volume operator : string ImageMath string corresponding to mathematical operator output_file : string nibabel-readable image volume Returns ------- output_file : string name of output nibabel-readable image volume Examples -------- >>> # Mask head with brain mask: >>> import os >>> from mindboggle.thirdparty.ants import ImageMath >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> volume1 = fetch_data(urls['T1_001']) >>> volume2 = fetch_data(urls['ants_mask']) >>> operator = 'm' >>> output_file = '' >>> output_file = ImageMath(volume1, volume2, operator, output_file) # doctest: +SKIP View result (skip test): >>> from mindboggle.mio.plots import plot_volumes >>> plot_volumes(output_file) # doctest: +SKIP """ import os from mindboggle.guts.utilities import execute if not output_file: output_file = os.path.join(os.getcwd(), os.path.basename(volume1) + '_' + os.path.basename(volume2)) cmd = ['ImageMath', '3', output_file, operator, volume1, volume2] execute(cmd, 'os') if not os.path.exists(output_file): raise IOError("ImageMath did not create " + output_file + ".") return output_file
def antsApplyTransformsToPoints(points, transform_files, inverse_booleans=[0]): """ Run ANTs antsApplyTransformsToPoints function to transform points. (Creates pre- and post-transformed .csv points files for ANTs.) Parameters ---------- points : list of lists of three integers point coordinate data transform_files : list transform file names inverse_booleans : list for each transform, one to apply inverse of transform (otherwise zero) Returns ------- transformed_points : list of lists of three integers transformed point coordinate data Examples -------- >>> from mindboggle.thirdparty.ants import antsApplyTransformsToPoints >>> from mindboggle.mio.vtks import read_vtk >>> transform_files = ['/Users/arno/Data/antsCorticalThickness/Twins-2-1/antsTemplateToSubject1GenericAffine.mat','/Users/arno/Data/antsCorticalThickness/Twins-2-1/antsTemplateToSubject0Warp.nii.gz','/Users/arno/Data/antsCorticalThickness/Twins-2-1/antsSubjectToTemplate0GenericAffine.mat','/Users/arno/Data/antsCorticalThickness/Twins-2-1/antsSubjectToTemplate1Warp.nii.gz'] >>> transform_files = [transform_files[0],transform_files[1],'/Users/arno/Data/mindboggle_cache/f36e3d5d99f7c4a9bb70e2494ed7340b/OASIS-30_Atropos_template_to_MNI152_affine.txt'] >>> vtk_file = '/Users/arno/mindboggle_working/Twins-2-1/Mindboggle/_hemi_lh/Surface_to_vtk/lh.pial.vtk' >>> points, indices, lines, faces, scalars, scalar_names, npoints, input_vtk = read_vtk(vtk_file) >>> inverse_booleans = [0,0,1] >>> transformed_points = antsApplyTransformsToPoints(points, transform_files, inverse_booleans) """ import os from mindboggle.guts.utilities import execute #------------------------------------------------------------------------- # Write points (x,y,z,1) to a .csv file: #------------------------------------------------------------------------- points_file = os.path.join(os.getcwd(), 'points.csv') fid = open(points_file, 'wa') fid.write('x,y,z,t\n') for point in points: string_of_zeros = (4 - len(point)) * ',0' fid.write(','.join([str(x) for x in point]) + string_of_zeros + '\n') fid.close() #------------------------------------------------------------------------- # Apply transforms to points in .csv file: #------------------------------------------------------------------------- transformed_points_file = os.path.join(os.getcwd(), 'transformed_points.csv') transform_string = '' for ixfm, transform_file in enumerate(transform_files): transform_string += " --t [{0},{1}]".\ format(transform_file, str(inverse_booleans[ixfm])) cmd = ['antsApplyTransformsToPoints', '-d', '3', '-i', points_file, '-o', transformed_points_file, transform_string] execute(cmd, 'os') if not os.path.exists(transformed_points_file): str1 = "antsApplyTransformsToPoints did not create " raise(IOError(str1 + transformed_points_file + ".")) #------------------------------------------------------------------------- # Return transformed points: #------------------------------------------------------------------------- fid = open(transformed_points_file, 'r') lines = fid.readlines() fid.close() transformed_points = [] for iline, line in enumerate(lines): if iline > 0: point_xyz1 = [float(x) for x in line.split(',')] transformed_points.append(point_xyz1[0:3]) return transformed_points
def PropagateLabelsThroughMask(mask, labels, mask_index=None, output_file='', binarize=True, stopvalue=''): """ Use ANTs to fill a binary volume mask with initial labels. This program uses ThresholdImage and the ImageMath PropagateLabelsThroughMask functions in ANTs. ThresholdImage ImageDimension ImageIn.ext outImage.ext threshlo threshhi <insideValue> <outsideValue> PropagateLabelsThroughMask: Final output is the propagated label image. ImageMath ImageDimension Out.ext PropagateLabelsThroughMask speed/binaryimagemask.nii.gz initiallabelimage.nii.gz ... Parameters ---------- mask : string nibabel-readable image volume labels : string nibabel-readable image volume with integer labels mask_index : integer (optional) mask with just voxels having this value output_file : string nibabel-readable labeled image volume binarize : Boolean binarize mask? stopvalue : integer stopping value Returns ------- output_file : string name of labeled output nibabel-readable image volume Examples -------- >>> import os >>> from mindboggle.thirdparty.ants import PropagateLabelsThroughMask >>> from mindboggle.mio.plots import plot_volumes >>> path = os.path.join(os.environ['MINDBOGGLE_DATA']) >>> labels = os.path.join(path, 'arno', 'labels', 'labels.DKT25.manual.nii.gz') >>> mask = os.path.join(path, 'arno', 'mri', 't1weighted_brain.nii.gz') >>> mask_index = None >>> output_file = '' >>> binarize = True >>> stopvalue = None >>> output_file = PropagateLabelsThroughMask(mask, labels, mask_index, output_file, binarize, stopvalue) >>> # View >>> plot_volumes(output_file) """ import os from mindboggle.guts.utilities import execute if not output_file: #output_file = os.path.join(os.getcwd(), # 'PropagateLabelsThroughMask.nii.gz') output_file = os.path.join(os.getcwd(), os.path.basename(labels) + '_through_' + os.path.basename(mask)) print('mask: {0}, labels: {1}'.format(mask, labels)) # Binarize image volume: if binarize: temp_file = os.path.join(os.getcwd(), 'PropagateLabelsThroughMask.nii.gz') cmd = ['ThresholdImage', '3', mask, temp_file, '0 1 0 1'] execute(cmd, 'os') mask = temp_file # Mask with just voxels having mask_index value: if mask_index: mask2 = os.path.join(os.getcwd(), 'temp.nii.gz') cmd = 'ThresholdImage 3 {0} {1} {2} {3} 1 0'.format(mask, mask2, mask_index, mask_index) execute(cmd) else: mask2 = mask # Propagate labels: cmd = ['ImageMath', '3', output_file, 'PropagateLabelsThroughMask', mask2, labels] if stopvalue: cmd.extend(stopvalue) execute(cmd, 'os') if not os.path.exists(output_file): raise(IOError("ImageMath did not create " + output_file + ".")) return output_file
def thickinthehead(segmented_file, labeled_file, cortex_value=2, noncortex_value=3, labels=[], names=[], resize=True, propagate=True, output_dir='', save_table=False, output_table='', verbose=False): """ Compute a simple thickness measure for each labeled cortex region volume. Since Mindboggle accepts FreeSurfer data as input, we include FreeSurfer cortical thickness estimates with Mindboggle’s shape measures. However, surface mesh reconstruction from MRI data does not always produce favorable results. For example, we found that at least a quarter of the over one hundred EMBARC brain images we processed through FreeSurfer clipped ventral cortical regions, resulting in bad surface patches in those regions. For comparison, we built a function called thickinthehead which computes a simple thickness measure for each cortical region using the hybrid segmentation volume rather than surfaces. The thickinthehead function first saves a brain volume that has been segmented into cortex and non-cortex voxels into separate binary files, then resamples these cortex and non-cortex files from, for example, 1mm^3 to 0.5mm^3 voxel dimensions to better represent the contours of the cortex, then extracts outer and inner boundary voxels of the cortex by morphologically eroding the cortex by one (resampled) voxel bordering the outside of the brain and bordering the inside of the brain (non-cortex). Then it estimates the middle cortical surface area by the average volume of the outer and inner boundary voxels of the cortex. Finally, it estimates the thickness of a labeled cortical region as the volume of the labeled region divided by the surface area of that region. We compared thickinthehead and FreeSurfer cortical thickness estimates for 16 cortical regions in 40 EMBARC control subjects (unpublished results) with published estimates based on manual delineations of MR images (Kabani, 2001). Forty percent of FreeSurfer estimates for the 640 labels were in the range of the published values, whereas almost ninety percent of thickinthehead’s estimates were within range. ANTs values deviated further from the published estimates and were less reliable (greater inter-subject ranges) than the FreeSurfer or thickinthehead values. Note:: - Cortex, noncortex, & label files are from the same coregistered brain. - Calls ANTs functions: ImageMath, Threshold, ResampleImageBySpacing - There may be slight discrepancies between volumes computed by thickinthehead() and volumes computed by volume_per_label(); in 31 of 600+ ADNI 1.5T images, some volume_per_label() volumes were slightly larger (in the third decimal place), presumably due to label propagation through the cortex in thickinthehead(). This is more pronounced in ANTs vs. FreeSurfer-labeled volumes. Example preprocessing steps :: 1. Run Freesurfer and antsCorticalThickness.sh on T1-weighted image. 2. Convert FreeSurfer volume labels (e.g., wmparc.mgz or aparc+aseg.mgz) to cortex (2) and noncortex (3) segments using relabel_volume() function [refer to labels.rst or FreeSurferColorLUT labels file]. 3. Convert ANTs Atropos-segmented volume (tmpBrainSegmentation.nii.gz) to cortex and noncortex segments, by converting 1-labels to 0 and 4-labels to 3 with the relabel_volume() function (the latter is to include deep-gray matter with noncortical tissues). 4. Combine FreeSurfer and ANTs segmentation volumes to obtain a single cortex (2) and noncortex (3) segmentation file using the function combine_2labels_in_2volumes(). This function takes the union of cortex voxels from the segmentations, the union of the noncortex voxels from the segmentations, and overwrites intersecting cortex and noncortex voxels with noncortex (3) labels. ANTs tends to include more cortical gray matter at the periphery of the brain than Freesurfer, and FreeSurfer tends to include more white matter that extends deep into gyral folds than ANTs, so the above attempts to remedy their differences by overlaying ANTs cortical gray with FreeSurfer white matter. 5. Optional, see Step 2 below: Fill segmented cortex with cortex labels and noncortex with noncortex labels using the PropagateLabelsThroughMask() function (which calls ImageMath ... PropagateLabelsThroughMask in ANTs). The labels can be initialized using FreeSurfer (e.g. wmparc.mgz) or ANTs (by applying the nonlinear inverse transform generated by antsCorticalThickness.sh to labels in the Atropos template space). [Note: Any further labeling steps may be applied, such as overwriting cerebrum with intersecting cerebellum labels.] Steps :: 1. Extract noncortex and cortex. 2. Either mask labels with cortex or fill cortex with labels. 3. Resample cortex and noncortex files from 1x1x1 to 0.5x0.5x0.5 to better represent the contours of the boundaries of the cortex. 4. Extract outer and inner boundary voxels of the cortex, by eroding 1 (resampled) voxel for cortex voxels (2) bordering the outside of the brain (0) and bordering noncortex (3). 5. Estimate middle cortical surface area by the average volume of the outer and inner boundary voxels of the cortex. 6. Compute the volume of a labeled region of cortex. 7. Estimate the thickness of the labeled cortical region as the volume of the labeled region (#6) divided by the surface area (#5). Parameters ---------- segmented_file : string image volume with cortex and noncortex (and any other) labels labeled_file : string corresponding image volume with index labels cortex_value : integer cortex label value in segmented_file noncortex_value : integer noncortex label value in segmented_file labels : list of integers label indices names : list of strings label names resize : bool resize (2x) segmented_file for more accurate thickness estimates? propagate : bool propagate labels through cortex? output_dir : string output directory save_table : bool save output table file with label volumes and thickness values? output_table : string name of output table file with label volumes and thickness values verbose : bool print statements? Returns ------- label_volume_thickness : list of lists of integers and floats label indices, volumes, and thickness values (default -1) output_table : string name of output table file with label volumes and thickness values Examples -------- >>> # Example simply using ants segmentation and labels: >>> import os >>> import numpy as np >>> from mindboggle.shapes.volume_shapes import thickinthehead >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> segmented_file = fetch_data(urls['ants_segmentation'], '', '.nii.gz') >>> labeled_file = fetch_data(urls['ants_labels'], '', '.nii.gz') >>> cortex_value = 2 >>> noncortex_value = 3 >>> #labels = [2] >>> labels = list(range(1002,1036)) + list(range(2002,2036)) >>> labels.remove(1004) >>> labels.remove(2004) >>> labels.remove(1032) >>> labels.remove(2032) >>> labels.remove(1033) >>> labels.remove(2033) >>> names = [] >>> resize = True >>> propagate = False >>> output_dir = '' >>> save_table = True >>> output_table = '' >>> verbose = False Skip online test because it requires installation of ANTs: >>> label_volume_thickness, output_table = thickinthehead(segmented_file, ... labeled_file, cortex_value, noncortex_value, labels, names, ... resize, propagate, output_dir, save_table, output_table, verbose) # doctest: +SKIP >>> [np.int("{0:.{1}f}".format(x, 5)) label_volume_thickness[0][0:10]] # doctest: +SKIP [1002, 1003, 1005, 1006, 1007, 1008, 1009, 1010, 1011 1012] >>> [np.float("{0:.{1}f}".format(x, 5)) for x in label_volume_thickness[1][0:5]] # doctest: +SKIP [3136.99383, 7206.98582, 3257.99359, 1950.99616, 12458.97549] >>> [np.float("{0:.{1}f}".format(x, 5)) for x in label_volume_thickness[2][0:5]] # doctest: +SKIP [3.8639, 3.69637, 2.56334, 4.09336, 4.52592] """ import os import numpy as np import nibabel as nb from io import open from mindboggle.guts.utilities import execute # ------------------------------------------------------------------------ # Output files: # ------------------------------------------------------------------------ if output_dir: if not os.path.exists(output_dir): os.mkdir(output_dir) else: output_dir = os.getcwd() cortex = os.path.join(output_dir, 'cortex.nii.gz') noncortex = os.path.join(output_dir, 'noncortex.nii.gz') temp = os.path.join(output_dir, 'temp.nii.gz') inner_edge = os.path.join(output_dir, 'cortex_inner_edge.nii.gz') use_outer_edge = True if use_outer_edge: outer_edge = os.path.join(output_dir, 'cortex_outer_edge.nii.gz') if save_table: if output_table: output_table = os.path.join(os.getcwd(), output_table) else: output_table = os.path.join(os.getcwd(), 'thickinthehead_for_each_label.csv') fid = open(output_table, 'w') if names: fid.write("name, ID, thickness (thickinthehead)\n") else: fid.write("ID, thickness (thickinthehead)\n") else: output_table = '' # ------------------------------------------------------------------------ # ants command paths: # ------------------------------------------------------------------------ ants_thresh = 'ThresholdImage' ants_math = 'ImageMath' ants_resample = 'ResampleImageBySpacing' # ------------------------------------------------------------------------ # Extract noncortex and cortex: # ------------------------------------------------------------------------ cmd = [ ants_thresh, '3', segmented_file, noncortex, str(noncortex_value), str(noncortex_value), '1 0' ] execute(cmd, 'os') cmd = [ ants_thresh, '3', segmented_file, cortex, str(cortex_value), str(cortex_value), '1 0' ] execute(cmd, 'os') # ------------------------------------------------------------------------ # Either mask labels with cortex or fill cortex with labels: # ------------------------------------------------------------------------ if propagate: cmd = [ ants_math, '3', cortex, 'PropagateLabelsThroughMask', cortex, labeled_file ] execute(cmd, 'os') else: cmd = [ants_math, '3', cortex, 'm', cortex, labeled_file] execute(cmd, 'os') # ------------------------------------------------------------------------ # Load data and dimensions: # ------------------------------------------------------------------------ if resize: rescale = 2.0 else: rescale = 1.0 compute_real_volume = True if compute_real_volume: img = nb.load(cortex) hdr = img.get_header() vv_orig = np.prod(hdr.get_zooms()) vv = np.prod([x / rescale for x in hdr.get_zooms()]) cortex_data = img.get_data().ravel() else: vv = 1 / rescale cortex_data = nb.load(cortex).get_data().ravel() # ------------------------------------------------------------------------ # Resample cortex and noncortex files from 1x1x1 to 0.5x0.5x0.5 # to better represent the contours of the boundaries of the cortex: # ------------------------------------------------------------------------ if resize: dims = ' '.join([str(1 / rescale), str(1 / rescale), str(1 / rescale)]) cmd = [ants_resample, '3', cortex, cortex, dims, '0 0 1'] execute(cmd, 'os') cmd = [ants_resample, '3', noncortex, noncortex, dims, '0 0 1'] execute(cmd, 'os') # ------------------------------------------------------------------------ # Extract outer and inner boundary voxels of the cortex, # by eroding 1 (resampled) voxel for cortex voxels (2) bordering # the outside of the brain (0) and bordering noncortex (3): # ------------------------------------------------------------------------ cmd = [ants_math, '3', inner_edge, 'MD', noncortex, '1'] execute(cmd, 'os') cmd = [ants_math, '3', inner_edge, 'm', cortex, inner_edge] execute(cmd, 'os') if use_outer_edge: cmd = [ants_thresh, '3', cortex, outer_edge, '1 10000 1 0'] execute(cmd, 'os') cmd = [ants_math, '3', outer_edge, 'ME', outer_edge, '1'] execute(cmd, 'os') cmd = [ants_thresh, '3', outer_edge, outer_edge, '1 1 0 1'] execute(cmd, 'os') cmd = [ants_math, '3', outer_edge, 'm', cortex, outer_edge] execute(cmd, 'os') cmd = [ants_thresh, '3', inner_edge, temp, '1 10000 1 0'] execute(cmd, 'os') cmd = [ants_thresh, '3', temp, temp, '1 1 0 1'] execute(cmd, 'os') cmd = [ants_math, '3', outer_edge, 'm', temp, outer_edge] execute(cmd, 'os') # ------------------------------------------------------------------------ # Load data: # ------------------------------------------------------------------------ inner_edge_data = nb.load(inner_edge).get_data().ravel() if use_outer_edge: outer_edge_data = nb.load(outer_edge).get_data().ravel() # ------------------------------------------------------------------------ # Loop through labels: # ------------------------------------------------------------------------ if not labels: labeled_data = nb.load(labeled_file).get_data().ravel() labels = np.unique(labeled_data) labels = [int(x) for x in labels] label_volume_thickness = -1 * np.ones((len(labels), 3)) label_volume_thickness[:, 0] = labels for ilabel, label in enumerate(labels): if names: name = names[ilabel] # -------------------------------------------------------------------- # Compute thickness as a ratio of label volume and edge volume: # - Estimate middle cortical surface area by the average volume # of the outer and inner boundary voxels of the cortex. # - Compute the volume of a labeled region of cortex. # - Estimate the thickness of the labeled cortical region as the # volume of the labeled region divided by the surface area. # -------------------------------------------------------------------- label_cortex_volume = vv_orig * len(np.where(cortex_data == label)[0]) label_inner_edge_volume = vv * len( np.where(inner_edge_data == label)[0]) if label_inner_edge_volume: if use_outer_edge: label_outer_edge_volume = \ vv * len(np.where(outer_edge_data==label)[0]) label_area = (label_inner_edge_volume + label_outer_edge_volume) / 2.0 else: label_area = label_inner_edge_volume thickness = label_cortex_volume / label_area label_volume_thickness[ilabel, 1] = label_cortex_volume label_volume_thickness[ilabel, 2] = thickness if save_table: if names: if verbose: print('{0} ({1}) thickinthehead thickness = ' '{2:2.2f}mm'.format(name, label, thickness)) fid.write('{0}, {1}, {2:2.3f}\n'.format( name, label, thickness)) else: if verbose: print( '{0} thickinthehead thickness = {1:2.2f}mm'.format( label, thickness)) fid.write('{0}, {1:2.3f}\n'.format(label, thickness)) label_volume_thickness = label_volume_thickness.transpose().tolist() return label_volume_thickness, output_table
def plot_mask_surface(vtk_file, mask_file='', nonmask_value=-1, masked_output='', remove_nonmask=False, program='vtkviewer', use_colormap=False, colormap_file='', background_value=-1): """ Use vtkviewer or mayavi2 to visualize VTK surface mesh data. If a mask_file is provided, a temporary masked file is saved, and it is this file that is viewed. If using vtkviewer, optionally provide colormap file or set $COLORMAP environment variable. Parameters ---------- vtk_file : string name of VTK surface mesh file mask_file : string name of VTK surface mesh file to mask vtk_file vertices nonmask_value : integer nonmask (usually background) value masked_output : string temporary masked output file name remove_nonmask : bool remove vertices that are not in mask? (otherwise assign nonmask_value) program : string {'vtkviewer', 'mayavi2'} program to visualize VTK file use_colormap : bool use Paraview-style XML colormap file set by $COLORMAP env variable? colormap_file : string use colormap in given file if use_colormap==True? if empty and use_colormap==True, use file set by $COLORMAP environment variable background_value : integer or float background value Examples -------- >>> import os >>> from mindboggle.mio.plots import plot_mask_surface >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> vtk_file = fetch_data(urls['freesurfer_labels'], '', '.vtk') >>> os.rename(vtk_file, vtk_file + '.nii.gz') >>> vtk_file = vtk_file + '.nii.gz' >>> mask_file = '' >>> nonmask_value = 0 #-1 >>> masked_output = '' >>> remove_nonmask = True >>> program = 'vtkviewer' >>> use_colormap = True >>> colormap_file = '' >>> background_value = -1 >>> plot_mask_surface(vtk_file, mask_file, nonmask_value, masked_output, ... remove_nonmask, program, use_colormap, colormap_file, ... background_value) # doctest: +SKIP """ import os import numpy as np from mindboggle.guts.mesh import keep_faces, reindex_faces_points from mindboggle.guts.utilities import execute from mindboggle.mio.plots import plot_surfaces from mindboggle.mio.vtks import read_scalars, rewrite_scalars, \ read_vtk, write_vtk # ------------------------------------------------------------------------ # Filter mesh with non-background values from a second (same-size) mesh: # ------------------------------------------------------------------------ if mask_file: mask, name = read_scalars(mask_file, True, True) if not masked_output: masked_output = os.path.join(os.getcwd(), 'temp.vtk') file_to_plot = masked_output # -------------------------------------------------------------------- # Remove nonmask-valued vertices: # -------------------------------------------------------------------- if remove_nonmask: # ---------------------------------------------------------------- # Load VTK files: # ---------------------------------------------------------------- points, indices, lines, faces, scalars, scalar_names, npoints, \ input_vtk = read_vtk(vtk_file, True, True) # ---------------------------------------------------------------- # Find mask indices, remove nonmask faces, and reindex: # ---------------------------------------------------------------- Imask = [i for i, x in enumerate(mask) if x != nonmask_value] mask_faces = keep_faces(faces, Imask) mask_faces, points, \ original_indices = reindex_faces_points(mask_faces, points) # ---------------------------------------------------------------- # Write VTK file with scalar values: # ---------------------------------------------------------------- if np.ndim(scalars) == 1: scalar_type = type(scalars[0]).__name__ elif np.ndim(scalars) == 2: scalar_type = type(scalars[0][0]).__name__ else: print("Undefined scalar type!") write_vtk(file_to_plot, points, [], [], mask_faces, scalars[original_indices].tolist(), scalar_names, scalar_type=scalar_type) else: scalars, name = read_scalars(vtk_file, True, True) scalars[mask == nonmask_value] = nonmask_value rewrite_scalars(vtk_file, file_to_plot, scalars, ['scalars'], [], background_value) else: file_to_plot = vtk_file # ------------------------------------------------------------------------ # Display with vtkviewer.py: # ------------------------------------------------------------------------ if program == 'vtkviewer': plot_surfaces(file_to_plot, use_colormap=use_colormap, colormap_file=colormap_file) # ------------------------------------------------------------------------ # Display with mayavi2: # ------------------------------------------------------------------------ elif program == 'mayavi2': cmd = ["mayavi2", "-d", file_to_plot, "-m", "Surface", "&"] execute(cmd, 'os')
def antsApplyTransformsToPoints(points, transform_files, inverse_booleans=[0]): """ Run ANTs antsApplyTransformsToPoints function to transform points. (Creates pre- and post-transformed .csv points files for ANTs.) Parameters ---------- points : list of lists of three integers point coordinate data transform_files : list transform file names inverse_booleans : list for each transform, one to apply inverse of transform (otherwise zero) Returns ------- transformed_points : list of lists of three integers transformed point coordinate data Examples -------- >>> from mindboggle.thirdparty.ants import antsApplyTransformsToPoints >>> from mindboggle.mio.vtks import read_vtk >>> transform_files = ['/Users/arno/Data/antsCorticalThickness/Twins-2-1/antsTemplateToSubject1GenericAffine.mat','/Users/arno/Data/antsCorticalThickness/Twins-2-1/antsTemplateToSubject0Warp.nii.gz','/Users/arno/Data/antsCorticalThickness/Twins-2-1/antsSubjectToTemplate0GenericAffine.mat','/Users/arno/Data/antsCorticalThickness/Twins-2-1/antsSubjectToTemplate1Warp.nii.gz'] >>> transform_files = [transform_files[0],transform_files[1],'/Users/arno/Data/mindboggle_cache/f36e3d5d99f7c4a9bb70e2494ed7340b/OASIS-30_Atropos_template_to_MNI152_affine.txt'] >>> vtk_file = '/Users/arno/mindboggle_working/Twins-2-1/Mindboggle/_hemi_lh/Surface_to_vtk/lh.pial.vtk' >>> faces, lines, indices, points, npoints, scalars, name, foo1 = read_vtk(vtk_file) >>> inverse_booleans = [0,0,1] >>> transformed_points = antsApplyTransformsToPoints(points, transform_files, inverse_booleans) """ import os from mindboggle.guts.utilities import execute #------------------------------------------------------------------------- # Write points (x,y,z,1) to a .csv file: #------------------------------------------------------------------------- points_file = os.path.join(os.getcwd(), 'points.csv') fid = open(points_file, 'wa') fid.write('x,y,z,t\n') for point in points: string_of_zeros = (4 - len(point)) * ',0' fid.write(','.join([str(x) for x in point]) + string_of_zeros + '\n') fid.close() #------------------------------------------------------------------------- # Apply transforms to points in .csv file: #------------------------------------------------------------------------- transformed_points_file = os.path.join(os.getcwd(), 'transformed_points.csv') transform_string = '' for ixfm, transform_file in enumerate(transform_files): transform_string += " --t [{0},{1}]".\ format(transform_file, str(inverse_booleans[ixfm])) cmd = [ 'antsApplyTransformsToPoints', '-d', '3', '-i', points_file, '-o', transformed_points_file, transform_string ] execute(cmd, 'os') if not os.path.exists(transformed_points_file): str1 = "antsApplyTransformsToPoints did not create " raise (IOError(str1 + transformed_points_file + ".")) #------------------------------------------------------------------------- # Return transformed points: #------------------------------------------------------------------------- fid = open(transformed_points_file, 'r') lines = fid.readlines() fid.close() transformed_points = [] for iline, line in enumerate(lines): if iline > 0: point_xyz1 = [float(x) for x in line.split(',')] transformed_points.append(point_xyz1[0:3]) return transformed_points
def thickinthehead(segmented_file, labeled_file, cortex_value=2, noncortex_value=3, labels=[], names=[], resize=True, propagate=True, output_dir='', save_table=False, output_table='', ants_path='', verbose=False): """ Compute a simple thickness measure for each labeled cortex region volume. Note:: - Cortex, noncortex, & label files are from the same coregistered brain. - Calls ANTs functions: ImageMath, Threshold, ResampleImageBySpacing - There may be slight discrepancies between volumes computed by thickinthehead() and volumes computed by volume_for_each_label(); in 31 of 600+ ADNI 1.5T images, some volume_for_each_label() volumes were slightly larger (in the third decimal place), presumably due to label propagation through the cortex in thickinthehead(). This is more pronounced in ANTs vs. FreeSurfer-labeled volumes. Example preprocessing steps :: 1. Run Freesurfer and antsCorticalThickness.sh on T1-weighted image. 2. Convert FreeSurfer volume labels (e.g., wmparc.mgz or aparc+aseg.mgz) to cortex (2) and noncortex (3) segments using relabel_volume() function [refer to LABELS.rst or FreeSurferColorLUT labels file]. 3. Convert ANTs Atropos-segmented volume (tmpBrainSegmentation.nii.gz) to cortex and noncortex segments, by converting 1-labels to 0 and 4-labels to 3 with the relabel_volume() function (the latter is to include deep-gray matter with noncortical tissues). 4. Combine FreeSurfer and ANTs segmentation volumes to obtain a single cortex (2) and noncortex (3) segmentation file using the function combine_2labels_in_2volumes(). This function takes the union of cortex voxels from the segmentations, the union of the noncortex voxels from the segmentations, and overwrites intersecting cortex and noncortex voxels with noncortex (3) labels. ANTs tends to include more cortical gray matter at the periphery of the brain than Freesurfer, and FreeSurfer tends to include more white matter that extends deep into gyral folds than ANTs, so the above attempts to remedy their differences by overlaying ANTs cortical gray with FreeSurfer white matter. 5. Optional, see Step 2 below: Fill segmented cortex with cortex labels and noncortex with noncortex labels using the PropagateLabelsThroughMask() function (which calls ImageMath ... PropagateLabelsThroughMask in ANTs). The labels can be initialized using FreeSurfer (e.g. wmparc.mgz) or ANTs (by applying the nonlinear inverse transform generated by antsCorticalThickness.sh to labels in the Atropos template space). [Note: Any further labeling steps may be applied, such as overwriting cerebrum with intersecting cerebellum labels.] Steps :: 1. Extract noncortex and cortex. 2. Either mask labels with cortex or fill cortex with labels. 3. Resample cortex and noncortex files from 1x1x1 to 0.5x0.5x0.5 to better represent the contours of the boundaries of the cortex. 4. Extract outer and inner boundary voxels of the cortex, by eroding 1 (resampled) voxel for cortex voxels (2) bordering the outside of the brain (0) and bordering noncortex (3). 5. Estimate middle cortical surface area by the average volume of the outer and inner boundary voxels of the cortex. 6. Compute the volume of a labeled region of cortex. 7. Estimate the thickness of the labeled cortical region as the volume of the labeled region (#6) divided by the surface area (#5). Parameters ---------- segmented_file : string image volume with cortex and noncortex (and any other) labels labeled_file : string corresponding image volume with index labels cortex_value : integer cortex label value in segmented_file noncortex_value : integer noncortex label value in segmented_file labels : list of integers label indices names : list of strings label names resize : bool resize (2x) segmented_file for more accurate thickness estimates? propagate : bool propagate labels through cortex? output_dir : string output directory save_table : bool save output table file with label volumes and thickness values? output_table : string name of output table file with label volumes and thickness values verbose : bool print statements? Returns ------- label_volume_thickness : list of lists of integers and floats label indices, volumes, and thickness values (default -1) output_table : string name of output table file with label volumes and thickness values Examples -------- >>> # Example simply using ants segmentation and labels: >>> import os >>> import numpy as np >>> from mindboggle.shapes.volume_shapes import thickinthehead >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> segmented_file = fetch_data(urls['ants_segmentation']) >>> labeled_file = fetch_data(urls['ants_labels']) >>> cortex_value = 2 >>> noncortex_value = 3 >>> #labels = [2] >>> labels = list(range(1002,1036)) + list(range(2002,2036)) >>> labels.remove(1004) >>> labels.remove(2004) >>> labels.remove(1032) >>> labels.remove(2032) >>> labels.remove(1033) >>> labels.remove(2033) >>> names = [] >>> resize = True >>> propagate = False >>> output_dir = '' >>> save_table = True >>> output_table = '' >>> verbose = False >>> label_volume_thickness, output_table = thickinthehead(segmented_file, ... labeled_file, cortex_value, noncortex_value, labels, names, ... resize, propagate, output_dir, save_table, output_table, verbose) >>> print(np.array_str(np.array(label_volume_thickness[0][0:10]), ... precision=5, suppress_small=True)) [ 1002. 1003. 1005. 1006. 1007. 1008. 1009. 1010. 1011. 1012.] >>> print(np.array_str(np.array(label_volume_thickness[1][0:5]), ... precision=5, suppress_small=True)) [ 3136.99383 7206.98582 3257.99359 1950.99616 12458.97549] >>> print(np.array_str(np.array(label_volume_thickness[2][0:5]), ... precision=5, suppress_small=True)) [ 3.8639 3.69637 2.56334 4.09336 4.52592] """ import os import numpy as np import nibabel as nb from io import open from mindboggle.guts.utilities import execute #------------------------------------------------------------------------- # Output files: #------------------------------------------------------------------------- if output_dir: if not os.path.exists(output_dir): os.mkdir(output_dir) else: output_dir = os.getcwd() cortex = os.path.join(output_dir, 'cortex.nii.gz') noncortex = os.path.join(output_dir, 'noncortex.nii.gz') temp = os.path.join(output_dir, 'temp.nii.gz') inner_edge = os.path.join(output_dir, 'cortex_inner_edge.nii.gz') use_outer_edge = True if use_outer_edge: outer_edge = os.path.join(output_dir, 'cortex_outer_edge.nii.gz') if save_table: if output_table: output_table = os.path.join(os.getcwd(), output_table) else: output_table = os.path.join(os.getcwd(), 'thickinthehead_for_each_label.csv') fid = open(output_table, 'w') if names: fid.write("name, ID, thickness (thickinthehead)\n") else: fid.write("ID, thickness (thickinthehead)\n") else: output_table = '' #------------------------------------------------------------------------- # ants command paths: #------------------------------------------------------------------------- ants_thresh = 'ThresholdImage' ants_math = 'ImageMath' ants_resample = 'ResampleImageBySpacing' #------------------------------------------------------------------------- # Extract noncortex and cortex: #------------------------------------------------------------------------- cmd = [ants_thresh, '3', segmented_file, noncortex, str(noncortex_value), str(noncortex_value), '1 0'] execute(cmd, 'os') cmd = [ants_thresh, '3', segmented_file, cortex, str(cortex_value), str(cortex_value), '1 0'] execute(cmd, 'os') #------------------------------------------------------------------------- # Either mask labels with cortex or fill cortex with labels: #------------------------------------------------------------------------- if propagate: cmd = [ants_math, '3', cortex, 'PropagateLabelsThroughMask', cortex, labeled_file] execute(cmd, 'os') else: cmd = [ants_math, '3', cortex, 'm', cortex, labeled_file] execute(cmd, 'os') #------------------------------------------------------------------------- # Load data and dimensions: #------------------------------------------------------------------------- if resize: rescale = 2.0 else: rescale = 1.0 compute_real_volume = True if compute_real_volume: img = nb.load(cortex) hdr = img.get_header() vv_orig = np.prod(hdr.get_zooms()) vv = np.prod([x/rescale for x in hdr.get_zooms()]) cortex_data = img.get_data().ravel() else: vv = 1/rescale cortex_data = nb.load(cortex).get_data().ravel() #------------------------------------------------------------------------- # Resample cortex and noncortex files from 1x1x1 to 0.5x0.5x0.5 # to better represent the contours of the boundaries of the cortex: #------------------------------------------------------------------------- if resize: dims = ' '.join([str(1/rescale), str(1/rescale), str(1/rescale)]) cmd = [ants_resample, '3', cortex, cortex, dims, '0 0 1'] execute(cmd, 'os') cmd = [ants_resample, '3', noncortex, noncortex, dims, '0 0 1'] execute(cmd, 'os') #------------------------------------------------------------------------- # Extract outer and inner boundary voxels of the cortex, # by eroding 1 (resampled) voxel for cortex voxels (2) bordering # the outside of the brain (0) and bordering noncortex (3): #------------------------------------------------------------------------- cmd = [ants_math, '3', inner_edge, 'MD', noncortex, '1'] execute(cmd, 'os') cmd = [ants_math, '3', inner_edge, 'm', cortex, inner_edge] execute(cmd, 'os') if use_outer_edge: cmd = [ants_thresh, '3', cortex, outer_edge, '1 10000 1 0'] execute(cmd, 'os') cmd = [ants_math, '3', outer_edge, 'ME', outer_edge, '1'] execute(cmd, 'os') cmd = [ants_thresh, '3', outer_edge, outer_edge, '1 1 0 1'] execute(cmd, 'os') cmd = [ants_math, '3', outer_edge, 'm', cortex, outer_edge] execute(cmd, 'os') cmd = [ants_thresh, '3', inner_edge, temp, '1 10000 1 0'] execute(cmd, 'os') cmd = [ants_thresh, '3', temp, temp, '1 1 0 1'] execute(cmd, 'os') cmd = [ants_math, '3', outer_edge, 'm', temp, outer_edge] execute(cmd, 'os') #------------------------------------------------------------------------- # Load data: #------------------------------------------------------------------------- inner_edge_data = nb.load(inner_edge).get_data().ravel() if use_outer_edge: outer_edge_data = nb.load(outer_edge).get_data().ravel() #------------------------------------------------------------------------- # Loop through labels: #------------------------------------------------------------------------- if not labels: labeled_data = nb.load(labeled_file).get_data().ravel() labels = np.unique(labeled_data) labels = [int(x) for x in labels] label_volume_thickness = -1 * np.ones((len(labels), 3)) label_volume_thickness[:, 0] = labels for ilabel, label in enumerate(labels): if names: name = names[ilabel] #--------------------------------------------------------------------- # Compute thickness as a ratio of label volume and edge volume: # - Estimate middle cortical surface area by the average volume # of the outer and inner boundary voxels of the cortex. # - Compute the volume of a labeled region of cortex. # - Estimate the thickness of the labeled cortical region as the # volume of the labeled region divided by the surface area. #--------------------------------------------------------------------- label_cortex_volume = vv_orig * len(np.where(cortex_data==label)[0]) label_inner_edge_volume = vv * len(np.where(inner_edge_data==label)[0]) if label_inner_edge_volume: if use_outer_edge: label_outer_edge_volume = \ vv * len(np.where(outer_edge_data==label)[0]) label_area = (label_inner_edge_volume + label_outer_edge_volume) / 2.0 else: label_area = label_inner_edge_volume thickness = label_cortex_volume / label_area label_volume_thickness[ilabel, 1] = label_cortex_volume label_volume_thickness[ilabel, 2] = thickness if save_table: if names: if verbose: print('{0} ({1}) thickinthehead thickness = ' '{2:2.2f}mm'.format(name, label, thickness)) fid.write('{0}, {1}, {2:2.3f}\n'.format(name, label, thickness)) else: if verbose: print('{0} thickinthehead thickness = {1:2.2f}mm'. format(label, thickness)) fid.write('{0}, {1:2.3f}\n'.format(label, thickness)) label_volume_thickness = label_volume_thickness.transpose().tolist() return label_volume_thickness, output_table
def antsApplyTransformsToPoints(points, transform_files, inverse_booleans=[0]): """ Run ANTs antsApplyTransformsToPoints function to transform points. (Creates pre- and post-transformed .csv points files for ANTs.) Parameters ---------- points : list of lists of three integers point coordinate data transform_files : list transform file names inverse_booleans : list for each transform, one to apply inverse of transform (otherwise zero) Returns ------- transformed_points : list of lists of three integers transformed point coordinate data Examples -------- >>> import os >>> import numpy as np >>> from mindboggle.thirdparty.ants import antsApplyTransformsToPoints >>> from mindboggle.mio.vtks import read_points >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> xfm1 = fetch_data(urls['ants_affine_template2subject']) >>> xfm2 = fetch_data(urls['ants_warp_template2subject']) >>> xfm3 = fetch_data(urls['OASIS-30_Atropos_template_to_MNI152_affine']) >>> transform_files = [xfm1, xfm2, xfm3] >>> vtk_file = fetch_data(urls['left_pial']) >>> points = read_points(vtk_file) >>> inverse_booleans = [0,0,1] >>> transformed_points = antsApplyTransformsToPoints(points, ... transform_files, inverse_booleans) # doctest: +SKIP >>> print(np.array_str(np.array(transformed_points[0:5]), ... precision=5, suppress_small=True)) # doctest: +SKIP [[-11.23189 -46.78223 -39.88869] [-11.71384 -46.87075 -40.13328] [-12.56237 -46.99126 -40.04564] [ -9.66693 -46.0446 -41.36334] [-10.67998 -46.45458 -40.7572 ]] """ import os from io import open from mindboggle.guts.utilities import execute #------------------------------------------------------------------------- # Write points (x,y,z,1) to a .csv file: #------------------------------------------------------------------------- points_file = os.path.join(os.getcwd(), 'points.csv') fid = open(points_file, 'wa') fid.write('x,y,z,t\n') for point in points: string_of_zeros = (4 - len(point)) * ',0' fid.write(','.join([str(x) for x in point]) + string_of_zeros + '\n') fid.close() #------------------------------------------------------------------------- # Apply transforms to points in .csv file: #------------------------------------------------------------------------- transformed_points_file = os.path.join(os.getcwd(), 'transformed_points.csv') transform_string = '' for ixfm, transform_file in enumerate(transform_files): transform_string += " --t [{0},{1}]".\ format(transform_file, str(inverse_booleans[ixfm])) cmd = ['antsApplyTransformsToPoints', '-d', '3', '-i', points_file, '-o', transformed_points_file, transform_string] try: execute(cmd, 'os') except: raise Exception("Cannot find antsApplyTransformsToPoints command.") if not os.path.exists(transformed_points_file): raise IOError("antsApplyTransformsToPoints did not create {0}.". format(transformed_points_file)) #------------------------------------------------------------------------- # Return transformed points: #------------------------------------------------------------------------- fid = open(transformed_points_file, 'rb') lines = fid.readlines() fid.close() transformed_points = [] for iline, line in enumerate(lines): if iline > 0: point_xyz1 = [float(x) for x in line.split(',')] transformed_points.append(point_xyz1[0:3]) return transformed_points
def thickinthehead(segmented_file, labeled_file, cortex_value=2, noncortex_value=3, labels=[], names=[], propagate=False, output_dir='', save_table=False, output_table='', verbose=False): """ Compute a simple thickness measure for each labeled cortex region volume. Since Mindboggle accepts FreeSurfer data as input, we include FreeSurfer cortical thickness estimates with Mindboggle’s shape measures. However, surface mesh reconstruction from MRI data does not always produce favorable results. For example, we found that at least a quarter of the over one hundred EMBARC brain images we processed through FreeSurfer clipped ventral cortical regions, resulting in bad surface patches in those regions. For comparison, we built this function called thickinthehead which computes a simple thickness measure for each cortical region using a segmentation volume rather than surfaces. We have revised this algorithm from the original published version. We removed upsampling to reduce memory issues for large image volumes, and replaced the estimated volume of middle cortical layer with an estimate of its surface area. We made these revisions to be less susceptible to deviations in voxel size from isometric 1mm^3 voxels for which thickinthehead was originally built. Steps :: 1. Extract noncortex and cortex into separate files. 2. Either mask labels with cortex or fill cortex with labels. 3. Extract outer and inner boundary voxels of the cortex, by morphologically eroding the cortex (=2) by one voxel bordering the outside of the brain (=0) and bordering the inside of the brain (non-cortex=3). 4. Estimate middle cortical layer's surface area by the average surface area of the outer and inner boundary voxels of the cortex, where surface area is roughly estimated as the average face area of a voxel times the number of voxels. 5. Compute the volume of a labeled region of cortex. 6. Estimate the thickness of the labeled cortical region as the volume of the labeled region (#5) divided by the estimate of the middle cortical surface area of that region (#4). Note:: - Cortex, noncortex, & label files are from the same coregistered brain. - Calls ANTs functions: ImageMath and Threshold - There may be slight discrepancies between volumes computed by thickinthehead() and volumes computed by volume_per_label(); in 31 of 600+ ADNI 1.5T images, some volume_per_label() volumes were slightly larger (in the third decimal place), presumably due to label propagation through the cortex in thickinthehead(). This is more pronounced in ANTs vs. FreeSurfer-labeled volumes. Parameters ---------- segmented_file : string image volume with cortex and noncortex (and any other) labels labeled_file : string corresponding image volume with index labels cortex_value : integer cortex label value in segmented_file noncortex_value : integer noncortex label value in segmented_file labels : list of integers label indices names : list of strings label names propagate : bool propagate labels through cortex (or mask labels with cortex)? output_dir : string output directory save_table : bool save output table file with label volumes and thickness values? output_table : string name of output table file with label volumes and thickness values verbose : bool print statements? Returns ------- label_volume_thickness : list of lists of integers and floats label indices, volumes, and thickness values (default -1) output_table : string name of output table file with label volumes and thickness values Examples -------- >>> # Example simply using ants segmentation and labels vs. hybrid segmentation: >>> import os >>> import numpy as np >>> from mindboggle.shapes.volume_shapes import thickinthehead >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> segmented_file = fetch_data(urls['ants_segmentation'], '', '.nii.gz') >>> labeled_file = fetch_data(urls['ants_labels'], '', '.nii.gz') >>> cortex_value = 2 >>> noncortex_value = 3 >>> #labels = [2] >>> labels = list(range(1002,1036)) + list(range(2002,2036)) >>> labels.remove(1004) >>> labels.remove(2004) >>> labels.remove(1032) >>> labels.remove(2032) >>> labels.remove(1033) >>> labels.remove(2033) >>> names = [] >>> propagate = False >>> output_dir = '' >>> save_table = True >>> output_table = '' >>> verbose = False Skip online test because it requires installation of ANTs: >>> label_volume_thickness, output_table = thickinthehead(segmented_file, ... labeled_file, cortex_value, noncortex_value, labels, names, ... propagate, output_dir, save_table, output_table, verbose) # doctest: +SKIP >>> [np.int("{0:.{1}f}".format(x, 5)) label_volume_thickness[0][0:10]] # doctest: +SKIP >>> [np.float("{0:.{1}f}".format(x, 5)) for x in label_volume_thickness[1][0:5]] # doctest: +SKIP >>> [np.float("{0:.{1}f}".format(x, 5)) for x in label_volume_thickness[2][0:5]] # doctest: +SKIP """ import os import numpy as np import nibabel as nb from io import open from mindboggle.guts.utilities import execute # ------------------------------------------------------------------------ # Output files: # ------------------------------------------------------------------------ if output_dir: if not os.path.exists(output_dir): os.mkdir(output_dir) else: output_dir = os.getcwd() cortex = os.path.join(output_dir, 'cortex.nii.gz') noncortex = os.path.join(output_dir, 'noncortex.nii.gz') temp = os.path.join(output_dir, 'temp.nii.gz') inner_edge = os.path.join(output_dir, 'cortex_inner_edge.nii.gz') use_outer_edge = True if use_outer_edge: outer_edge = os.path.join(output_dir, 'cortex_outer_edge.nii.gz') if save_table: if output_table: output_table = os.path.join(os.getcwd(), output_table) else: output_table = os.path.join(os.getcwd(), 'thickinthehead_for_each_label.csv') fid = open(output_table, 'w', encoding='utf-8') if names: fid.write("name, ID, thickness (thickinthehead)\n") else: fid.write("ID, thickness (thickinthehead)\n") else: output_table = '' # ------------------------------------------------------------------------ # Extract noncortex and cortex: # ------------------------------------------------------------------------ cmd = ['ThresholdImage', '3', segmented_file, noncortex, str(noncortex_value), str(noncortex_value), '1 0'] execute(cmd, 'os') cmd = ['ThresholdImage', '3', segmented_file, cortex, str(cortex_value), str(cortex_value), '1 0'] execute(cmd, 'os') # ------------------------------------------------------------------------ # Either mask labels with cortex or fill cortex with labels: # ------------------------------------------------------------------------ if propagate: cmd = ['ImageMath', '3', cortex, 'PropagateLabelsThroughMask', cortex, labeled_file] execute(cmd, 'os') else: cmd = ['ImageMath', '3', cortex, 'm', cortex, labeled_file] execute(cmd, 'os') # ------------------------------------------------------------------------ # Load data and dimensions: # ------------------------------------------------------------------------ img = nb.load(cortex) cortex_data = img.get_data().ravel() voxsize = img.header.get_zooms() voxvol = np.prod(voxsize) voxarea = (voxsize[0] * voxsize[1] + \ voxsize[0] * voxsize[2] + \ voxsize[1] * voxsize[2]) / 3 # ------------------------------------------------------------------------ # Extract outer and inner boundary voxels of the cortex, # by eroding 1 voxel for cortex voxels (=2) bordering # the outside of the brain (=0) and bordering noncortex (=3): # ------------------------------------------------------------------------ cmd = ['ImageMath', '3', inner_edge, 'MD', noncortex, '1'] execute(cmd, 'os') cmd = ['ImageMath', '3', inner_edge, 'm', cortex, inner_edge] execute(cmd, 'os') if use_outer_edge: cmd = ['ThresholdImage', '3', cortex, outer_edge, '1 10000 1 0'] execute(cmd, 'os') cmd = ['ImageMath', '3', outer_edge, 'ME', outer_edge, '1'] execute(cmd, 'os') cmd = ['ThresholdImage', '3', outer_edge, outer_edge, '1 1 0 1'] execute(cmd, 'os') cmd = ['ImageMath', '3', outer_edge, 'm', cortex, outer_edge] execute(cmd, 'os') cmd = ['ThresholdImage', '3', inner_edge, temp, '1 10000 1 0'] execute(cmd, 'os') cmd = ['ThresholdImage', '3', temp, temp, '1 1 0 1'] execute(cmd, 'os') cmd = ['ImageMath', '3', outer_edge, 'm', temp, outer_edge] execute(cmd, 'os') # ------------------------------------------------------------------------ # Load data: # ------------------------------------------------------------------------ inner_edge_data = nb.load(inner_edge).get_data().ravel() if use_outer_edge: outer_edge_data = nb.load(outer_edge).get_data().ravel() # ------------------------------------------------------------------------ # Loop through labels: # ------------------------------------------------------------------------ if not labels: labeled_data = nb.load(labeled_file).get_data().ravel() labels = np.unique(labeled_data) labels = [int(x) for x in labels] label_volume_thickness = -1 * np.ones((len(labels), 3)) label_volume_thickness[:, 0] = labels for ilabel, label in enumerate(labels): if names: name = names[ilabel] # -------------------------------------------------------------------- # Compute thickness as a ratio of label volume and layer surface area: # - Estimate middle cortical surface area by the average area # of the outer and inner boundary voxels of the cortex. # - Surface area is roughly estimated as the average face area # of a voxel times the number of voxels. # - Compute the volume of a labeled region of cortex. # - Estimate the thickness of the labeled cortical region as the # volume of the labeled region divided by the middle surface area. # -------------------------------------------------------------------- label_cortex_volume = voxvol * len(np.where(cortex_data==label)[0]) label_inner_edge_area = voxarea * \ len(np.where(inner_edge_data==label)[0]) if label_inner_edge_area: if use_outer_edge: label_outer_edge_area = \ voxarea * len(np.where(outer_edge_data==label)[0]) label_area = (label_inner_edge_area + label_outer_edge_area) / 2.0 else: label_area = label_inner_edge_area thickness = label_cortex_volume / label_area label_volume_thickness[ilabel, 1] = label_cortex_volume label_volume_thickness[ilabel, 2] = thickness if save_table: if names: if verbose: print('{0} ({1}) thickinthehead thickness = ' '{2:2.2f}mm'.format(name, label, thickness)) fid.write('{0}, {1}, {2:2.3f}\n'.format(name, label, thickness)) else: if verbose: print('{0} thickinthehead thickness = {1:2.2f}mm'. format(label, thickness)) fid.write('{0}, {1:2.3f}\n'.format(label, thickness)) label_volume_thickness = label_volume_thickness.transpose().tolist() return label_volume_thickness, output_table
def PropagateLabelsThroughMask(mask, labels, mask_index=None, output_file='', binarize=True, stopvalue=''): """ Use ANTs to fill a binary volume mask with initial labels. This program uses ThresholdImage and the ImageMath PropagateLabelsThroughMask functions in ANTs. ThresholdImage ImageDimension ImageIn.ext outImage.ext threshlo threshhi <insideValue> <outsideValue> PropagateLabelsThroughMask: Final output is the propagated label image. ImageMath ImageDimension Out.ext PropagateLabelsThroughMask speed/binaryimagemask.nii.gz initiallabelimage.nii.gz ... Parameters ---------- mask : string nibabel-readable image volume labels : string nibabel-readable image volume with integer labels mask_index : integer (optional) mask with just voxels having this value output_file : string (optional) nibabel-readable labeled image volume binarize : bool (optional) binarize mask? stopvalue : integer (optional) stopping value Returns ------- output_file : string name of labeled output nibabel-readable image volume Examples -------- >>> # Propagate FreeSurfer labels through brain mask: >>> import os >>> from mindboggle.thirdparty.ants import PropagateLabelsThroughMask >>> from mindboggle.mio.fetch_data import prep_tests >>> urls, fetch_data = prep_tests() >>> labels = fetch_data(urls['freesurfer_labels']) >>> mask = fetch_data(urls['ants_mask']) >>> mask_index = None >>> output_file = '' >>> binarize = True >>> stopvalue = None >>> output_file = PropagateLabelsThroughMask(mask, labels, mask_index, ... output_file, binarize, stopvalue) # doctest: +SKIP View result (skip test): >>> from mindboggle.mio.plots import plot_volumes >>> plot_volumes(output_file) # doctest: +SKIP """ import os from mindboggle.guts.utilities import execute if not output_file: #output_file = os.path.join(os.getcwd(), # 'PropagateLabelsThroughMask.nii.gz') output_file = os.path.join(os.getcwd(), os.path.basename(labels) + '_through_' + os.path.basename(mask)) print('mask: {0}, labels: {1}'.format(mask, labels)) # Binarize image volume: if binarize: temp_file = os.path.join(os.getcwd(), 'PropagateLabelsThroughMask.nii.gz') cmd = ['ThresholdImage', '3', mask, temp_file, '0 1 0 1'] execute(cmd, 'os') mask = temp_file # Mask with just voxels having mask_index value: if mask_index: mask2 = os.path.join(os.getcwd(), 'temp.nii.gz') cmd = ['ThresholdImage', '3', mask, mask2, str(mask_index), str(mask_index)] execute(cmd, 'os') else: mask2 = mask # Propagate labels: cmd = ['ImageMath', '3', output_file, 'PropagateLabelsThroughMask', mask2, labels] if stopvalue: cmd.extend(str(stopvalue)) execute(cmd, 'os') if not os.path.exists(output_file): raise IOError("ImageMath did not create " + output_file + ".") return output_file