Esempio n. 1
0
def main():
    # Setup description
    description = '''Convert from one image type to another

If a processing log is set, it is appended to the processing log of the
input AIM file. If the input is not an AIM file, then the processing log
is set as given.

Patient positioning in MINC and NIFTI file formats is not explicitely
handled.
'''

    # Setup argument parsing
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        prog="blImageConverter",
        description=description)
    parser.add_argument('input_filename', help='Input image file')
    parser.add_argument('output_filename', help='Output image file')
    parser.add_argument('-l',
                        '--processing_log',
                        default='',
                        help='Processing log to add to AIM images')
    parser.add_argument('-o',
                        '--overwrite',
                        action='store_true',
                        help='Overwrite output without asking')

    # Parse and display
    args = parser.parse_args()
    print(echo_arguments('ImageConverter', vars(args)))

    # Run program
    ImageConverter(**vars(args))
Esempio n. 2
0
def main():

    description = '''Generates an n88model from registered images.'''

    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        prog='blRegBCn88modelgenerator',
        description=description)
    parser.add_argument('input_file',
                        help='Input image file (*REG_HOM_LS.AIM).')
    parser.add_argument('config_file', help='Configuration file (*.conf).')
    parser.add_argument(
        '-c',
        '--correction',
        action='store_true',
        help='Apply transformed boundary conditions from 3D registration.')
    parser.add_argument('-t',
                        '--transform',
                        help='Transformation matrix (*.txt).')
    parser.add_argument('-o',
                        '--overwrite',
                        action='store_true',
                        help='Overwrite existing output.')
    parser.add_argument('-b',
                        '--fixed_boundary',
                        default='axial',
                        help='Fixed boundary type (uniaxial/axial)')

    # Parse and display arguments:
    args = parser.parse_args()
    print((echo_arguments('blRegBCn88modelgenerator', vars(args))))

    # Run program HERE
    CreateN88Model(**vars(args))
Esempio n. 3
0
def main():
    # Setup description
    description = '''Convert a series of 2D images to one 3D image

Example usage:

    blPseudoCT ZTE_DATASET/1700-PUZTE_FA_1_NEX2_1.3mm 1700-PUZTE_FA_1_NEX2_1.3mm_CT
    
    blPseudoCT /Volumes/ZTE_Study/1700-PUZTE_FA_1_NEX2_1.3mm/ /Volumes/ZTE_Study/1700-PUZTE_FA_1_NEX2_1.3mm_CT/
    blPseudoCT /Volumes/ZTE_Study/1100-PUZTE_FA_1_NEX2_1.3mm/ /Volumes/ZTE_Study/1100-PUZTE_FA_1_NEX2_1.3mm_CT/
    blPseudoCT /Volumes/ZTE_Study/700-PUZTE_FA_1_NEX2_1.3mm/ /Volumes/ZTE_Study/700-PUZTE_FA_1_NEX2_1.3mm_CT/
    blPseudoCT /Volumes/ZTE_Study/400-PUZTE_FA_1_NEX2_1.3mm/ /Volumes/ZTE_Study/400-PUZTE_FA_1_NEX2_1.3mm_CT/

There are a great deal of details in the DICOM standard and not
all are handled by this function. If you experience problematic outputs,
please consult a commercial DICOM importer.

Stacking of slices will be determined by GDCM if the input is a DICOM
series, otherwise an alpha-numeric sort.

The output will be a DICOM file.
'''

    # Setup argument parsing
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        prog="blPseudoCT",
        description=description)
    parser.add_argument('input_directory', help='Input DICOM series directory')
    parser.add_argument('output_directory',
                        help='Output DICOM series directory')
    parser.add_argument(
        '-e',
        '--expression',
        default='*',
        type=str,
        help='An expression for matching files (default: %(default)s)')
    parser.add_argument('-o',
                        '--overwrite',
                        action='store_true',
                        help='Overwrite output without asking')
    parser.add_argument('-v',
                        '--verbose',
                        action='store_true',
                        help='Verbose output')

    # Parse and display
    args = parser.parse_args()
    print(echo_arguments('PseudoCT', vars(args)))

    # Run program
    PseudoCT(**vars(args))
Esempio n. 4
0
def main():
    # Set constant variables
    environment_location = os.environ.get(
        BL_DATA_DIRECTORY_ENVIRONMENT_VARIABLE)
    output_directory = os.getenv(BL_DATA_DIRECTORY_ENVIRONMENT_VARIABLE,
                                 BL_DATA_DIRECTORY_DEFAULT)

    description = '''Download the bonelab dataset.

This function fetches the bonelab dataset and stores it locally on your achine.
This repository is good for testing scripts and creating simple programs.

The default URL is \"{BL_DATA_DEFAULT_URL}\". This can be changed with the `--url` flag.

The output directory is resolved in the following order:
    - First, it is assigned to ~/.bldata (Currently \"{BL_DATA_DIRECTORY_DEFAULT}\")
    - Next, if the environment variable {ENV_VAR} is set, it defaults there (Current \"{environment_location}\")
    - Finally, it can be specified at the command line with the flag `--output_directory``
Note that the order of precedence is `--output_directory` > {ENV_VAR} > ~/.bldata

`svn export` is used for downloading the data. This required you to have svn installed on your system.
'''.format(BL_DATA_DEFAULT_URL=BL_DATA_DEFAULT_URL,
           BL_DATA_DIRECTORY_DEFAULT=BL_DATA_DIRECTORY_DEFAULT,
           ENV_VAR=BL_DATA_DIRECTORY_ENVIRONMENT_VARIABLE,
           environment_location=environment_location)

    # Setup argument parsing
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        prog="blDownloadData",
        description=description)
    parser.add_argument('--url',
                        default=BL_DATA_DEFAULT_URL,
                        help='URL for downloading data')
    parser.add_argument('--output_directory',
                        default=output_directory,
                        help='Local directory to store data in')
    parser.add_argument('--no_verify',
                        '-n',
                        action='store_true',
                        help='Prompt user to verify url and output_directory')

    # Parse and display
    args = parser.parse_args()
    print(echo_arguments('DownloadData', vars(args)))

    # Run program
    DownloadData(**vars(args))
Esempio n. 5
0
def main():
    # Setup description
    description='''2D slice visualizer

The following keyboard mappings are available:
    w   Print window/Level to terminal
    n   Set interpolator to nearest neighbour
    c   Set interpolator to cubic
    r   Reset window/level
    x   View in x-plane
    y   View in y-plane
    z   View in z-plane
    q   Quit

The following mouse mappings are available:
    left click + vertical scroll                Modify window
    left click + horizontal scroll              Modify level
    right click + vertical scroll               Zoom
    control + left click + vertical scroll      Slice level
    control + right click + vertical scroll     Rotate slice
    shift + left click + vertical scroll        Translate slice
'''

    # Setup argument parsing
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        prog="blSliceViewer",
        description=description
    )
    parser.add_argument('input_filename', help='Input image file')
    parser.add_argument('--window',
                    default=float(0), type=float,
                    help='The initial window. If window is zero or less, the window is computed from the dynamic range of the image.')
    parser.add_argument('--level',
                        default=float(0), type=float,
                        help='The initial level. If window is zero or less, the level is computed from the dynamic range of the image.')
    parser.add_argument('--nThreads', '-n', 
                        default=int(1), type=int,
                        help='Number of threads for each image slice visualizer (default: %(default)s)')

    # Parse and display
    args = parser.parse_args()
    print(echo_arguments('SliceViewer', vars(args)))

    # Run program
    SliceViewer(**vars(args))
Esempio n. 6
0
def main():
    # Setup description
    description='''Convert a series of 2D images to one 3D image

Example usage:
    blImageSeries2Image ~/.bldata/dicom dicom.nii

There are a great deal of details in the DICOM standard of which not
all are handled by this function. If you experience nuanced outputs,
please consult a commercial DICOM importer.

Stacking of slices will be determined by GDCM if the input is a DICOM
series, otherwise an alpha-numeric sort.

If you are not reading in a DICOM, it is highly recommended to set a
matching expression with `-e` to avoid extra files - like .DS_store,
text files, other images - from being input into the stack. If the
script fails, this is almost always the problem. `expression` is
ignored for DICOM data.

The output cannot be an AIM type. If you require an AIM, please write
to an intermediate type (say, .nii) and then convert to AIM using
`blImageConvert`.
'''

    # Setup argument parsing
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        prog="blImageSeries2Image",
        description=description
    )
    parser.add_argument('input_directory', help='Input image')
    parser.add_argument('output_filename', help='Output converted image')
    parser.add_argument( '-e', '--expression', default='*', type=str,
                        help='An expression for matching files (default: %(default)s)')
    parser.add_argument('-o', '--overwrite', action='store_true', help='Overwrite output without asking')
    parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')

    # Parse and display
    args = parser.parse_args()
    print(echo_arguments('ImageSeries2Image', vars(args)))

    # Run program
    ImageSeries2Image(**vars(args))
Esempio n. 7
0
def main():

    description = '''Tabulates all transformation matrices from 3D registration.'''

    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        prog='blRegN88TabulateTransform',
        description=description
    )
    parser.add_argument('transform_files', nargs='*',
                        help='Array of transformation matrices (*.txt).')
    parser.add_argument('-o', '--output_file', default='REPO_transforms.csv',
                        help='Output file name.')

    # Parse and display arguments:
    args = parser.parse_args()
    print((echo_arguments('RegN88TabulateTransform', vars(args))))

    # Run program HERE
    TransformTabulateResults(**vars(args))
Esempio n. 8
0
def main():
    # Setup description
    description = '''Reads segmented image and writes specified labels.

Valid input and output formats include: 
.nii, .nii.gz

Currently only accepts NIFTI file formats as input and output.

'''

    # Setup argument parsing
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        prog="blImageFilter",
        description=description)
    parser.add_argument('input_filename',
                        help='Input image file (*.nii, *.nii.gz)')
    parser.add_argument('output_filename',
                        help='Output image file (*.nii, *.nii.gz)')
    parser.add_argument(
        '--range',
        type=int,
        nargs=2,
        default=[0, 10],
        metavar='0',
        help='Set range of scalars to keep (default: %(default)s)')
    parser.add_argument('-o',
                        '--overwrite',
                        action='store_true',
                        help='Overwrite output without asking')

    # Parse and display
    args = parser.parse_args()
    print(echo_arguments('ImageFilter', vars(args)))

    # Run program
    ImageFilter(**vars(args))
Esempio n. 9
0
def main():

    description = '''Transforms Faim outputs generated from solving a RegBC N88 model.'''

    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        prog='blRegN88TransformResults',
        description=description)
    parser.add_argument(
        'data_file', help='File containing data from registered FE analysis.')
    parser.add_argument(
        'transform_file',
        help='File containing transformations from 3D registration (.csv).')
    parser.add_argument('-o',
                        '--output_file',
                        default='REPO_transformed_results.csv',
                        help='Transformed output file (.csv).')

    # Parse and display arguments:
    args = parser.parse_args()
    print((echo_arguments('RegN88TransformResults', vars(args))))

    # Run program HERE
    TransformResults(**vars(args))
Esempio n. 10
0
def main():
    # Setup argument parsing
    parser = argparse.ArgumentParser(prog="aimod",
                                     description='''AIM information examine

Reads an AIM and allows user to modify image information.
The following characters are options during modification.
    'q' - Quit
    'e' - Exit modification mode (write the AIM)

A reimplementation of the seminal SCANCO program `aimod`. All
efforts have been made to preserve the style.

vtk only supports modification of spacing, origin, and dimensions.
''')
    parser.add_argument('input_aim', help='Input aim')
    parser.add_argument('output_aim', help='Output aim')

    # Parse and display
    args = parser.parse_args()
    print(echo_arguments('aimod', vars(args)))

    # Run program
    aimod(**vars(args))
Esempio n. 11
0
def main():
    # Setup description
    description = '''Overlay segmentation on visualization

The following keyboard mappings are available:
    w   Print window/Level to terminal
    n   Set interpolator to nearest neighbour
    c   Set interpolator to cubic
    r   Reset window/level
    x   View in x-plane
    y   View in y-plane
    z   View in z-plane
    q   Quit

The following mouse mappings are available:
    left click + vertical scroll                Modify window
    left click + horizontal scroll              Modify level
    right click + vertical scroll               Zoom
    control + left click + vertical scroll      Slice level
    control + right click + vertical scroll     Rotate slice
    shift + left click + vertical scroll        Translate slice
'''

    # Setup argument parsing
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        prog="blVisualizeSegmentation",
        description=description)
    parser.add_argument('input_filename', help='Input image')
    parser.add_argument('segmentation_filename',
                        help='Input segmentation image')
    parser.add_argument('--window',
                        default=float(500),
                        type=float,
                        help='The initial window')
    parser.add_argument('--level',
                        default=float(0),
                        type=float,
                        help='The initial level')
    parser.add_argument(
        '--nThreads',
        '-n',
        default=int(1),
        type=int,
        help=
        'Number of threads for each image slice visualizer (default: %(default)s)'
    )
    parser.add_argument(
        '--opacity',
        '-o',
        default=float(0.25),
        type=float,
        help=
        'The opacity of the segmentation between zero and one (default: %(default)s)'
    )

    # Parse and display
    args = parser.parse_args()
    print(echo_arguments('VisualizeSegmentation', vars(args)))

    # Run program
    VisualizeSegmentation(**vars(args))
Esempio n. 12
0
def main():
    # Setup description
    description='''Muscle segmentation and quantification

Example usage:
    blMuscle ~/.bldata/D0010131.AIM D0010131.nii D0010131_SEG.nii --csv_filename muscle.csv --tiff_filename D0010131_MUSCLE.tif

It is assumed that the voxel coordinate z-axis corresponds to the
proximal-distal direction in the image. If strange cross sectional
areas are being found, check the `converted_filename` image
orientation in itksnap.

It is assumed the image is tightly cropped to the bone in the z-axes.
This would affect the cross sectional area calculation by adding zero
values before averaging.

Finally, it is assumed that the image voxels are isotropic.

Output TIFFs have been window/leveled to have display range (0, `bone_threshold`).

To compute the real density and cross sectional area, use the following formulas:
    density = density_slope * 'muscle density [native]' + density_intercept
    Cross.A = 'Spacing.X [mm]' * 'Spacing.Y [mm]' * 'A.Cross [vox^2]'

Note that for very poor quality muscles, it is possible to get negative density
values. Of course this is not reasonable. The source of this error is that
the calibration equation is a linearization around bone of a true, non-linear
calibration equation. Alternatively, a calibration phantom more appropriate for
density and fat could be provided.
'''

    # Setup argument parsing
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        prog="blMuscle",
        description=description
    )
    parser.add_argument('input_filename', help='Input image')
    parser.add_argument('converted_filename', help='Output converted image (typically .nii)')
    parser.add_argument('segmentation_filename', help='Input image (typically .nii)')
    parser.add_argument('--csv_filename', '-o',
                        default='', type=str,
                        help='Write results to CSV file (empty string causes no write)')
    parser.add_argument('--tiff_filename', '-i',
                        default='', type=str,
                        help='Write one slice to a TIFF image (empty string causes no write)')
    parser.add_argument('--bone_threshold',
                        default=800.0, type=float,
                        help='Threshold for selecting bone (default: %(default)s mg HA/cc)')
    parser.add_argument('--smoothing_iterations',
                        default=10, type=int,
                        help='Number of iterations of Perona-Malik anisotropic filtering (default: %(default)s)')
    parser.add_argument('--segmentation_iterations',
                        default=2, type=int,
                        help='Number of iterations in confidence connected thresholding (default: %(default)s)')
    parser.add_argument('--segmentation_multiplier',
                        default=2.0, type=float,
                        help='Multiplier in confidence connected thresholding (default: %(default)s)')
    parser.add_argument('--initial_neighborhood_radius',
                        default=1.0, type=float,
                        help='Initial neighborhood radius in confidence connected thresholding (default: %(default)s [mm])')
    parser.add_argument('--closing_radius',
                        default=0.5, type=float,
                        help='Morphological closing radius for cleaning up image (default: %(default)s [mm]) ')
    parser.add_argument('--histogram_filename',
                        default='', type=str,
                        help='Create a histogram and save to the defined file (typically, a TIFF or PDF)')

    # Parse and display
    args = parser.parse_args()
    print(echo_arguments('Muscle', vars(args)))

    # Run program
    Muscle(**vars(args))
Esempio n. 13
0
def main():
    # Setup description
    description = '''Extract field on elements from N88 model

Take values stored in the elements of a N88 model and extract them
into an image. This can be used for visualization with uct_3d or
for other processing requirements. Possible choices of field names
include: {valid_fields}

Example usage:
    blExtractFields test25a_uniaxial.n88model test25a_sed.aim

Note that the domain of the finite element model does not match
the domain of the image. For voxels that do not have a corresponding
element, a value must still be placed there.

This solution manually indexes the hexahedron elements and may be
slow for very large models.

If you are using this script to visualize a field with uct_3d, the
output_type must be either char or short. If lower_threshold and
upper_threshold are specified and not equal, the field will be
truncated between these two values and rescaled to the min/max of
the datatype range. If lower_threshold equals upper_threshold,
the dynamic range of the image will be mapped to the range of the
output type.

Valid output types include: {convert_types}
'''.format(valid_fields=', '.join(valid_fields()),
           convert_types=', '.join(EXTRACT_FIELDS_SUPPORTED_TYPES.keys()))

    # Setup argument parsing
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        prog="blExtractFields",
        description=description)
    parser.add_argument('input_model', help='Input N88 model')
    parser.add_argument('output_image', help='Output image filename')
    parser.add_argument('-f',
                        '--field_name',
                        default='StrainEnergyDensity',
                        type=str,
                        help='Field name to extract (default: %(default)s)')
    parser.add_argument(
        '-o',
        '--outside_value',
        default=0.0,
        type=float,
        help=
        'Value to assign voxels where there is no field (default: %(default)s)'
    )
    parser.add_argument(
        '-l',
        '--lower_threshold',
        default=0.0,
        type=float,
        help=
        'If output type is not float, what is the lower threshold for rescaling? (default: %(default)s)'
    )
    parser.add_argument(
        '-u',
        '--upper_threshold',
        default=0.0,
        type=float,
        help=
        'If output type is not float, what is the upper threshold for rescaling? (default: %(default)s)'
    )
    parser.add_argument('-t',
                        '--output_type',
                        default='float',
                        type=str,
                        help='Specify the output type (default: %(default)s)')

    # Parse and display
    args = parser.parse_args()
    print(echo_arguments('ExtractFields', vars(args)))

    # Run program
    ExtractFields(**vars(args))
Esempio n. 14
0
def main():
    # Setup description
    description = '''Convert a 3D image to a series of 2D Images

Example usage:
    blImage2ImageSeries ~/.bldata/test25a.nii temp/test25a

Output files will be numbered according to last dimension slice number
and formatted as follows:
    '{output_filename}_{number}.{extension}'
Output files will be overwritten if they exist. The method will
automatically figure out the number of digits required. However, if you
want your filenames to have a specific number of zeros greater than that
required, please set `number_of_digits`.

AIM files are not supported. It is recommended to use blImageConvert
if you would like to serialize an AIM.
    blImageConvert filename.aim filename.nii
    blImage2ImageSeries filename.nii filename

Currently, only single component images are supported. Please submit
an issue if support for Vector images is required.

Writing of DICOM series is not supported because it requires delicate
handling of the DICOM tags. Please see the SimpleITK DICOM documentation
to write your own DICOM series writer.

Writing is performed using the SimpleITK ImageSeriesWriter. Supported
extension include:
    bmp
    tif tiff
    png
    jpeg jpg

Many filetypes used for 2D images (png, bmp, jpg) only support a limited
number of data types. For this reason, the image may need to be cast to a
supporting type. By default, the algorithm will stretch the dynamic range
of the image to fit optimally inside the new type. However, if you do not
want the datatype to be stretch, set the flag `--no_rescale` to cause
direct conversion. This may cause loss of data, for example when going
from a signed to unsigned type.
'''

    # Setup argument parsing
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        prog="blImage2ImageSeries",
        description=description)
    parser.add_argument('input_filename', help='Input image')
    parser.add_argument('output_filename',
                        help='Output converted image prefix')
    parser.add_argument(
        '-e',
        '--extension',
        default='bmp',
        type=str,
        help='Filename extension without a period (default: %(default)s)')
    parser.add_argument(
        '-n',
        '--number_of_digits',
        default=-1,
        type=int,
        help='Number of digits to format with (default: %(default)d)')
    parser.add_argument('-v',
                        '--verbose',
                        action='store_true',
                        help='Verbose output')
    parser.add_argument('--no_rescale',
                        action='store_true',
                        help='Rescale pixel types on casting')

    # Parse and display
    args = parser.parse_args()
    print(echo_arguments('Image2ImageSeries', vars(args)))

    # Run program
    Image2ImageSeries(**vars(args))
Esempio n. 15
0
 def test_empty_dictionary(self):
     expected = 'title:' + os.linesep
     self.assertEqual(echo_arguments('title', {}), expected)
Esempio n. 16
0
 def test_simple_dictionary(self):
     expected = 'title:' + os.linesep + '  a  1' + os.linesep
     self.assertEqual(echo_arguments('title', {'a': 1}), expected)
Esempio n. 17
0
def main():
    # Setup description
    description='''
A general utility for creating STL files used for rapid prototyping from 
AIMs and back again.

This is a collection of utilities that together provide significant 
opportunities for creating complex STL files from input AIMs as well
as conversion back to an AIM format. 

aim2stl         : takes a thresholded AIM file and applies isosurface for STL
stl2aim         : takes an STL model and converts to a thresholded AIM

view_stl        : view an STL model
boolean_stl     : join, intersect or difference of two STL models
add_stl         : add two STL models (an alternative to 'join' boolean_stl)

create_sign     : make a sign with text
create_sphere   : make a sphere
create_cylinder : make a cylinder
create_cube     : make a cube

Input AIM must be type 'char'. STL model mesh properties can be controlled 
by setting gaussian smoothing and isosurface. The number of polygons
can be reduced (decimation).

Visualization can be performed to view STL models. Some useful keys:

 'a' - actor mode (mouse, shift, control to manipulate) [REQUIRED]
 'c' - camera mode  (mouse, shift, control to manipulate)
 't' - trackball
 'j' - joystick
 'w' - wireframe
 's' - solid surface
 'u' – a user-defined function prints 4x4 transform.
 'p' – a user-defined function picks a point.
 'd' – a user-defined function deletes a point.
 'o' – a user-defined function outputs file of points.
 'q' - quits
 
When you quit from the visualization the model will be printed with the new 
transform if you have applied one.
'''
    epilog='''
To see the options for each of the utilities, type something like this:
$ blRapidPrototype create_cube --help
'''
    # Setup argument parsing
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        prog="blRapidPrototype",
        description=description,
        epilog=epilog
    )
    subparsers = parser.add_subparsers()
    
    # parser for aim2stl
    parser_aim2stl = subparsers.add_parser('aim2stl')
    parser_aim2stl.add_argument('input_file', action=CheckExt({'aim','AIM'}), help='Input AIM image file name')
    parser_aim2stl.add_argument('output_file', action=CheckExt({'stl','STL'}), help='Output STL image file name')
    parser_aim2stl.add_argument('--transform_file', default="None", action=CheckExt({'txt','TXT'}), metavar='FILE', help='Apply a 4x4 transform from a file (default: %(default)s)')
    parser_aim2stl.add_argument('--gaussian', type=float, default=0.7, metavar='GAUSS', help='Gaussian standard deviation (default: %(default)s)')
    parser_aim2stl.add_argument('--radius', type=float, default=1.0, metavar='RADIUS', help='Gaussian radius support (default: %(default)s)')
    parser_aim2stl.add_argument('--marching_cubes', type=float, default=50.0, metavar='MC', help='Marching cubes threshold (default: %(default)s)')
    parser_aim2stl.add_argument('--decimation', type=float, default=0.0, metavar='DEC', choices=Range(0.0,1.0), help='Decimation is 0 (none) to 1 (max) (default: %(default)s)')
    parser_aim2stl.add_argument('--visualize', action='store_true', help='Visualize the model (default: %(default)s)')
    parser_aim2stl.add_argument('--overwrite', action='store_true', help='Overwrite output without asking (default: %(default)s)')
    parser_aim2stl.set_defaults(func=aim2stl)
    
    # parser for stl2aim
    parser_stl2aim = subparsers.add_parser('stl2aim')
    parser_stl2aim.add_argument('input_file', action=CheckExt({'stl','STL'}), help='Input STL image file name')
    parser_stl2aim.add_argument('output_file', action=CheckExt({'aim','AIM'}), help='Output AIM image file name')
    parser_stl2aim.add_argument('--transform_file', default="None", action=CheckExt({'txt','TXT'}), metavar='FILE', help='Apply a 4x4 transform from a file (default: %(default)s)')
    parser_stl2aim.add_argument('--el_size_mm', type=float, nargs=3, default=[0.0607,0.0607,0.0607], metavar='0', help='Set element size (default: %(default)s)')
    parser_stl2aim.add_argument('--visualize', action='store_true', help='Visualize the model (default: %(default)s)')
    parser_stl2aim.add_argument('--overwrite', action='store_true', help='Overwrite output without asking (default: %(default)s)')
    parser_stl2aim.set_defaults(func=stl2aim)
    
    # parser for view_stl
    parser_view_stl = subparsers.add_parser('view_stl')
    parser_view_stl.add_argument('input_file', action=CheckExt({'stl','STL'}), help='Input STL image file name')
    parser_view_stl.add_argument('--transform_file', default="None", action=CheckExt({'txt','TXT'}), metavar='FILE', help='Apply a 4x4 transform from a file (default: %(default)s)')
    parser_view_stl.set_defaults(func=view_stl)

    # parser for add_stl
    parser_add_stl = subparsers.add_parser('add_stl')
    parser_add_stl.add_argument('input_file1', action=CheckExt({'stl','STL'}), help='Input STL image file name')
    parser_add_stl.add_argument('input_file2', action=CheckExt({'stl','STL'}), help='Input STL image file name (allows transform)')
    parser_add_stl.add_argument('output_file', action=CheckExt({'stl','STL'}), help='Output STL image file name')
    parser_add_stl.add_argument('--transform_file', default="None", action=CheckExt({'txt','TXT'}), metavar='FILE', help='Apply a 4x4 transform from a file to input_file1 (default: %(default)s)')
    parser_add_stl.add_argument('--visualize', action='store_true', help='Visualize the model (default: %(default)s)')
    parser_add_stl.add_argument('--overwrite', action='store_true', help='Overwrite output without asking (default: %(default)s)')
    parser_add_stl.set_defaults(func=add_stl)
    
    # parser for boolean_stl
    parser_boolean_stl = subparsers.add_parser('boolean_stl')
    parser_boolean_stl.add_argument('input_file1', action=CheckExt({'stl','STL'}), help='Input STL image file name')
    parser_boolean_stl.add_argument('input_file2', action=CheckExt({'stl','STL'}), help='Input STL image file name (allows transform)')
    parser_boolean_stl.add_argument('output_file', action=CheckExt({'stl','STL'}), help='Output STL image file name')
    parser_boolean_stl.add_argument('--operation', default="union", choices=['union', 'intersection', 'difference'], metavar='OP', help='Valid operations are union, intersection or difference (default: %(default)s)')
    parser_boolean_stl.add_argument('--transform_file', default="None", action=CheckExt({'txt','TXT'}), metavar='FILE', help='Apply a 4x4 transform from a file to input_file1 (default: %(default)s)')
    parser_boolean_stl.add_argument('--visualize', action='store_true', help='Visualize the model (default: %(default)s)')
    parser_boolean_stl.add_argument('--overwrite', action='store_true', help='Overwrite output without asking (default: %(default)s)')
    parser_boolean_stl.set_defaults(func=boolean_stl)
    
    # parser for create_sign
    parser_create_sign = subparsers.add_parser('create_sign')
    parser_create_sign.add_argument('output_file', action=CheckExt({'stl','STL'}), help='Output STL image file name')
    parser_create_sign.add_argument('--transform_file', default="None", action=CheckExt({'txt','TXT'}), metavar='FILE', help='Apply a 4x4 transform from a file (default: %(default)s)')
    parser_create_sign.add_argument('--text', default="Hello!", help='Letters for 3D rendering (default: %(default)s)')
    parser_create_sign.add_argument('--width', type=float, help='Set width of text')
    parser_create_sign.add_argument('--height', type=float, help='Set height of text')
    parser_create_sign.add_argument('--depth', type=float, default=0.5, help='Set depth of text (default: %(default)s mm)')
    parser_create_sign.add_argument('--nobacking', action='store_true', help='Suppress adding a backing to the text (default: %(default)s)')
    parser_create_sign.add_argument('--visualize', action='store_true', help='Visualize the model (default: %(default)s)')
    parser_create_sign.add_argument('--overwrite', action='store_true', help='Overwrite output without asking (default: %(default)s)')
    parser_create_sign.set_defaults(func=create_sign)
    
    # parser for create_sphere
    parser_create_sphere = subparsers.add_parser('create_sphere')
    parser_create_sphere.add_argument('output_file', action=CheckExt({'stl','STL'}), help='Output STL image file name')
    parser_create_sphere.add_argument('--transform_file', default="None", action=CheckExt({'txt','TXT'}), metavar='FILE', help='Apply a 4x4 transform from a file (default: %(default)s)')
    parser_create_sphere.add_argument('--radius', type=float, default=0.5, help='Sphere radius (default: %(default)s)')
    parser_create_sphere.add_argument('--phi', type=int, default=8, metavar='RES', help='Sphere phi_resolution (default: %(default)s)')
    parser_create_sphere.add_argument('--theta', type=int, default=8, metavar='RES', help='Sphere theta_resolution (default: %(default)s)')
    parser_create_sphere.add_argument('--phi_start', type=int, default=0, choices=range(0,180), metavar='RES', help='Sphere phi start angle (default: %(default)s)')
    parser_create_sphere.add_argument('--phi_end', type=int, default=180, choices=range(0,180), metavar='RES', help='Sphere phi end angle (default: %(default)s)')
    parser_create_sphere.add_argument('--theta_start', type=int, default=0, choices=range(0,360), metavar='RES', help='Sphere theta start angle (default: %(default)s)')
    parser_create_sphere.add_argument('--theta_end', type=int, default=360, choices=range(0,360), metavar='RES', help='Sphere theta end angle (default: %(default)s)')
    parser_create_sphere.add_argument('--center', type=float, nargs=3, default=[0,0,0], metavar='0', help='Sphere center (default: %(default)s)')
    parser_create_sphere.add_argument('--visualize', action='store_true', help='Visualize the model (default: %(default)s)')
    parser_create_sphere.add_argument('--overwrite', action='store_true', help='Overwrite output without asking (default: %(default)s)')
    parser_create_sphere.set_defaults(func=create_sphere)
        
    # parser for create_cylinder
    parser_create_cylinder = subparsers.add_parser('create_cylinder')
    parser_create_cylinder.add_argument('output_file', action=CheckExt({'stl','STL'}), help='Output STL image file name')
    parser_create_cylinder.add_argument('--transform_file', default="None", action=CheckExt({'txt','TXT'}), metavar='FILE', help='Apply a 4x4 transform from a file (default: %(default)s)')
    parser_create_cylinder.add_argument('--radius', type=float, default=0.5, help='Cylinder radius (default: %(default)s)')
    parser_create_cylinder.add_argument('--height', type=float, default=1.0, help='Cylinder height (default: %(default)s)')
    parser_create_cylinder.add_argument('--resolution', type=int, default=6, metavar='RES', help='Cylinder resolution (default: %(default)s)')
    parser_create_cylinder.add_argument('--capping', action='store_false', help='Cylinder capping (default: %(default)s)')
    parser_create_cylinder.add_argument('--center', type=float, nargs=3, default=[0,0,0], metavar='0', help='Cylinder center (default: %(default)s)')
    parser_create_cylinder.add_argument('--rotate', type=float, nargs=3, default=[0,0,0], metavar='0', help='Rotation angle about X, Y and Z axes (default: %(default)s)')
    parser_create_cylinder.add_argument('--visualize', action='store_true', help='Visualize the model (default: %(default)s)')
    parser_create_cylinder.add_argument('--overwrite', action='store_true', help='Overwrite output without asking (default: %(default)s)')
    parser_create_cylinder.set_defaults(func=create_cylinder)
    
    # parser for create_cube
    parser_create_cube = subparsers.add_parser('create_cube')
    parser_create_cube.add_argument('output_file', action=CheckExt({'stl','STL'}), help='Output STL image file name')
    parser_create_cube.add_argument('--transform_file', default="None", action=CheckExt({'txt','TXT'}), metavar='FILE', help='Apply a 4x4 transform from a file (default: %(default)s)')
    parser_create_cube.add_argument('--bounds', type=float, nargs=6, default=[0,1,0,1,0,1], metavar='0', help='Cube bounds in units mm (default: %(default)s)')
    parser_create_cube.add_argument('--rotate', type=float, nargs=3, default=[0,0,0], metavar='0', help='Rotation angle about X, Y and Z axes (default: %(default)s)')
    parser_create_cube.add_argument('--visualize', action='store_true', help='Visualize the model (default: %(default)s)')
    parser_create_cube.add_argument('--overwrite', action='store_true', help='Overwrite output without asking (default: %(default)s)')
    parser_create_cube.set_defaults(func=create_cube)
    
    # Parse and display
    args = parser.parse_args()
    print(echo_arguments('RapidPrototype', vars(args)))
        
    # Run program
    args.func(**vars(args))
Esempio n. 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)