示例#1
0
def main():

    SOFTWARE = 'Buie-Burghardt-Scanco Autocontour VTK Implemention'
    VERSION = 0.2

    args = create_parser().parse_args()

    if args.out_value >= args.in_value:
        raise ValueError('please make `in-value` larger than `out-value`')

    aim_fn_list = glob(os.path.join(args.aim_dir, args.aim_pattern))

    reader = vtkbone.vtkboneAIMReader()
    reader.DataOnCellsOff()

    for aim_fn in aim_fn_list:

        print(aim_fn)

        cort_mask_fn = aim_fn.replace('.AIM', '_CORT_MASK.AIM')
        trab_mask_fn = aim_fn.replace('.AIM', '_TRAB_MASK.AIM')

        reader.SetFileName(aim_fn)
        reader.Update()

        img = reader.GetOutput()
        m, b = get_aim_density_equation(reader.GetProcessingLog())
        img = convert_aim_to_density(img, m, b)

        cort_mask, trab_mask = autocontour_buie(img, args)

        write_mask(reader, cort_mask, cort_mask_fn, 'CORT_MASK', SOFTWARE,
                   VERSION)
        write_mask(reader, trab_mask, trab_mask_fn, 'TRAB_MASK', SOFTWARE,
                   VERSION)
示例#2
0
    def test_write_aim_set_log(self):
        '''Can write aim file with processing log'''
        extension = '.aim'
        filename = os.path.join(self.test_dir, 'file' + extension)
        scalar_type = vtk.VTK_SHORT
        processing_log = 'This is a fake processing log'

        source = self.generate_image(scalar_type)
        writer = vtkbone.vtkboneAIMWriter()
        writer.SetInputConnection(source.GetOutputPort())
        writer.SetFileName(filename)

        handle_filetype_writing_special_cases(writer,
                                              processing_log=processing_log)

        writer.Update()
        self.assertTrue(os.path.isfile(filename))

        reader = vtkbone.vtkboneAIMReader()
        reader.DataOnCellsOff()
        reader.SetFileName(filename)
        reader.Update()

        self.assertEqual(reader.GetOutput().GetScalarType(), vtk.VTK_SHORT)

        # A dummy log is created in this instance. Only check that our log was appended
        self.assertEqual(reader.GetProcessingLog().split(os.linesep)[-1],
                         processing_log)
示例#3
0
def get_vtk_reader(filename):
    '''Get the appropriate vtkImageReader given the filename

    This function utilizes the factory method classes in VTK with
    some added functionality for working with the AIM, nifti, and
    dicom readers.

    Args:
        filename (string):      Image to be read in

    Returns:
        vtkImageReader:         The corresponding vtkImageReader or None
                                if one cannot be found.
    '''
    # Try factory method
    reader = vtk.vtkImageReader2Factory.CreateImageReader2(filename)

    # If it doesn't work, try specific cases
    if reader is None:
        if filename.lower().endswith('.aim'):
            reader = vtkbone.vtkboneAIMReader()
            reader.DataOnCellsOff()
        elif filename.lower().endswith('.nii'):
            reader = vtk.vtkNIFTIImageReader()
        elif filename.lower().endswith('.nii.gz'):
            reader = vtk.vtkNIFTIImageReader()
        elif filename.lower().endswith('.dcm'):
            reader = vtk.vtkDICOMImageReader()

    return reader
示例#4
0
    def test_get_aim(self):
        '''AIM file returns correct reader'''
        extension='.aim'
        expected = type(vtkbone.vtkboneAIMReader())
        writer = vtkbone.vtkboneAIMWriter()

        filename = os.path.join(self.test_dir, 'file'+extension)
        self.generate_image(filename, writer)
        self.assertEqual(type(get_vtk_reader(filename)), expected)
示例#5
0
    def test_spacing(self):
        '''Can run `aimod` changing spacing'''
        stdin = os.linesep.join(['1', '2', '3']) + os.linesep * 7
        input_file = os.path.join(self.test_dir, 'test25a.aim')
        output_file = os.path.join(self.test_dir, 'test.aim')
        os.sys.stdin = StringIO(stdin)
        aimod(input_file, output_file)
        self.assertTrue(os.path.isfile(output_file))

        reader = vtkbone.vtkboneAIMReader()
        reader.DataOnCellsOff()
        reader.SetFileName(output_file)
        reader.Update()
        image = reader.GetOutput()

        self.assertAlmostEqual(image.GetSpacing()[0], 1.0)
        self.assertAlmostEqual(image.GetSpacing()[1], 2.0)
        self.assertAlmostEqual(image.GetSpacing()[2], 3.0)
示例#6
0
    def test_position(self):
        '''Can run `aimod` changing position'''
        stdin = os.linesep * 3 + os.linesep.join(['10.0', '11.2', '13.5'
                                                  ]) + os.linesep * 4
        input_file = os.path.join(self.test_dir, 'test25a.aim')
        output_file = os.path.join(self.test_dir, 'test.aim')
        os.sys.stdin = StringIO(stdin)
        aimod(input_file, output_file)
        self.assertTrue(os.path.isfile(output_file))

        reader = vtkbone.vtkboneAIMReader()
        reader.DataOnCellsOff()
        reader.SetFileName(output_file)
        reader.Update()
        image = reader.GetOutput()

        self.assertAlmostEqual(image.GetOrigin()[0], 10.0, places=1)
        self.assertAlmostEqual(image.GetOrigin()[1], 11.2, places=1)
        self.assertAlmostEqual(image.GetOrigin()[2], 13.5, places=1)
示例#7
0
    def test_dimension(self):
        '''Can run `aimod` changing dimension'''
        stdin = os.linesep * 6 + os.linesep.join(['2', '3', '4'
                                                  ]) + os.linesep * 1
        input_file = os.path.join(self.test_dir, 'test25a.aim')
        output_file = os.path.join(self.test_dir, 'test.aim')
        os.sys.stdin = StringIO(stdin)
        aimod(input_file, output_file)
        self.assertTrue(os.path.isfile(output_file))

        reader = vtkbone.vtkboneAIMReader()
        reader.DataOnCellsOff()
        reader.SetFileName(output_file)
        reader.Update()
        image = reader.GetOutput()

        self.assertEqual(image.GetDimensions()[0], 2)
        self.assertEqual(image.GetDimensions()[1], 3)
        self.assertEqual(image.GetDimensions()[2], 4)
示例#8
0
    def test_write_aim_unsigned_long(self):
        '''Can write aim file with type unsigned long'''
        extension = '.aim'
        filename = os.path.join(self.test_dir, 'file' + extension)
        scalar_type = vtk.VTK_UNSIGNED_LONG

        source = self.generate_image(scalar_type)
        writer = vtkbone.vtkboneAIMWriter()
        writer.SetInputConnection(source.GetOutputPort())
        writer.SetFileName(filename)

        handle_filetype_writing_special_cases(writer)

        writer.Update()
        self.assertTrue(os.path.isfile(filename))

        reader = vtkbone.vtkboneAIMReader()
        reader.DataOnCellsOff()
        reader.SetFileName(filename)
        reader.Update()

        self.assertEqual(reader.GetOutput().GetScalarType(), vtk.VTK_FLOAT)
if args.nThreads < 1:
    os.sys.exit('Number of threads must be one or greater. Given {n}. Exiting...'.format(n=args.nThreads))

# Check opacity
if args.opacity > 1 or args.opacity < 0:
    os.sys.exit('Opaicty must be between zeor and one. Given {o}. Exiting...'.format(o=args.opacity))

# Read both images
inputReader = vtk.vtkImageReader2Factory.CreateImageReader2(args.inputImage)
if inputReader is None:
    if args.inputImage.lower().endswith('.nii'):
        inputReader = vtk.vtkNIFTIImageReader()
    elif args.inputImage.lower().endswith('.dcm'):
        inputReader = vtk.vtkDICOMImageReader()
    elif vtkboneImported and args.inputImage.lower().endswith('.aim'):
        inputReader = vtkbone.vtkboneAIMReader()
        inputReader.DataOnCellsOff()
    elif vtkbonelabImported and args.inputImage.lower().endswith('.aim'):
        inputReader = vtkbonelab.vtkbonelabAIMReader()
        inputReader.DataOnCellsOff()
    else:
        os.sys.exit('Unable to find a reader for \"{fileName}\". Exiting...'.format(fileName=args.inputImage))
inputReader.SetFileName(args.inputImage)
print('Loading {}...'.format(args.inputImage))
inputReader.Update()

segReader = vtk.vtkImageReader2Factory.CreateImageReader2(args.inputSegmentation)
if segReader is None:
    if args.inputSegmentation.lower().endswith('.nii'):
        segReader = vtk.vtkNIFTIImageReader()
    elif args.inputSegmentation.lower().endswith('.dcm'):
示例#10
0
# Notes:
#   - This uses the processing log entries 'Mu_Scaling' and 'HU: mu water' to
#       determine the linear equation between native units and HU
#   - Outut is a float
#   - I don't think it is working perfectly yet
#
# Usage: python native_to_hu.py input.aim output.nii

# Imports
import argparse
import vtk
import re
import os
try:
    import vtkbone
    reader = vtkbone.vtkboneAIMReader()
except ImportError:
    try:
        import vtkn88
        reader = vtkn88.vtkn88AIMReader()
    except ImportError:
        try:
            import vtkbonelab
            reader = vtkbonelab.vtkbonelabAIMReader()
        except ImportError:
            os.sys.exit(
                'Unable to import vtkbone, vtkn88, or vtkbonelab. Exiting...')

# Argument parser
parser = argparse.ArgumentParser(
    description='Subget medical data',
示例#11
0
文件: aix.py 项目: njneeteson/Bonelab
def aix(aim_file, log, stat, verbose, meta):
    # Python 2/3 compatible input
    from six.moves import input

    debug = False

    # Read input file
    if not os.path.isfile(aim_file):
        print("!%  Can't open file {} for reading".format(aim_file))
        print("File read error: {}".format(aim_file))
    reader = vtkbone.vtkboneAIMReader()
    reader.DataOnCellsOff()
    reader.SetFileName(aim_file)
    reader.Update()
    image = reader.GetOutput()

    # Precompute some values
    guard = '!-------------------------------------------------------------------------------'
    phys_dim = [
        x * y for x, y in zip(image.GetDimensions(), reader.GetElementSize())
    ]
    size = os.path.getsize(aim_file)
    names = ['Bytes', 'KBytes', 'MBytes', 'GBytes']
    n_image_voxels = image.GetDimensions()[0] * image.GetDimensions(
    )[1] * image.GetDimensions()[2]
    voxel_volume = reader.GetElementSize()[0] * reader.GetElementSize(
    )[1] * reader.GetElementSize()[2]
    i = 0
    while int(size) > 1024 and i < len(names):
        i += 1
        size = size / 2.0**10

    if (not meta):
        # Print header
        print('')
        print(guard)
        print('!>')
        print(
            '!> dim                            {: >6}  {: >6}  {: >6}'.format(
                *image.GetDimensions()))
        print('!> off                                 x       x       x')
        print(
            '!> pos                            {: >6}  {: >6}  {: >6}'.format(
                *reader.GetPosition()))
        print(
            '!> element size in mm             {:.4f}  {:.4f}  {:.4f}'.format(
                *reader.GetElementSize()))
        print(
            '!> phys dim in mm                 {:.4f}  {:.4f}  {:.4f}'.format(
                *phys_dim))
        print('!>')
        print('!> Type of data               {}'.format(
            image.GetScalarTypeAsString()))
        print('!> Total memory size          {:.1f} {: <10}'.format(
            size, names[i]))
        print(guard)

    # Print log
    if log:
        print(reader.GetProcessingLog())

    # Print meta data information
    if meta:
        log = reader.GetProcessingLog()

        if (not log):
            print("None")
            exit(1)

        meta_list = []

        p_site = re.compile('^Site[ ]+([0-9]+)')
        p_patient = re.compile('^Index Patient[ ]+([0-9]+)')
        p_measurement = re.compile('^Index Measurement[ ]+([0-9]+)')
        p_name = re.compile('^Patient Name[ ]+ ([\w\-\(\' ]+)')
        p_strip_trailing_zeros = re.compile('[ \t]+$')

        for line in iter(log.splitlines()):
            try:
                m = p_site.search(line)
                if (m):
                    name = 'Site'
                    value = m.group(1)
                    if (value == '20'): value = 'RL'
                    if (value == '21'): value = 'RR'
                    if (value == '38'): value = 'TL'
                    if (value == '39'): value = 'TR'
                    meta_list.append(value)
                    if (debug): print('{0:25s}[{1:s}]'.format("Site", value))

                m = p_patient.search(line)
                if (m):
                    name = 'Index Patient'
                    value = m.group(1)
                    meta_list.append(value)
                    if (debug):
                        print('{0:25s}[{1:s}]'.format("Index Patient", value))

                m = p_measurement.search(line)
                if (m):
                    name = 'Index Measurement'
                    value = m.group(1)
                    meta_list.append(value)
                    if (debug):
                        print('{0:25s}[{1:s}]'.format("Index Measurement",
                                                      value))

                m = p_name.search(line)
                if (m):
                    name = 'Patient Name'
                    name_with_trailing_zeros = m.group(1)
                    value = p_strip_trailing_zeros.sub(
                        '', name_with_trailing_zeros)
                    meta_list.append(value)
                    if (debug):
                        print('{0:25s}[{1:s}]'.format("Patient Name", value))

            except AttributeError:
                print("Error: Cannot find meta data.")
                exit(1)

        for item in meta_list:
            print('\"{0:s}\" '.format(item), end='')
        print('\n', end='')
        #print('\"{0}\" \"{1}\" \"{2}\" \"{3}\"'.format(meta_list[0],meta_list[1],meta_list[2],meta_list[3]))

    # Print Stat
    if stat:
        array = vtk_to_numpy(image.GetPointData().GetScalars()).ravel()
        data = {
            '!> Max       =': array.max(),
            '!> Min       =': array.min(),
            '!> Mean      =': array.mean(),
            '!> SD        =': array.std(),
            '!> TV        =': n_image_voxels * voxel_volume
        }

        max_length = 0
        for measure, outcome in data.items():
            max_length = max(max_length, len(measure))
        formatter = '{{:<{}}} {{:>15.4f}} {{}}'.format(max_length)
        for measure, outcome in data.items():
            if measure == '!> TV        =':
                unit = '[mm^3]'
            else:
                unit = '[1]'
            print(formatter.format(measure, outcome, unit))
        print(guard)

    # Print verbose
    if verbose:
        half_slice_size = image.GetDimensions()[0] * image.GetDimensions(
        )[1] * 0.5
        array = vtk_to_numpy(image.GetPointData().GetScalars())
        array = array.reshape(image.GetDimensions()).transpose(2, 1, 0)
        i = 1
        it = np.nditer(array, flags=['multi_index'])
        while not it.finished:
            print("{} {}".format(it.multi_index, it[0]))

            # Print a half slice at a time
            if i % half_slice_size == 0:
                result = input('Continue printing results? (y/n)')
                if result not in ['y', 'yes']:
                    print('')
                    print('Aborting...')
                    break
                print('')

            it.iternext()
            i += 1
示例#12
0
def aimod(input_aim, output_aim):
    # Python 2/3 compatible input
    from six.moves import input

    # Read file
    print('Reading file: {}.....'.format(input_aim))
    reader = vtkbone.vtkboneAIMReader()
    reader.DataOnCellsOff()
    reader.SetFileName(input_aim)
    reader.Update()
    image = reader.GetOutput()

    spacing = np.array(image.GetSpacing())
    origin = np.array(image.GetOrigin())
    dim = np.array(image.GetDimensions())

    def set(x, index, value):
        x[index] = value

    mapping = [[
        'el_size_mm.x', lambda: spacing[0], lambda x: set(spacing, 0, x), float
    ], [
        'el_size_mm.y', lambda: spacing[1], lambda x: set(spacing, 1, x), float
    ], [
        'el_size_mm.z', lambda: spacing[2], lambda x: set(spacing, 2, x), float
    ], ['pos.x', lambda: origin[0], lambda x: set(origin, 0, x),
        float], [
            'pos.y', lambda: origin[1], lambda x: set(origin, 1, x), float
        ], ['pos.z', lambda: origin[2], lambda x: set(origin, 2, x), float],
               ['dim.x', lambda: dim[0], lambda x: set(dim, 0, x), int],
               ['dim.y', lambda: dim[1], lambda x: set(dim, 1, x), int],
               ['dim.z', lambda: dim[2], lambda x: set(dim, 2, x), int]]

    max_length = 0
    for line in mapping:
        max_length = max(max_length, len(line[0]))
    formatter_float = '{{: <{}}} [{{:0.3f}}] ? '.format(max_length)
    formatter_int = '{{: <{}}} [{{}}] ? '.format(max_length)
    formatter_switch = lambda x: formatter_int if x == int else formatter_float

    for i in range(len(mapping)):
        # Grab this input
        line = mapping[i]
        formatter = formatter_switch(line[3])

        # Prompt user
        result = input(formatter.format(line[0], line[1]()))

        # Enter == continue
        if result == '':
            continue

        # q, quit, no save
        # e, exit, save
        if len(result) > 0:
            if result[0] == 'q':
                print('Quit: not saved.')
                os.sys.exit()
            if result[0] == 'e':
                print('Exit.')
                break

        # Otherwise, set value
        line[2](line[3](result))

    # Set results
    image.SetSpacing(spacing)
    image.SetOrigin(origin)
    image.SetDimensions(dim)

    # Write
    writer = vtkbone.vtkboneAIMWriter()
    writer.SetInputData(image)
    writer.SetFileName(output_aim)
    writer.SetProcessingLog(reader.GetProcessingLog())
    writer.Update()
示例#13
0
def aix(aim_file, log, stat, verbose):
    # Python 2/3 compatible input
    from six.moves import input

    # Read input file
    if not os.path.isfile(aim_file):
        print("!%  Can't open file {} for reading".format(aim_file))
        print("File read error: {}".format(aim_file))
    reader = vtkbone.vtkboneAIMReader()
    reader.DataOnCellsOff()
    reader.SetFileName(aim_file)
    reader.Update()
    image = reader.GetOutput()

    # Precompute some values
    guard = '!-------------------------------------------------------------------------------'
    phys_dim = [
        x * y for x, y in zip(image.GetDimensions(), reader.GetElementSize())
    ]
    size = os.path.getsize(aim_file)
    names = ['Bytes', 'KBytes', 'MBytes', 'GBytes']
    i = 0
    while int(size) > 1024 and i < len(names):
        i += 1
        size = size / 2.0**10

    # Print header
    print('')
    print(guard)
    print('!>')
    print('!> dim                            {: >6}  {: >6}  {: >6}'.format(
        *image.GetDimensions()))
    print('!> off                                 x       x       x')
    print('!> pos                            {: >6}  {: >6}  {: >6}'.format(
        *reader.GetPosition()))
    print('!> element size in mm             {:.4f}  {:.4f}  {:.4f}'.format(
        *reader.GetElementSize()))
    print('!> phys dim in mm                 {:.4f}  {:.4f}  {:.4f}'.format(
        *phys_dim))
    print('!>')
    print('!> Type of data               {}'.format(
        image.GetScalarTypeAsString()))
    print('!> Total memory size          {:.1f} {: <10}'.format(
        size, names[i]))
    print(guard)

    # Print log
    if log:
        print(reader.GetProcessingLog())

    # Print Stat
    if stat:
        array = vtk_to_numpy(image.GetPointData().GetScalars()).ravel()
        data = {
            'Max': array.max(),
            'Min': array.min(),
            'Mean': array.mean(),
            'SD': array.std()
        }
        max_length = 0
        for measure, outcome in data.items():
            max_length = max(max_length, len(measure))
        formatter = '{{:<{}}} {{:0.2f}}'.format(max_length)
        for measure, outcome in data.items():
            print(formatter.format(measure, outcome))
        print(guard)

    # Print verbose
    if verbose:
        array = vtk_to_numpy(image.GetPointData().GetScalars())
        array = array.reshape(image.GetDimensions()).transpose(2, 1, 0)
        i = 1
        it = np.nditer(array, flags=['multi_index'])
        while not it.finished:
            print("{} {}".format(it.multi_index, it[0]))

            # Print every 100
            if i % 100 == 0:
                result = input('Continue printing results? (y/n)')
                if result not in ['y', 'yes']:
                    print('')
                    print('Aborting...')
                    break
                print('')

            it.iternext()
            i += 1
示例#14
0
def fileConverter(inputImage, outputImage, AIMProcessingLog):
    print('******************************************************')
    print(f'CONVERTING: {inputImage} to {outputImage}')

    # Extract directory, filename, basename, and extensions from the output image
    outDirectory, outFilename = os.path.split(outputImage)
    outBasename, outExtension = os.path.splitext(outFilename)

    # Check the output file format
    if outExtension.lower() == '.mha':
        outputImageFileName = os.path.join(outDirectory, outBasename + '.mha')
    elif outExtension.lower() == '.aim':
        if not AIMProcessingLog or ('.txt' not in AIMProcessingLog.lower()):
            print()
            print(
                'Error: A valid processing log (header) is needed to write out an AIM file.'
            )
            sys.exit(1)
        outputImageFileName = os.path.join(outDirectory, outBasename + '.aim')
    else:
        print()
        print('Error: output file extension must be MHA or AIM')
        sys.exit(1)

    # Check if the input is a file or directory
    if os.path.isfile(inputImage):
        # NOT DIRECTORY:
        # Extract directory, filename, basename, and extensions from the input image
        inDirectory, inFilename = os.path.split(inputImage)
        inBasename, inExtension = os.path.splitext(inFilename)

        # Setup the correct reader based on the input image extension
        if '.aim' in inExtension.lower():

            # If the input AIM contains a version number, remove it and rename the file
            if ';' in inExtension.lower():
                inputImageNew = inputImage.rsplit(';', 1)[0]
                os.rename(inputImage, inputImageNew)
                inputImage = inputImageNew

            # Get the processing log file extension
            procLogDir, procLogFilename = os.path.split(
                AIMProcessingLog.lower())
            procLogBasename, procLogExtension = os.path.splitext(
                procLogFilename)

            # Check to make sure the processing log file extension is valid
            # If the provided file name is not valid, create a new file based on the requested output file name
            if not AIMProcessingLog or (procLogExtension != '.txt'):
                AIMProcessingLog = os.path.join(outDirectory,
                                                outBasename + '.txt')
                print(
                    'Warning: No AIM processing log file name provided or invalid file extension provided. Using default file name: '
                    + AIMProcessingLog)
            else:
                print('WRITING PROCESSING LOG: ' + AIMProcessingLog)

            # Read in the AIM
            imageReader = vtkbone.vtkboneAIMReader()
            imageReader.SetFileName(inputImage)
            imageReader.DataOnCellsOff()
            imageReader.Update()
            inputHeader = imageReader.GetProcessingLog()

            # Write out the processing log as a txt file
            f = open(AIMProcessingLog, 'w')
            f.write(inputHeader)

            # Determine scalar type to use
            #   VTK_CHAR <-> D1char
            #   VTK_SHORT <-> D1short
            #   If it is of type BIT, CHAR, SIGNED CHAR, or UNSIGNED CHAR it is possible
            #   to store in a CHAR.
            inputScalarType = imageReader.GetOutput().GetScalarType()

            if (inputScalarType == vtk.VTK_BIT
                    or inputScalarType == vtk.VTK_CHAR
                    or inputScalarType == vtk.VTK_SIGNED_CHAR
                    or inputScalarType == vtk.VTK_UNSIGNED_CHAR):

                # Make sure the image will fit in the range
                #   It is possible that the chars are defined in such a way that either
                #   signed or unsigned chars don't fit inside the char. We can be safe
                #   buy checking if the image range will fit inside the VTK_CHAR
                scalarRange = imageReader.GetOutput().GetScalarRange()
                if scalarRange[0] >= vtk.VTK_SHORT_MIN and scalarRange[
                        1] <= vtk.VTK_SHORT_MAX:
                    outputScalarType = vtk.VTK_CHAR
                else:
                    outputScalarType = vtk.VTK_SHORT
            else:
                outputScalarType = vtk.VTK_SHORT

            # Cast
            caster = vtk.vtkImageCast()
            caster.SetOutputScalarType(outputScalarType)
            caster.SetInputConnection(imageReader.GetOutputPort())
            caster.ReleaseDataFlagOff()
            caster.Update()

            # Get VTK and SITK images
            vtk_image = caster.GetOutput()
            sitk_image = vtk2sitk(vtk_image)

        else:
            sitk_image = sitk.ReadImage(inputImage)

    # Check if the input is a directory
    elif os.path.isdir(inputImage):
        # DIRECTORY:
        print()
        print('Error: Please provide a valid file, not a directory!')
        sys.exit(1)

    # Setup the correct writer based on the output image extension
    if outExtension.lower() == '.mha':
        print('WRITING IMAGE: ' + str(outputImage))
        sitk.WriteImage(sitk_image, str(outputImageFileName))

    elif outExtension.lower() == '.aim':
        print('WRITING IMAGE: ' + str(outputImage))

        # Handle the special case of MHA to AIM to avoid crashing due to SITK to VTK conversion
        # Need to cast grayscale images to VTK_SHORT type and binary images to VTK_CHAR type to display properly on the OpenVMS system
        # Scan the AIM log file to determine if the image is segmented or grayscale
        segFile = searchAIMLog(AIMProcessingLog)

        if segFile and inExtension.lower() == '.mha':
            img = vtk.vtkMetaImageReader()
            img.SetFileName(inputImage)
            img.Update()

            caster = vtk.vtkImageCast()
            caster.SetInputData(img.GetOutput())
            caster.SetOutputScalarType(vtk.VTK_CHAR)
            caster.ReleaseDataFlagOff()
            caster.Update()

            vtk_image = caster.GetOutput()

        elif not segFile and inExtension.lower() == '.mha':
            img = vtk.vtkMetaImageReader()
            img.SetFileName(inputImage)
            img.Update()

            caster = vtk.vtkImageCast()
            caster.SetInputData(img.GetOutput())
            caster.SetOutputScalarType(vtk.VTK_SHORT)
            caster.ReleaseDataFlagOff()
            caster.Update()

            vtk_image = caster.GetOutput()

        # Open the processing log for reading
        f = open(AIMProcessingLog, 'r')
        header = f.read()

        writer = vtkbone.vtkboneAIMWriter()
        writer.SetInputData(vtk_image)
        writer.SetFileName(str(outputImageFileName))

        # Do not create a new processing log as this will add extra values that
        # will cause problems when processing the new AIM in IPL
        writer.NewProcessingLogOff()
        writer.SetProcessingLog(header)

        writer.Update()

    print('DONE')
    print('******************************************************')
    print()
示例#15
0
def aim2stl(input_file, output_file, transform_file, gaussian, radius, marching_cubes, decimation, visualize, overwrite, func):

  if os.path.isfile(output_file) and not overwrite:
    result = input('File \"{}\" already exists. Overwrite? [y/n]: '.format(output_file))
    if result.lower() not in ['y', 'yes']:
      print('Not overwriting. Exiting...')
      os.sys.exit()
  
  message("Reading AIM file " + input_file)
  reader = vtkbone.vtkboneAIMReader()
  reader.SetFileName(input_file)
  reader.DataOnCellsOff()
  reader.Update()

  image = reader.GetOutput()
  message("Read %d points from AIM file" % image.GetNumberOfPoints())
  image_bounds = image.GetBounds()
  message("Image bounds:", (" %.4f"*6) % image_bounds)
  image_extent = image.GetExtent()
  message("Image extent:", (" %d"*6) % image_extent)

  message("Gaussian smoothing")
  gauss = vtk.vtkImageGaussianSmooth()
  gauss.SetStandardDeviation(gaussian)
  gauss.SetRadiusFactor(radius)
  gauss.SetInputConnection(reader.GetOutputPort())
  gauss.Update()
  message("Total of %d voxels" % gauss.GetOutput().GetNumberOfCells())
  
  message("Padding the data")
  pad = vtk.vtkImageConstantPad()
  pad.SetConstant(0)
  pad.SetOutputWholeExtent(image_extent[0]-1,image_extent[1]+1,
                           image_extent[2]-1,image_extent[3]+1,
                           image_extent[4]-1,image_extent[5]+1)
  pad.SetInputConnection(gauss.GetOutputPort())
  pad.Update()
  message("Total of %d padded voxels" % pad.GetOutput().GetNumberOfCells())

  message("Extracting isosurface")
  mcube = vtk.vtkImageMarchingCubes()
  mcube.ComputeScalarsOff()
  mcube.ComputeNormalsOff()
  mcube.SetValue(0,marching_cubes)
  mcube.SetInputConnection(pad.GetOutputPort())
  mcube.Update()
  message("Generated %d triangles" % mcube.GetOutput().GetNumberOfCells())
  
  if (decimation>0.0):
    message("Decimating the isosurface. Targeting {:.1f}%".format(decimation*100.0))
    deci = vtk.vtkDecimatePro()
    deci.SetInputConnection(mcube.GetOutputPort())
    deci.SetTargetReduction(decimation) # 0 is none, 1 is maximum decimation.
    deci.Update()
    message("Decimated to %d triangles" % deci.GetOutput().GetNumberOfCells())
    mesh = deci
  else:
    message("No decimation of the isosurface")
    mesh = mcube
  
  mesh = applyTransform(transform_file, mesh)
  
  if (visualize):
    mat4x4 = visualize_actors( mesh.GetOutputPort(), None )
  else:
    mat4x4 = vtk.vtkMatrix4x4()
  
  write_stl( mesh.GetOutputPort(), output_file, mat4x4 )
示例#16
0
# Check threads
if args.nThreads < 1:
    os.sys.exit(
        'Number of threads must be one or greater. Given {n}. Exiting...'.
        format(n=args.nThreads))

# Read in inputs
reader1 = vtk.vtkImageReader2Factory.CreateImageReader2(args.inputImage1)
if reader1 is None:
    if args.inputImage1.lower().endswith('.nii'):
        reader1 = vtk.vtkNIFTIImageReader()
    elif args.inputImage1.lower().endswith('.dcm'):
        reader1 = vtk.vtkDICOMImageReader()
    elif vtkboneImported and args.inputImage1.lower().endswith('.aim'):
        reader1 = vtkbone.vtkboneAIMReader()
        reader1.DataOnCellsOff()
    elif vtkbonelabImported and args.inputImage1.lower().endswith('.aim'):
        reader1 = vtkbonelab.vtkbonelabAIMReader()
        reader1.DataOnCellsOff()
    else:
        os.sys.exit(
            'Unable to find a reader for \"{fileName}\". Exiting...'.format(
                fileName=args.inputImage1))
reader1.SetFileName(args.inputImage1)
print('Loading {}...'.format(args.inputImage1))
reader1.Update()

reader2 = vtk.vtkImageReader2Factory.CreateImageReader2(args.inputImage1)
if reader2 is None:
    if args.inputImage2.lower().endswith('.nii'):
示例#17
0
def CreateN88Model(input_file, config_file, correction, transform, overwrite,
                   fixed_boundary):

    # Assign output file name:
    filename, ext = os.path.splitext(input_file)
    if (correction):
        output_file = filename + '.n88model'
    else:
        output_file = filename + '_NOROTATE.n88model'

    # Check if output file already exists, and ask permission to overwrite if not specified:
    if os.path.isfile(output_file) and not overwrite:
        result = eval(
            input('File \"{}\" already exists. Overwrite? [y/n]: '.format(
                output_file)))
        if result.lower() not in ['y', 'yes']:
            print('Not overwriting. Exiting...')
            os.sys.exit()

    # Read input image:
    if not os.path.isfile(input_file):
        os.sys.exit('[ERROR] File \"{}\" not found!'.format(input_file))

    message("Reading AIM file " + input_file + " as input...")
    reader = vtkbone.vtkboneAIMReader()
    reader.SetFileName(input_file)
    reader.DataOnCellsOn()
    reader.Update()

    image = reader.GetOutput()
    if not reader.GetOutput():
        message("[ERROR] No image data read!")
        sys.exit(1)

    message("Image bounds:", (" %.4f" * 6) % image.GetBounds())

    # Read the configuration file:
    message("Reading configuration file...")
    exec(compile(open(config_file).read(), config_file, 'exec'), globals())
    settings_text = ["Parameters from RegN88Config:"]
    settings_text.append("Load                         = %s" % load_input)
    settings_text.append("Bone material id             = %s" %
                         bone_material_id)
    settings_text.append("Bone Young's modulus         = %s" %
                         bone_material_modulus)
    settings_text.append("Bone Poisson's ratio         = %s" %
                         bone_material_poissons_ratio)
    settings_text.append("Bone Surface material id     = %s" %
                         surface_material_id)
    settings_text.append("Bone Surface Young's modulus = %s" %
                         surface_material_modulus)
    settings_text.append("Bone Surface Poisson's ratio = %s" %
                         surface_material_poissons_ratio)
    settings_text.append("Top surface depth            = %s" %
                         top_surface_maximum_depth)
    settings_text.append("Bottom surface depth         = %s\n" %
                         bottom_surface_maximum_depth)
    message(*settings_text)

    # Read the transformation matrix:
    message("Reading IPL transformation matrix...")
    if (transform):
        t_mat = np.loadtxt(fname=transform, skiprows=2)
        rotation = t_mat[:3, :3]

    # Filter the image with a connectivity filter:
    message("Applying connectivity filter...")
    confilt = vtkbone.vtkboneImageConnectivityFilter()
    confilt.SetInputData(image)
    confilt.Update()
    image = confilt.GetOutput()

    # Generate a mesh:
    message("Generating mesh...")
    mesher = vtkbone.vtkboneImageToMesh()
    mesher.SetInputData(image)
    mesher.Update()
    mesh = mesher.GetOutput()

    # Generate material table:
    message("Generating material table...")
    E = bone_material_modulus
    v = bone_material_poissons_ratio
    material_name = 'Linear XT2'
    linear_material = vtkbone.vtkboneLinearIsotropicMaterial()
    linear_material.SetYoungsModulus(E)
    linear_material.SetPoissonsRatio(v)
    linear_material.SetName(material_name)

    material_table = vtkbone.vtkboneMaterialTable()
    material_table.AddMaterial(surface_material_id, linear_material)
    material_table.AddMaterial(bone_material_id, linear_material)

    # Compile the model:
    message("Compiling model...")
    modelConfig = vtkbone.vtkboneApplyTestBase()
    modelConfig.SetInputData(0, mesh)
    modelConfig.SetInputData(1, material_table)

    modelConfig.SetTopConstraintSpecificMaterial(surface_material_id)
    modelConfig.UnevenTopSurfaceOn()
    modelConfig.UseTopSurfaceMaximumDepthOn()
    modelConfig.SetTopSurfaceMaximumDepth(top_surface_maximum_depth)

    modelConfig.SetBottomConstraintSpecificMaterial(surface_material_id)
    modelConfig.UnevenBottomSurfaceOn()
    modelConfig.UseBottomSurfaceMaximumDepthOn()
    modelConfig.SetBottomSurfaceMaximumDepth(bottom_surface_maximum_depth)
    modelConfig.Update()

    model = modelConfig.GetOutput()
    message("Model bounds:", (" %.4f" * 6) % model.GetBounds())

    # Apply boundary conditions:
    message("Setting displacement boundary conditions...")
    e_init = np.array([0, 0, -load_input])

    # Apply fixed boundary conditions (default axial):
    if fixed_boundary == 'uniaxial':
        model.ApplyBoundaryCondition('face_z0',
                                     vtkbone.vtkboneConstraint.SENSE_Z, 0,
                                     'z_fixed')
    elif fixed_boundary == 'axial':
        model.FixNodes('face_z0', 'bottom_fixed')
    else:
        message("[ERROR] Invalid fixed boundary conditions!")
        sys.exit(1)

    # Apply displacement boundary conditions:
    if "S1" in filename:
        message("Setting non-registered boundary conditions.")
        model.ApplyBoundaryCondition('face_x1',
                                     vtkbone.vtkboneConstraint.SENSE_X,
                                     e_init[0], 'x_moved')
        model.ApplyBoundaryCondition('face_y1',
                                     vtkbone.vtkboneConstraint.SENSE_Y,
                                     e_init[1], 'y_moved')
        model.ApplyBoundaryCondition('face_z1',
                                     vtkbone.vtkboneConstraint.SENSE_Z,
                                     e_init[2], 'z_moved')
    else:
        message("Setting registered boundary conditions.")
        if not (correction):
            message(
                "[ERROR] Applying registered boundary conditions to the wrong image!"
            )
            sys.exit(1)

        e_trafo = np.dot(np.linalg.inv(rotation), e_init)
        model.ApplyBoundaryCondition('face_z1',
                                     vtkbone.vtkboneConstraint.SENSE_X,
                                     e_trafo[0], 'x_moved')
        model.ApplyBoundaryCondition('face_z1',
                                     vtkbone.vtkboneConstraint.SENSE_Y,
                                     e_trafo[1], 'y_moved')
        model.ApplyBoundaryCondition('face_z1',
                                     vtkbone.vtkboneConstraint.SENSE_Z,
                                     e_trafo[2], 'z_moved')

    info = model.GetInformation()
    pp_node_sets_key = vtkbone.vtkboneSolverParameters.POST_PROCESSING_NODE_SETS(
    )
    pp_node_sets_key.Append(info, 'face_z1')
    pp_node_sets_key.Append(info, 'face_z0')

    model.AppendHistory("Created with blRegN88ModelGenerator version 1.1")

    # Write the n88model file:
    message("Writing n88model file...")
    writer = vtkbone.vtkboneN88ModelWriter()
    writer.SetInputData(model)
    writer.SetFileName(output_file)
    writer.Update()

    message("Done. Have a nice day!")
示例#18
0
def Muscle(input_filename, converted_filename, segmentation_filename, bone_threshold, smoothing_iterations, segmentation_iterations, segmentation_multiplier, initial_neighborhood_radius, closing_radius, csv_filename='', tiff_filename='', histogram_filename=''):
    # Python 2/3 compatible input
    from six.moves import input

    # Input must be an AIM
    if not input_filename.lower().endswith('.aim'):
        os.sys.exit('[ERROR] Input \"{}\" must be an AIM'.format(input_filename))

    # Read input
    if not os.path.isfile(input_filename):
        os.sys.exit('[ERROR] Cannot find file \"{}\"'.format(input_filename))

    # Internal constants
    bone_label = 1
    muscle_label = 2

    # Compute calibration constants
    print('Computing calibration constants')
    reader = vtkbone.vtkboneAIMReader()
    reader.SetFileName(input_filename)
    reader.DataOnCellsOff()
    reader.Update()
    m,b = get_aim_density_equation(reader.GetProcessingLog())
    del reader
    print('  m: {}'.format(m))
    print('  b: {}'.format(b))
    print('')

    # Converting image
    print('Converting {} to {}'.format(input_filename, converted_filename))
    ImageConverter(input_filename, converted_filename, overwrite=True)
    print('')

    print('Reading in converted image')
    image = sitk.ReadImage(converted_filename)

    # Segment bone
    print('Segmenting bone')
    seg_bone = segment_bone(image, (bone_threshold - b)/m)
    seg_bone = (seg_bone>0)*bone_label
    print('')

    # Find centroid
    print('Finding centroid of the two largest bones')
    stat_filter = sitk.LabelShapeStatisticsImageFilter()
    stat_filter.Execute(sitk.RelabelComponent(sitk.ConnectedComponent(seg_bone)))
    centroids = [
        stat_filter.GetCentroid(1),
        stat_filter.GetCentroid(2)
    ]
    seed = [0 for i in range(len(centroids[0]))]
    for i in range(len(seed)):
        seed[i] = 0.5*(centroids[0][i] + centroids[1][i])
    seed_index = image.TransformPhysicalPointToIndex(seed)
    print('  Centroid1:       {}'.format(centroids[0]))
    print('  Centroid2:       {}'.format(centroids[1]))
    print('  Seed (physical): {}'.format(seed))
    print('  Seed (index):    {}'.format(seed_index))
    print('')

    # Smooth image
    print('Performing anisotropic smoothing')
    timeStep = image.GetSpacing()[0] / 2.0**4
    smooth_image = sitk.GradientAnisotropicDiffusion(
        sitk.Cast(image, sitk.sitkFloat32),
        timeStep=timeStep,
        numberOfIterations=smoothing_iterations
    )
    print('')

    # Segment muscle
    print('Segmenting muscle')
    radius = int(max(1, initial_neighborhood_radius/image.GetSpacing()[0]))
    print('  initialNeighborhoodRadius [vox]: {}'.format(radius))
    seg_muscle = sitk.ConfidenceConnected(
        smooth_image,
        seedList=[seed_index],
        numberOfIterations=segmentation_iterations,
        multiplier=segmentation_multiplier,
        initialNeighborhoodRadius=radius,
        replaceValue=1
    )

    # Take largest component
    seg_muscle = (sitk.RelabelComponent(sitk.ConnectedComponent(seg_muscle>0))==1)*muscle_label
    print('')

    # One, solid peice of background
    print('Cleaning up segmentation')
    vector_radius = [int(max(1, closing_radius//s)) for s in image.GetSpacing()]
    print('  Closing radius [mm]:    {}'.format(closing_radius))
    print('  Vector radius [voxels]: {}'.format(vector_radius))
    seg = (seg_bone+seg_muscle)>0
    seg = sitk.BinaryDilate(seg, vector_radius)
    background = sitk.RelabelComponent(sitk.ConnectedComponent(seg<1))==1
    seg_muscle = sitk.BinaryErode(background<1, vector_radius)*muscle_label
    print('')

    # Join segmentation
    seg_muscle = sitk.Mask(seg_muscle, 1-(seg_bone>0))
    seg = seg_bone + seg_muscle
    seg = sitk.Cast(seg, sitk.sitkInt8)

    # Write segmentation
    print('Writing segmentation to ' + segmentation_filename)
    sitk.WriteImage(seg, segmentation_filename)
    print('')

    print('Performing quantification')
    # Quantify Density
    intensity_filter = sitk.LabelIntensityStatisticsImageFilter()
    intensity_filter.Execute(seg, image)
    muscle_density = intensity_filter.GetMean(muscle_label)

    # Quantify cross sectional area
    # Note that since
    stat_filter = sitk.LabelShapeStatisticsImageFilter()
    stat_filter.Execute(seg)
    ave_cross_area = float(stat_filter.GetNumberOfPixels(muscle_label)) / seg.GetSize()[2]

    print('  density:       {}'.format(muscle_density))
    print('  cross section: {}'.format(ave_cross_area))
    print('')

    # Write results
    entry = OrderedDict()
    entry['Filename'] = input_filename
    entry['Spacing.X [mm]'] = image.GetSpacing()[0]
    entry['Spacing.Y [mm]'] = image.GetSpacing()[1]
    entry['Spacing.Z [mm]'] = image.GetSpacing()[2]
    entry['density_slope'] = m
    entry['density_intercept'] = b
    entry['muscle density [native]'] = muscle_density
    entry['A.Cross [vox^2]'] = ave_cross_area
    print(echo_arguments('Muscle Outcomes:', entry))

    # Write CSV
    if len(csv_filename)>0:
        print('  Writing to csv file ' + csv_filename)
        write_csv(entry, csv_filename)

    # Write TIFF
    if len(tiff_filename)>0:
        overlay = sitk.LabelOverlay(
            sitk.Cast(sitk.IntensityWindowing(
                sitk.Cast(image, sitk.sitkFloat32),
                windowMinimum = 0,
                windowMaximum = (bone_threshold - b)/m,
                outputMinimum = 0.0,
                outputMaximum = 255.0
            ), sitk.sitkUInt8),
            seg,
            opacity=0.3
        )

        size = list(overlay.GetSize())
        index = [0, 0, int(size[2]//2)]
        size[2]=0

        #Step 5: Extract that specific slice using the Extract Image Filter
        tiff_image = sitk.Extract(
            overlay,
            size=size,
            index=index
        )

        print('  Save single slice to ' + tiff_filename)
        sitk.WriteImage(tiff_image, tiff_filename)

    # Create histogram
    if len(histogram_filename)>0:
        import matplotlib.pyplot as plt
        print('Saving histogram to ' + histogram_filename)

        data = m*sitk.GetArrayFromImage(image)+b
        mask = sitk.GetArrayFromImage(seg)
        data = data.ravel()
        mask = mask.ravel()

        plt.figure(figsize=(8, 6))
        plt.hist(data[mask==muscle_label], bins=1000, density=True)
        plt.xlabel('Density [mg HA/ccm]')
        plt.ylabel('Normalized Count')
        plt.pause(0.1)
        plt.savefig(histogram_filename)