Esempio n. 1
0
def main():
    parser = getParser()
    args = getArguments(parser)

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)

    # loading input images
    img, hdr = load(args.input)
    img = img.astype(numpy.bool)

    # check spacing values
    if not len(args.spacing) == img.ndim:
        parser.error(
            'The image has {} dimensions, but {} spacing parameters have been supplied.'
            .format(img.ndim, len(args.spacing)))

    # check if output image exists
    if not args.force:
        if os.path.exists(args.output):
            parser.error('The output image {} already exists.'.format(
                args.output))

    logger.debug('target voxel spacing: {}'.format(args.spacing))

    # determine number of required complete slices for up-sampling
    vs = header.get_pixel_spacing(hdr)
    rcss = [
        int(y // x - 1) for x, y in zip(args.spacing, vs)
    ]  # TODO: For option b, remove the - 1; better: no option b, since I am rounding later anyway

    # remove negatives and round up to next even number
    rcss = [x if x > 0 else 0 for x in rcss]
    rcss = [x if 0 == x % 2 else x + 1 for x in rcss]
    logger.debug('intermediate slices to add per dimension: {}'.format(rcss))

    # for each dimension requiring up-sampling, from the highest down, perform shape based slice interpolation
    logger.info('Adding required slices using shape based interpolation.')
    for dim, rcs in enumerate(rcss):
        if rcs > 0:
            logger.debug(
                'adding {} intermediate slices to dimension {}'.format(
                    rcs, dim))
            img = shape_based_slice_interpolation(img, dim, rcs)
            logger.debug('resulting new image shape: {}'.format(img.shape))

    # compute and set new voxel spacing
    nvs = [x / (y + 1.) for x, y in zip(vs, rcss)]
    header.set_pixel_spacing(hdr, nvs)
    logger.debug('intermediate voxel spacing: {}'.format(nvs))

    # interpolate with nearest neighbour
    logger.info('Re-sampling the image with a b-spline order of {}.'.format(
        args.order))
    img, hdr = resample(img, hdr, args.spacing, args.order, mode='nearest')

    # saving the resulting image
    save(img, args.output, hdr, args.force)
Esempio n. 2
0
def main():
    args = getArguments(getParser())

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)
    
    # load input image
    data_input, header_input = load(args.input)
    
    # transform to uin8
    data_input = data_input.astype(scipy.uint8)
                                      
    # reduce to 3D, if larger dimensionality
    if data_input.ndim > 3:
        for _ in range(data_input.ndim - 3): data_input = data_input[...,0]
        
    # iter over slices (2D) until first with content is detected
    for plane in data_input:
        if scipy.any(plane):
            # set pixel spacing
            spacing = list(header.get_pixel_spacing(header_input))
            spacing = spacing[1:3]
            __update_header_from_array_nibabel(header_input, plane)
            header.set_pixel_spacing(header_input, spacing)
            # save image
            save(plane, args.output, header_input, args.force)
            break
    
    logger.info("Successfully terminated.")    
Esempio n. 3
0
def zoom(image, factor, dimension, hdr = False, order = 3):
    """
    Zooms the provided image by the supplied factor in the supplied dimension.
    The factor is an integer determining how many slices should be put between each
    existing pair.
    If an image header (hdr) is supplied, its voxel spacing gets updated.
    Returns the image and the updated header or false.
    """
    # check if supplied dimension is valid
    if dimension >= image.ndim:
        raise argparse.ArgumentError('The supplied zoom-dimension {} exceeds the image dimensionality of 0 to {}.'.format(dimension, image.ndim - 1))
    
    # get logger
    logger = Logger.getInstance()

    logger.debug('Old shape = {}.'.format(image.shape))

    # perform the zoom
    zoom = [1] * image.ndim
    zoom[dimension] = (image.shape[dimension] + (image.shape[dimension] - 1) * factor) / float(image.shape[dimension])
    logger.debug('Reshaping with = {}.'.format(zoom))
    image = interpolation.zoom(image, zoom, order=order)
        
    logger.debug('New shape = {}.'.format(image.shape))
    
    if hdr:
        new_spacing = list(header.get_pixel_spacing(hdr))
        new_spacing[dimension] = new_spacing[dimension] / float(factor + 1)
        logger.debug('Setting pixel spacing from {} to {}....'.format(header.get_pixel_spacing(hdr), new_spacing))
        header.set_pixel_spacing(hdr, tuple(new_spacing))
    
    return image, hdr
def main():
    args = getArguments(getParser())

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)
    
    # load input image
    data_input, header_input = load(args.input)
    
    logger.debug('Original shape = {}.'.format(data_input.shape))
    
    # check if supplied dimension parameters is inside the images dimensions
    if args.dimension1 >= data_input.ndim or args.dimension1 < 0:
        raise ArgumentError('The first swap-dimension {} exceeds the number of input volume dimensions {}.'.format(args.dimension1, data_input.ndim))
    elif args.dimension2 >= data_input.ndim or args.dimension2 < 0:
        raise ArgumentError('The second swap-dimension {} exceeds the number of input volume dimensions {}.'.format(args.dimension2, data_input.ndim))
    
    # swap axes
    data_output = scipy.swapaxes(data_input, args.dimension1, args.dimension2)
    # swap pixel spacing and offset
    ps = list(header.get_pixel_spacing(header_input))
    ps[args.dimension1], ps[args.dimension2] = ps[args.dimension2], ps[args.dimension1]
    header.set_pixel_spacing(header_input, ps)
    os = list(header.get_offset(header_input))
    os[args.dimension1], os[args.dimension2] = os[args.dimension2], os[args.dimension1]
    header.set_offset(header_input, os)
    
    logger.debug('Resulting shape = {}.'.format(data_output.shape))
    
    # save resulting volume
    save(data_output, args.output, header_input, args.force)
    
    logger.info("Successfully terminated.")    
def main():
    args = getArguments(getParser())

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)
    
    # load input image
    data_input, header_input = load(args.input)
    
    # transform to uin8
    data_input = data_input.astype(scipy.uint8)
                                      
    # reduce to 3D, if larger dimensionality
    if data_input.ndim > 3:
        for _ in range(data_input.ndim - 3): data_input = data_input[...,0]
        
    # iter over slices (2D) until first with content is detected
    for plane in data_input:
        if scipy.any(plane):
            # set pixel spacing
            spacing = list(header.get_pixel_spacing(header_input))
            spacing = spacing[1:3]
            __update_header_from_array_nibabel(header_input, plane)
            header.set_pixel_spacing(header_input, spacing)
            # save image
            save(plane, args.output, header_input, args.force)
            break
    
    logger.info("Successfully terminated.")    
Esempio n. 6
0
def resample(img, hdr, target_spacing, bspline_order=3, mode='constant'):
        """
        Re-sample an image to a new voxel-spacing. Taken form medpy.io.


        
        Parameters
        ----------
        img : array_like
            The image.
        hdr : object
            The image header.
        target_spacing : number or sequence of numbers
            The target voxel spacing to achieve. If a single number, isotropic spacing is assumed.
        bspline_order : int
            The bspline order used for interpolation.
        mode : str
            Points outside the boundaries of the input are filled according to the given mode ('constant', 'nearest', 'reflect' or 'wrap'). Default is 'constant'.
            
        Warning
        -------
        Voxel-spacing of input header will be modified in-place! If the target spacing can't be set exactly, for example in low pixel images, then the closest spacing will be chosen
            
        Returns
        -------
        img : ndarray
            The re-sampled image.
        hdr : object
            The image header with the new voxel spacing.
        """
        if isinstance(target_spacing, numbers.Number):
            target_spacing = [target_spacing] * img.ndim
        
        # compute zoom values
        zoom_factors = [old / float(new) for new, old in zip(target_spacing, header.get_pixel_spacing(hdr))]

        print "Zoom Factors"
        print zoom_factors

        oldImageShape = img.shape
    
        # zoom image
        img = zoom(img, zoom_factors, order=bspline_order, mode=mode)

        newImageShape = img.shape
        old_pixel_spacing = header.get_pixel_spacing(hdr)
        new_pixel_spacing = np.round(np.divide(np.multiply(oldImageShape,old_pixel_spacing),newImageShape),7)
        print "Target Pixel Spacing"
        print target_spacing

        print "Actual Pixel Spacing"
        print new_pixel_spacing
        # set new voxel spacing
        header.set_pixel_spacing(hdr, new_pixel_spacing)
        
        return img, hdr
Esempio n. 7
0
def main():
    args = getArguments(getParser())

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)

    # load input data
    input_data, input_header = load(args.input)

    logger.debug('Old shape = {}.'.format(input_data.shape))

    # compute new shape
    new_shape = list(input_data.shape)
    new_shape[args.dimension] = 1 + (new_shape[args.dimension] -
                                     1) / (args.discard + 1)

    # prepare output image
    output_data = scipy.zeros(new_shape, dtype=input_data.dtype)

    # prepare slicers
    slicer_in = [slice(None)] * input_data.ndim
    slicer_out = [slice(None)] * input_data.ndim

    # prepare skip-counter and output image slice counter
    skipc = 0
    slicec = 0

    logger.debug('Shrinking from {} to {}...'.format(input_data.shape,
                                                     new_shape))
    for idx in range(input_data.shape[args.dimension]):

        if 0 == skipc:
            # transfer slice
            slicer_in[args.dimension] = slice(idx, idx + 1)
            slicer_out[args.dimension] = slice(slicec, slicec + 1)
            output_data[slicer_out] = input_data[slicer_in]

            # resert resp. increase counter
            skipc = args.discard
            slicec += 1

        else:  # skip slice
            # decrease skip counter
            skipc -= 1

    # set new pixel spacing
    new_spacing = list(header.get_pixel_spacing(input_header))
    new_spacing[
        args.dimension] = new_spacing[args.dimension] * float(args.discard + 1)
    logger.debug('Setting pixel spacing from {} to {}....'.format(
        header.get_pixel_spacing(input_header), new_spacing))
    header.set_pixel_spacing(input_header, tuple(new_spacing))

    save(output_data, args.output, input_header, args.force)
def main():
    # parse cmd arguments
    parser = getParser()
    parser.parse_args()
    args = getArguments(parser)
    
    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)
    
    # load first input image as example 
    example_data, example_header = load(args.inputs[0])
    
    # test if the supplied position is valid
    if args.position > example_data.ndim or args.position < 0:
        raise ArgumentError('The supplied position for the new dimension is invalid. It has to be between 0 and {}.'.format(example_data.ndim))
    
    # prepare empty output volume
    output_data = scipy.zeros([len(args.inputs)] + list(example_data.shape), dtype=example_data.dtype)
    
    # add first image to output volume
    output_data[0] = example_data
    
    # load input images and add to output volume
    for idx, image in enumerate(args.inputs[1:]):
        image_data, _ = load(image)
        if not args.ignore and image_data.dtype != example_data.dtype:
            raise ArgumentError('The dtype {} of image {} differs from the one of the first image {}, which is {}.'.format(image_data.dtype, image, args.inputs[0], example_data.dtype))
        if image_data.shape != example_data.shape:
            raise ArgumentError('The shape {} of image {} differs from the one of the first image {}, which is {}.'.format(image_data.shape, image, args.inputs[0], example_data.shape))
        output_data[idx + 1] = image_data
        
    # move new dimension to the end or to target position
    for dim in range(output_data.ndim - 1):
        if dim >= args.position: break
        output_data = scipy.swapaxes(output_data, dim, dim + 1)
        
    # set pixel spacing
    spacing = list(header.get_pixel_spacing(example_header))
    spacing = tuple(spacing[:args.position] + [args.spacing] + spacing[args.position:])
    
    # !TODO: Find a way to enable this also for PyDicom and ITK images
    if __is_header_nibabel(example_header):
        __update_header_from_array_nibabel(example_header, output_data)
        header.set_pixel_spacing(example_header, spacing)
    else:
        raise ArgumentError("Sorry. Setting the voxel spacing of the new dimension only works with NIfTI images. See the description of this program for more details.")
    
    # save created volume
    save(output_data, args.output, example_header, args.force)
        
    logger.info("Successfully terminated.")
Esempio n. 9
0
def main():
    args = getArguments(getParser())

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)
    
    # load input data
    input_data, input_header = load(args.input)
    
    logger.debug('Old shape = {}.'.format(input_data.shape))
    
    # compute new shape
    new_shape = list(input_data.shape)
    new_shape[args.dimension] = 1 + (new_shape[args.dimension] - 1) / (args.discard + 1)
    
    # prepare output image
    output_data = scipy.zeros(new_shape, dtype=input_data.dtype)
    
    # prepare slicers
    slicer_in = [slice(None)] * input_data.ndim
    slicer_out = [slice(None)] * input_data.ndim
    
    # prepare skip-counter and output image slice counter
    skipc = 0
    slicec = 0
    
    logger.debug('Shrinking from {} to {}...'.format(input_data.shape, new_shape))
    for idx in range(input_data.shape[args.dimension]):
        
        if 0 == skipc:
            # transfer slice
            slicer_in[args.dimension] = slice(idx, idx + 1)
            slicer_out[args.dimension]  = slice(slicec, slicec + 1)
            output_data[slicer_out] = input_data[slicer_in]
            
            # resert resp. increase counter
            skipc = args.discard
            slicec += 1
            
        else: # skip slice
            # decrease skip counter
            skipc -= 1

    
    # set new pixel spacing
    new_spacing = list(header.get_pixel_spacing(input_header))
    new_spacing[args.dimension] = new_spacing[args.dimension] * float(args.discard + 1)
    logger.debug('Setting pixel spacing from {} to {}....'.format(header.get_pixel_spacing(input_header), new_spacing))
    header.set_pixel_spacing(input_header, tuple(new_spacing))
    
    save(output_data, args.output, input_header, args.force)
def main():
    args = getArguments(getParser())

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)

    # copy the example image or generate empty image, depending on the modus
    if args.example:
        grid_image = scipy.zeros(args.example_image.shape, scipy.bool_)
        grid_header = args.example_header
    else:
        grid_image = scipy.zeros(args.shape, scipy.bool_)
        # !TODO: Find another solution for this
        # Saving and loading image once to generate a valid header
        tmp_dir = tempfile.mkdtemp()
        tmp_image = '{}/{}'.format(tmp_dir, args.output.split('/')[-1])
        save(grid_image, tmp_image)
        _, grid_header = load(tmp_image)
        try:
            os.remove(tmp_image)
            os.rmdir(tmp_dir)
        except Exception:
            pass

    # set the image attributes if supplied
    if args.pixelspacing:
        header.set_pixel_spacing(grid_header, args.pixelspacing)
    if args.offset:
        header.set_offset(grid_header, args.offset)

    # compute the right grid spacing for each dimension
    if args.real:
        grid_spacing = [
            int(round(sp / float(ps))) for sp, ps in zip(
                args.spacing, header.get_pixel_spacing(grid_header))
        ]
    else:
        grid_spacing = args.spacing

    # paint the grid into the empty image volume
    for dim in range(grid_image.ndim):
        if 0 == grid_spacing[dim]:
            continue  # skip dimension of 0 grid spacing supplied
        for offset in range(0, grid_image.shape[dim], grid_spacing[dim]):
            slicer = [slice(None)] * grid_image.ndim
            slicer[dim] = slice(offset, offset + 1)
            grid_image[slicer] = True

    # saving resulting grid volume
    save(grid_image, args.output, grid_header, args.force)
Esempio n. 11
0
def main():
    parser = getParser()
    args = getArguments(parser)

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)
    
    # loading input images
    img, hdr = load(args.input)
    img = img.astype(numpy.bool)
    
    # check spacing values
    if not len(args.spacing) == img.ndim:
        parser.error('The image has {} dimensions, but {} spacing parameters have been supplied.'.format(img.ndim, len(args.spacing)))
        
    # check if output image exists
    if not args.force:
        if os.path.exists(args.output):
            parser.error('The output image {} already exists.'.format(args.output)) 
        
    logger.debug('target voxel spacing: {}'.format(args.spacing))
    
    # determine number of required complete slices for up-sampling
    vs = header.get_pixel_spacing(hdr)
    rcss = [int(y // x - 1) for x, y in zip(args.spacing, vs)] # TODO: For option b, remove the - 1; better: no option b, since I am rounding later anyway
    
    # remove negatives and round up to next even number
    rcss = [x if x > 0 else 0 for x in rcss]
    rcss = [x if 0 == x % 2 else x + 1 for x in rcss]
    logger.debug('intermediate slices to add per dimension: {}'.format(rcss))
    
    # for each dimension requiring up-sampling, from the highest down, perform shape based slice interpolation
    logger.info('Adding required slices using shape based interpolation.')
    for dim, rcs in enumerate(rcss):
        if rcs > 0:
            logger.debug('adding {} intermediate slices to dimension {}'.format(rcs, dim))
            img = shape_based_slice_interpolation(img, dim, rcs)
            logger.debug('resulting new image shape: {}'.format(img.shape))
            
    # compute and set new voxel spacing
    nvs = [x / (y + 1.) for x, y in zip(vs, rcss)]
    header.set_pixel_spacing(hdr, nvs)
    logger.debug('intermediate voxel spacing: {}'.format(nvs))
    
    # interpolate with nearest neighbour
    logger.info('Re-sampling the image with a b-spline order of {}.'.format(args.order))
    img, hdr = resample(img, hdr, args.spacing, args.order, mode='nearest')
    
    # saving the resulting image
    save(img, args.output, hdr, args.force)
Esempio n. 12
0
def main():
    args = getArguments(getParser())

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)
    
    # copy the example image or generate empty image, depending on the modus
    if args.example:
        grid_image = scipy.zeros(args.example_image.shape, scipy.bool_)
        grid_header = args.example_header
    else:
        grid_image = scipy.zeros(args.shape, scipy.bool_)
        # !TODO: Find another solution for this
        # Saving and loading image once to generate a valid header
        tmp_dir = tempfile.mkdtemp()
        tmp_image = '{}/{}'.format(tmp_dir, args.output.split('/')[-1])
        save(grid_image, tmp_image)
        _, grid_header = load(tmp_image)
        try:
            os.remove(tmp_image)
            os.rmdir(tmp_dir)
        except Exception:
            pass
        
    # set the image attributes if supplied
    if args.pixelspacing:
        header.set_pixel_spacing(grid_header, args.pixelspacing)
    if args.offset:
        header.set_offset(grid_header, args.offset)
    
    # compute the right grid spacing for each dimension
    if args.real:
        grid_spacing = [int(round(sp / float(ps))) for sp, ps in zip(args.spacing, header.get_pixel_spacing(grid_header))]
    else:
        grid_spacing = args.spacing
        
    # paint the grid into the empty image volume
    for dim in range(grid_image.ndim):
        if 0 == grid_spacing[dim]: continue # skip dimension of 0 grid spacing supplied
        for offset in range(0, grid_image.shape[dim], grid_spacing[dim]):
            slicer = [slice(None)] * grid_image.ndim
            slicer[dim] = slice(offset, offset + 1)
            grid_image[slicer] = True
            
    # saving resulting grid volume
    save(grid_image, args.output, grid_header, args.force)
def main():
    args = getArguments(getParser())

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)
    
    # load input
    data_input, header_input = load(args.image)
    
    # change pixel spacing
    logger.info('Setting pixel spacing along {} to {}...'.format(data_input.shape, args.spacing))
    header.set_pixel_spacing(header_input, args.spacing)
    
    # save file
    save(data_input.copy(), args.image, header_input, True)
    
    logger.info("Successfully terminated.")    
def main():
    args = getArguments(getParser())

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)
    
    # load input
    data_input, header_input = load(args.input)
    
    # change pixel spacing
    logger.info('Setting pixel spacing along {} to {}...'.format(data_input.shape, args.spacing))
    header.set_pixel_spacing(header_input, args.spacing)
    
    # save file
    save(data_input, args.output, header_input, args.force)
    
    logger.info("Successfully terminated.")    
Esempio n. 15
0
def main():
    parser = getParser()
    args = getArguments(parser)

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)

    # loading input images
    img, hdr = load(args.input)

    # check spacing values
    if not len(args.spacing) == img.ndim:
        parser.error(
            'The image has {} dimensions, but {} spacing parameters have been supplied.'
            .format(img.ndim, len(args.spacing)))

    # check if output image exists
    if not args.force:
        if os.path.exists(args.output):
            parser.error('The output image {} already exists.'.format(
                args.output))

    logger.debug('target voxel spacing: {}'.format(args.spacing))

    # compute zoom values
    zoom_factors = [
        old / float(new)
        for new, old in zip(args.spacing, header.get_pixel_spacing(hdr))
    ]
    logger.debug('zoom-factors: {}'.format(zoom_factors))

    # zoom image
    img = scipy.ndimage.interpolation.zoom(img, zoom_factors, order=args.order)
    logger.debug('new image shape: {}'.format(img.shape))

    # set new voxel spacing
    header.set_pixel_spacing(hdr, args.spacing)

    # saving the resulting image
    save(img, args.output, hdr, args.force)
def main():
    args = getArguments(getParser())

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)

    # load input image
    data_input, header_input = load(args.input)

    logger.debug('Original shape = {}.'.format(data_input.shape))

    # check if supplied dimension parameters is inside the images dimensions
    if args.dimension1 >= data_input.ndim or args.dimension1 < 0:
        raise ArgumentError(
            'The first swap-dimension {} exceeds the number of input volume dimensions {}.'
            .format(args.dimension1, data_input.ndim))
    elif args.dimension2 >= data_input.ndim or args.dimension2 < 0:
        raise ArgumentError(
            'The second swap-dimension {} exceeds the number of input volume dimensions {}.'
            .format(args.dimension2, data_input.ndim))

    # swap axes
    data_output = scipy.swapaxes(data_input, args.dimension1, args.dimension2)
    # swap pixel spacing and offset
    ps = list(header.get_pixel_spacing(header_input))
    ps[args.dimension1], ps[args.dimension2] = ps[args.dimension2], ps[
        args.dimension1]
    header.set_pixel_spacing(header_input, ps)
    os = list(header.get_offset(header_input))
    os[args.dimension1], os[args.dimension2] = os[args.dimension2], os[
        args.dimension1]
    header.set_offset(header_input, os)

    logger.debug('Resulting shape = {}.'.format(data_output.shape))

    # save resulting volume
    save(data_output, args.output, header_input, args.force)

    logger.info("Successfully terminated.")
def main():
    # parse cmd arguments
    parser = getParser()
    parser.parse_args()
    args = getArguments(parser)

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)

    # load input image
    data_input, header_input = load(args.input)

    # check if the supplied dimension is valid
    if args.dimension >= data_input.ndim or args.dimension < 0:
        raise ArgumentError(
            'The supplied cut-dimension {} exceeds the image dimensionality of 0 to {}.'
            .format(args.dimension, data_input.ndim - 1))

    # prepare output file string
    name_output = args.output.replace('{}', '{:03d}')

    # compute the new the voxel spacing
    spacing = list(header.get_pixel_spacing(header_input))
    del spacing[args.dimension]

    # iterate over the cut dimension
    slices = data_input.ndim * [slice(None)]
    for idx in range(data_input.shape[args.dimension]):
        # cut the current slice from the original image
        slices[args.dimension] = slice(idx, idx + 1)
        data_output = scipy.squeeze(data_input[slices])
        # update the header and set the voxel spacing
        __update_header_from_array_nibabel(header_input, data_output)
        header.set_pixel_spacing(header_input, spacing)
        # save current slice
        save(data_output, name_output.format(idx), header_input, args.force)

    logger.info("Successfully terminated.")
Esempio n. 18
0
def main():
    args = getArguments(getParser())

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)
    
    # constants
    contour_dimension = 2
    
    # load input data
    input_data, input_header = load(args.input)
    
    # create output array
    new_shape = list(input_data.shape)
    new_shape[contour_dimension] = input_data.shape[contour_dimension] + (input_data.shape[contour_dimension] - 1) * args.enhancement
    output_data = scipy.zeros(new_shape, scipy.uint8)
    
    # prepare slicers
    slicer_from = [slice(None)] * input_data.ndim
    slicer_to = [slice(None)] * output_data.ndim
    
    logger.debug('Old shape = {}.'.format(input_data.shape))
    
    # copy data    
    for idx in range(input_data.shape[contour_dimension]):
        slicer_from[contour_dimension] = slice(idx, idx + 1)
        slicer_to[contour_dimension] = slice(idx * (args.enhancement + 1), idx * (args.enhancement + 1) + 1)
        
        output_data[slicer_to] = input_data[slicer_from]
        
    logger.debug('New shape = {}.'.format(output_data.shape))
    
    new_spacing = list(header.get_pixel_spacing(input_header))
    new_spacing[contour_dimension] = new_spacing[contour_dimension] / float(args.enhancement + 1)
    logger.debug('Setting pixel spacing from {} to {}....'.format(header.get_pixel_spacing(input_header), new_spacing))
    header.set_pixel_spacing(input_header, tuple(new_spacing))
    
    save(output_data, args.output, input_header, args.force)
def main():
    # parse cmd arguments
    parser = getParser()
    parser.parse_args()
    args = getArguments(parser)
    
    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)
    
    # load input image
    data_input, header_input = load(args.input)
    
    # check if the supplied dimension is valid
    if args.dimension >= data_input.ndim or args.dimension < 0:
        raise ArgumentError('The supplied cut-dimension {} exceeds the image dimensionality of 0 to {}.'.format(args.dimension, data_input.ndim - 1))
    
    # prepare output file string
    name_output = args.output.replace('{}', '{:03d}')
    
    # compute the new the voxel spacing
    spacing = list(header.get_pixel_spacing(header_input))
    del spacing[args.dimension]
    
    # iterate over the cut dimension
    slices = data_input.ndim * [slice(None)]
    for idx in range(data_input.shape[args.dimension]):
        # cut the current slice from the original image 
        slices[args.dimension] = slice(idx, idx + 1)
        data_output = scipy.squeeze(data_input[slices])
        # update the header and set the voxel spacing
        __update_header_from_array_nibabel(header_input, data_output)
        header.set_pixel_spacing(header_input, spacing)
        # save current slice
        save(data_output, name_output.format(idx), header_input, args.force)
        
    logger.info("Successfully terminated.")
Esempio n. 20
0
def zoom(image, factor, dimension, hdr=False, order=3):
    """
    Zooms the provided image by the supplied factor in the supplied dimension.
    The factor is an integer determining how many slices should be put between each
    existing pair.
    If an image header (hdr) is supplied, its voxel spacing gets updated.
    Returns the image and the updated header or false.
    """
    # check if supplied dimension is valid
    if dimension >= image.ndim:
        raise argparse.ArgumentError(
            'The supplied zoom-dimension {} exceeds the image dimensionality of 0 to {}.'
            .format(dimension, image.ndim - 1))

    # get logger
    logger = Logger.getInstance()

    logger.debug('Old shape = {}.'.format(image.shape))

    # perform the zoom
    zoom = [1] * image.ndim
    zoom[dimension] = (image.shape[dimension] +
                       (image.shape[dimension] - 1) * factor) / float(
                           image.shape[dimension])
    logger.debug('Reshaping with = {}.'.format(zoom))
    image = interpolation.zoom(image, zoom, order=order)

    logger.debug('New shape = {}.'.format(image.shape))

    if hdr:
        new_spacing = list(header.get_pixel_spacing(hdr))
        new_spacing[dimension] = new_spacing[dimension] / float(factor + 1)
        logger.debug('Setting pixel spacing from {} to {}....'.format(
            header.get_pixel_spacing(hdr), new_spacing))
        header.set_pixel_spacing(hdr, tuple(new_spacing))

    return image, hdr
Esempio n. 21
0
def main():
    parser = getParser()
    args = getArguments(parser)

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)
    
    # loading input images
    img, hdr = load(args.input)

    # check spacing values
    if not len(args.spacing) == img.ndim:
        parser.error('The image has {} dimensions, but {} spacing parameters have been supplied.'.format(img.ndim, len(args.spacing)))
        
    # check if output image exists
    if not args.force:
        if os.path.exists(args.output):
            parser.error('The output image {} already exists.'.format(args.output)) 
        
    logger.debug('target voxel spacing: {}'.format(args.spacing))

    # compute zoom values
    zoom_factors = [old / float(new) for new, old in zip(args.spacing, header.get_pixel_spacing(hdr))]
    logger.debug('zoom-factors: {}'.format(zoom_factors))

    # zoom image
    img = scipy.ndimage.interpolation.zoom(img, zoom_factors, order=args.order)
    logger.debug('new image shape: {}'.format(img.shape))

    # set new voxel spacing
    header.set_pixel_spacing(hdr, args.spacing)

    # saving the resulting image
    save(img, args.output, hdr, args.force)
Esempio n. 22
0
    def test_MetadataConsistency(self):
        """
        This test checks the ability of different image formats to consistently save
        meta-data information. Especially if a conversion between formats is required,
        that involves different 3rd party modules, this is not always guaranteed.
        
        The images are saved in one format, loaded and then saved in another format.
        Subsequently the differences in the meta-data is checked.
        
        Currently this test can only check:
        - voxel spacing
        - image offset
        
        Note that some other test are inherently performed by the
        loadsave.TestIOFacilities class:
        - data type
        - shape
        - content
        
        With the verboose switches, a comprehensive list of the results can be obtianed.
        """
        ####
        # VERBOOSE SETTINGS
        # The following are two variables that can be used to print some nicely
        # formatted additional output. When one of them is set to True, this unittest
        # should be run stand-alone.
        ####
        # Print a list of format to format conversion which preserve meta-data
        consistent = True
        # Print a list of format to format conversion which do not preserve meta-data
        inconsistent = True
        # Print a list of formats that failed conversion in general
        unsupported = False
        
        ####
        # OTHER SETTINGS
        ####
        # debug settings
        logger = Logger.getInstance()
        #logger.setLevel(logging.DEBUG)
        
        # run test either for most important formats or for all (see loadsave.TestIOFacilities)
        #__suffixes = self.__important # (choice 1)
        __suffixes = self.__pydicom + self.__nifti + self.__itk + self.__itk_more # (choice 2)
        
        # dimensions and dtypes to check
        __suffixes = list(set(__suffixes))
        __ndims = [1, 2, 3, 4, 5]
        __dtypes = [scipy.bool_,
                    scipy.int8, scipy.int16, scipy.int32, scipy.int64,
                    scipy.uint8, scipy.uint16, scipy.uint32, scipy.uint64,
                    scipy.float32, scipy.float64, #scipy.float128, # last one removed, as not present on every machine
                    scipy.complex64, scipy.complex128, ] #scipy.complex256 ## removed, as not present on every machine
        
        # prepare struct to save settings that passed the test
        consistent_types = dict.fromkeys(__suffixes)
        for k0 in consistent_types:
            consistent_types[k0] = dict.fromkeys(__suffixes)
            for k1 in consistent_types[k0]:
                consistent_types[k0][k1] = dict.fromkeys(__ndims)
                for k2 in consistent_types[k0][k1]:
                    consistent_types[k0][k1][k2] = []
        
        # prepare struct to save settings that did not
        inconsistent_types = dict.fromkeys(__suffixes)
        for k0 in inconsistent_types:
            inconsistent_types[k0] = dict.fromkeys(__suffixes)
            for k1 in inconsistent_types[k0]:
                inconsistent_types[k0][k1] = dict.fromkeys(__ndims)
                for k2 in inconsistent_types[k0][k1]:
                    inconsistent_types[k0][k1][k2] = dict.fromkeys(__dtypes)
        
        # prepare struct to save settings that did not pass the data integrity test
        unsupported_types = dict.fromkeys(__suffixes)
        for k0 in consistent_types:
            unsupported_types[k0] = dict.fromkeys(__suffixes)
            for k1 in unsupported_types[k0]:
                unsupported_types[k0][k1] = dict.fromkeys(__ndims)
                for k2 in unsupported_types[k0][k1]:
                    unsupported_types[k0][k1][k2] = dict.fromkeys(__dtypes)
        
        # create artifical images, save them, load them again and compare them
        path = tempfile.mkdtemp()
        try:
            for ndim in __ndims:
                logger.debug('Testing for dimension {}...'.format(ndim))
                arr_base = scipy.random.randint(0, 10, range(10, ndim + 10))
                for dtype in __dtypes:
                    arr_save = arr_base.astype(dtype)
                    for suffix_from in __suffixes:
                        # do not run test, if in avoid array
                        if ndim in self.__avoid and suffix_from in self.__avoid[ndim]:
                            unsupported_types[suffix_from][suffix_from][ndim][dtype] = "Test skipped, as combination in the tests __avoid array."
                            continue
                        
                        # save array as file, load again to obtain header and set the meta-data
                        image_from = '{}/img{}'.format(path, suffix_from)
                        try:
                            save(arr_save, image_from, None, True)
                            if not os.path.exists(image_from):
                                raise Exception('Image of type {} with shape={}/dtype={} has been saved without exception, but the file does not exist.'.format(suffix_from, arr_save.shape, dtype))
                        except Exception as e:
                            unsupported_types[suffix_from][suffix_from][ndim][dtype] = e.message
                            continue
                        
                        try:
                            img_from, hdr_from = load(image_from)
                            img_from = img_from.astype(dtype) # change dtype of loaded image again, as sometimes the type is higher (e.g. int64 instead of int32) after loading!
                        except Exception as e:
                            unsupported_types[suffix_from][suffix_from][ndim][dtype] = 'Saved reference image of type {} with shape={}/dtype={} could not be loaded. Reason: {}'.format(suffix_from, arr_save.shape, dtype, e.message)
                            continue

                        header.set_pixel_spacing(hdr_from, [scipy.random.rand() * scipy.random.randint(1, 10) for _ in range(img_from.ndim)])
                        try:
                            header.set_pixel_spacing(hdr_from, [scipy.random.rand() * scipy.random.randint(1, 10) for _ in range(img_from.ndim)])
                            header.set_offset(hdr_from, [scipy.random.rand() * scipy.random.randint(1, 10) for _ in range(img_from.ndim)])
                        except Exception as e:
                            logger.error('Could not set the header meta-data for image of type {} with shape={}/dtype={}. This should not happen and hints to a bug in the code. Signaled reason is: {}'.format(suffix_from, arr_save.shape, dtype, e))
                            unsupported_types[suffix_from][suffix_from][ndim][dtype] = e.message             
                            continue

                        for suffix_to in __suffixes:
                            # do not run test, if in avoid array
                            if ndim in self.__avoid and suffix_to in self.__avoid[ndim]:
                                unsupported_types[suffix_from][suffix_to][ndim][dtype] = "Test skipped, as combination in the tests __avoid array."
                                continue
                            
                            # for each other format, try format to format conversion an check if the meta-data is consistent
                            image_to = '{}/img_to{}'.format(path, suffix_to)
                            try:
                                save(img_from, image_to, hdr_from, True)
                                if not os.path.exists(image_to):
                                    raise Exception('Image of type {} with shape={}/dtype={} has been saved without exception, but the file does not exist.'.format(suffix_to, arr_save.shape, dtype))
                            except Exception as e:
                                unsupported_types[suffix_from][suffix_from][ndim][dtype] = e.message
                                continue
                            
                            try:
                                _, hdr_to = load(image_to)
                            except Exception as e:
                                unsupported_types[suffix_from][suffix_to][ndim][dtype] = 'Saved testing image of type {} with shape={}/dtype={} could not be loaded. Reason: {}'.format(suffix_to, arr_save.shape, dtype, e.message)
                                continue
                            
                            msg = self.__diff(hdr_from, hdr_to)
                            if msg:
                                inconsistent_types[suffix_from][suffix_to][ndim][dtype] = msg
                            else:
                                consistent_types[suffix_from][suffix_to][ndim].append(dtype)
                                
                            # remove testing image
                            if os.path.exists(image_to): os.remove(image_to)
                        
                        # remove reference image
                        if os.path.exists(image_to): os.remove(image_to)
                        
        except Exception:
            if not os.listdir(path): os.rmdir(path)
            else: logger.debug('Could not delete temporary directory {}. Is not empty.'.format(path))
            raise
        
        if consistent:
            print '\nthe following format conversions are meta-data consistent:'
            print 'from\tto\tndim\tdtypes'
            for suffix_from in consistent_types:
                for suffix_to in consistent_types[suffix_from]:
                    for ndim, dtypes in consistent_types[suffix_from][suffix_to].iteritems():
                        if list == type(dtypes) and not 0 == len(dtypes):
                            print '{}\t{}\t{}D\t{}'.format(suffix_from, suffix_to, ndim, map(lambda x: str(x).split('.')[-1][:-2], dtypes))
        if inconsistent:
            print '\nthe following form conversions are not meta-data consistent:'
            print 'from\tto\tndim\tdtype\t\terror'
            for suffix_from in inconsistent_types:
                for suffix_to in inconsistent_types[suffix_from]:
                    for ndim in inconsistent_types[suffix_from][suffix_to]:
                        for dtype, msg in inconsistent_types[suffix_from][suffix_to][ndim].iteritems():
                            if msg:
                                print '{}\t{}\t{}D\t{}\t\t{}'.format(suffix_from, suffix_to, ndim, str(dtype).split('.')[-1][:-2], msg)
            
        if unsupported:
            print '\nthe following form conversions could not be tested due to errors:'
            print 'from\tto\tndim\tdtype\t\terror'
            for suffix_from in unsupported_types:
                for suffix_to in unsupported_types[suffix_from]:
                    for ndim in unsupported_types[suffix_from][suffix_to]:
                        for dtype, msg in unsupported_types[suffix_from][suffix_to][ndim].iteritems():
                            if msg:
                                print '{}\t{}\t{}D\t{}\t\t{}'.format(suffix_from, suffix_to, ndim, str(dtype).split('.')[-1][:-2], msg)
def main():
    args = getArguments(getParser())

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)
    
    # constants
    dimension_spatial = 0
    dimension_temporal = 3
    name_output_data = '{}/{}_d{:01d}_s{:04d}.nii' # basename / slice-dimension / slice-number
    name_output_marker = '{}/m{}_d{:01d}_s{:04d}.nii' # basename / slice-dimension / slice-number
    
    # load input image
    input_data, input_header = load(args.input)
    
    # determine type of extraction and eventuelly load contour file
    if args.paintc or args.type in ['es']:
        contour_data, _ = load(args.contour)
    
    # select dimension and set variable parameter
    if args.type in ['ed', 'es', 'spatial']: cut_dimension = dimension_temporal
    else: cut_dimension = dimension_spatial
    basename = '.'.join(os.path.basename(args.input).split('.')[:-1])
    
    # adjust voxel spacing
    voxel_spacing = list(header.get_pixel_spacing(input_header))
    del voxel_spacing[cut_dimension]
    voxel_spacing += [0]
    header.set_pixel_spacing(input_header, voxel_spacing)
    
    # split and save images
    first = True
    slices = input_data.ndim * [slice(None)]
    for idx in range(input_data.shape[cut_dimension]):
        slices[cut_dimension] = slice(idx, idx + 1)
        
        # skip if output image already exists
        if not args.force and os.path.exists(name_output_data.format(args.output, basename, cut_dimension, idx)):
            logger.warning('The output image {} already exists, skipping this step.'.format(name_output_data.format(args.output, basename, cut_dimension, idx)))
            continue
        elif not args.force and os.path.exists(name_output_marker.format(args.output, basename, cut_dimension, idx)):
            logger.warning('The output marker image {} already exists, skipping this step.'.format(name_output_marker.format(args.output, basename, cut_dimension, idx)))
            continue
        
        # extract original sub-volume
        data_output = scipy.squeeze(input_data[slices])
        
        # create same-sized empty volume as marker base
        data_marker = scipy.zeros(data_output.shape, scipy.uint8)
        
        # extract also contour sub-volume if required
        if args.paintc:
            data_marker += scipy.squeeze(contour_data[slices])
            
        # check if contour data is empty in ed and es case; if yes, skip this volume
        if args.type in ['es'] and 0 == len(scipy.squeeze(contour_data[slices]).nonzero()[0]):
            continue
        
        # check if first volume in case of type = es, then skip
        if 'es' == args.type and first:
            first = False
            continue
        
        # save data and marker sub-volumes
        save(data_output, name_output_data.format(args.output, basename, cut_dimension, idx), input_header, args.force)
        save(data_marker, name_output_marker.format(args.output, basename, cut_dimension, idx), input_header, args.force)
        
        # in case of type = ed, break after first
        if 'ed' == args.type: break
        
    logger.info("Successfully terminated.")
def main():
    args = getArguments(getParser())

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)
    
    # constants
    dimension_spatial = 0
    dimension_temporal = 3
    name_output_data = '{}/{}_d{:01d}_s{:04d}.nii' # basename / slice-dimension / slice-number
    name_output_marker = '{}/m{}_d{:01d}_s{:04d}.nii' # basename / slice-dimension / slice-number
    
    # load input image
    input_data, input_header = load(args.input)
    
    # determine type of extraction and eventuelly load contour file
    if args.paintc or args.type in ['es']:
        contour_data, _ = load(args.contour)
    
    # select dimension and set variable parameter
    if args.type in ['ed', 'es', 'spatial']: cut_dimension = dimension_temporal
    else: cut_dimension = dimension_spatial
    basename = '.'.join(os.path.basename(args.input).split('.')[:-1])
    
    # adjust voxel spacing
    voxel_spacing = list(header.get_pixel_spacing(input_header))
    del voxel_spacing[cut_dimension]
    voxel_spacing += [0]
    header.set_pixel_spacing(input_header, voxel_spacing)
    
    # split and save images
    first = True
    slices = input_data.ndim * [slice(None)]
    for idx in range(input_data.shape[cut_dimension]):
        slices[cut_dimension] = slice(idx, idx + 1)
        
        # skip if output image already exists
        if not args.force and os.path.exists(name_output_data.format(args.output, basename, cut_dimension, idx)):
            logger.warning('The output image {} already exists, skipping this step.'.format(name_output_data.format(args.output, basename, cut_dimension, idx)))
            continue
        elif not args.force and os.path.exists(name_output_marker.format(args.output, basename, cut_dimension, idx)):
            logger.warning('The output marker image {} already exists, skipping this step.'.format(name_output_marker.format(args.output, basename, cut_dimension, idx)))
            continue
        
        # extract original sub-volume
        data_output = scipy.squeeze(input_data[slices])
        
        # create same-sized empty volume as marker base
        data_marker = scipy.zeros(data_output.shape, scipy.uint8)
        
        # extract also contour sub-volume if required
        if args.paintc:
            data_marker += scipy.squeeze(contour_data[slices])
            
        # check if contour data is empty in ed and es case; if yes, skip this volume
        if args.type in ['es'] and 0 == len(scipy.squeeze(contour_data[slices]).nonzero()[0]):
            continue
        
        # check if first volume in case of type = es, then skip
        if 'es' == args.type and first:
            first = False
            continue
        
        # save data and marker sub-volumes
        save(data_output, name_output_data.format(args.output, basename, cut_dimension, idx), input_header, args.force)
        save(data_marker, name_output_marker.format(args.output, basename, cut_dimension, idx), input_header, args.force)
        
        # in case of type = ed, break after first
        if 'ed' == args.type: break
        
    logger.info("Successfully terminated.")
    def test_MetadataConsistency(self):
        """
        This test checks the ability of different image formats to consistently save
        meta-data information. Especially if a conversion between formats is required,
        that involves different 3rd party modules, this is not always guaranteed.
        
        The images are saved in one format, loaded and then saved in another format.
        Subsequently the differences in the meta-data is checked.
        
        Currently this test can only check:
        - voxel spacing
        - image offset
        
        Note that some other test are inherently performed by the
        loadsave.TestIOFacilities class:
        - data type
        - shape
        - content
        
        With the verboose switches, a comprehensive list of the results can be obtianed.
        """
        ####
        # VERBOOSE SETTINGS
        # The following are two variables that can be used to print some nicely
        # formatted additional output. When one of them is set to True, this unittest
        # should be run stand-alone.
        ####
        # Print a list of format to format conversion which preserve meta-data
        consistent = True
        # Print a list of format to format conversion which do not preserve meta-data
        inconsistent = False
        # Print a list of formats that failed conversion in general
        unsupported = False

        ####
        # OTHER SETTINGS
        ####
        # debug settings
        logger = Logger.getInstance()
        #logger.setLevel(logging.DEBUG)

        # run test either for most important formats or for all (see loadsave.TestIOFacilities)
        #__suffixes = self.__important # (choice 1)
        __suffixes = self.__important + self.__itk  # (choice 2)

        # dimensions and dtypes to check
        __suffixes = list(set(__suffixes))
        __ndims = [1, 2, 3, 4, 5]
        __dtypes = [
            scipy.bool_,
            scipy.int8,
            scipy.int16,
            scipy.int32,
            scipy.int64,
            scipy.uint8,
            scipy.uint16,
            scipy.uint32,
            scipy.uint64,
            scipy.float32,
            scipy.
            float64,  #scipy.float128, # last one removed, as not present on every machine
            scipy.complex64,
            scipy.complex128,
        ]  #scipy.complex256 ## removed, as not present on every machine

        # prepare struct to save settings that passed the test
        consistent_types = dict.fromkeys(__suffixes)
        for k0 in consistent_types:
            consistent_types[k0] = dict.fromkeys(__suffixes)
            for k1 in consistent_types[k0]:
                consistent_types[k0][k1] = dict.fromkeys(__ndims)
                for k2 in consistent_types[k0][k1]:
                    consistent_types[k0][k1][k2] = []

        # prepare struct to save settings that did not
        inconsistent_types = dict.fromkeys(__suffixes)
        for k0 in inconsistent_types:
            inconsistent_types[k0] = dict.fromkeys(__suffixes)
            for k1 in inconsistent_types[k0]:
                inconsistent_types[k0][k1] = dict.fromkeys(__ndims)
                for k2 in inconsistent_types[k0][k1]:
                    inconsistent_types[k0][k1][k2] = dict.fromkeys(__dtypes)

        # prepare struct to save settings that did not pass the data integrity test
        unsupported_types = dict.fromkeys(__suffixes)
        for k0 in consistent_types:
            unsupported_types[k0] = dict.fromkeys(__suffixes)
            for k1 in unsupported_types[k0]:
                unsupported_types[k0][k1] = dict.fromkeys(__ndims)
                for k2 in unsupported_types[k0][k1]:
                    unsupported_types[k0][k1][k2] = dict.fromkeys(__dtypes)

        # create artifical images, save them, load them again and compare them
        path = tempfile.mkdtemp()
        try:
            for ndim in __ndims:
                logger.debug('Testing for dimension {}...'.format(ndim))
                arr_base = scipy.random.randint(0, 10,
                                                list(range(10, ndim + 10)))
                for dtype in __dtypes:
                    arr_save = arr_base.astype(dtype)
                    for suffix_from in __suffixes:
                        # do not run test, if in avoid array
                        if ndim in self.__avoid and suffix_from in self.__avoid[
                                ndim]:
                            unsupported_types[suffix_from][suffix_from][ndim][
                                dtype] = "Test skipped, as combination in the tests __avoid array."
                            continue

                        # save array as file, load again to obtain header and set the meta-data
                        image_from = '{}/img{}'.format(path, suffix_from)
                        try:
                            save(arr_save, image_from, None, True)
                            if not os.path.exists(image_from):
                                raise Exception(
                                    'Image of type {} with shape={}/dtype={} has been saved without exception, but the file does not exist.'
                                    .format(suffix_from, arr_save.shape,
                                            dtype))
                        except Exception as e:
                            unsupported_types[suffix_from][suffix_from][
                                ndim][dtype] = e.message if hasattr(
                                    e, 'message') else str(e.args)
                            continue

                        try:
                            img_from, hdr_from = load(image_from)
                            img_from = img_from.astype(
                                dtype
                            )  # change dtype of loaded image again, as sometimes the type is higher (e.g. int64 instead of int32) after loading!
                        except Exception as e:
                            _message = e.message if hasattr(
                                e, 'message') else str(e.args)
                            unsupported_types[suffix_from][suffix_from][ndim][
                                dtype] = 'Saved reference image of type {} with shape={}/dtype={} could not be loaded. Reason: {}'.format(
                                    suffix_from, arr_save.shape, dtype,
                                    _message)
                            continue

                        header.set_pixel_spacing(hdr_from, [
                            scipy.random.rand() * scipy.random.randint(1, 10)
                            for _ in range(img_from.ndim)
                        ])
                        try:
                            header.set_pixel_spacing(hdr_from, [
                                scipy.random.rand() *
                                scipy.random.randint(1, 10)
                                for _ in range(img_from.ndim)
                            ])
                            header.set_offset(hdr_from, [
                                scipy.random.rand() *
                                scipy.random.randint(1, 10)
                                for _ in range(img_from.ndim)
                            ])
                        except Exception as e:
                            logger.error(
                                'Could not set the header meta-data for image of type {} with shape={}/dtype={}. This should not happen and hints to a bug in the code. Signaled reason is: {}'
                                .format(suffix_from, arr_save.shape, dtype, e))
                            unsupported_types[suffix_from][suffix_from][
                                ndim][dtype] = e.message if hasattr(
                                    e, 'message') else str(e.args)
                            continue

                        for suffix_to in __suffixes:
                            # do not run test, if in avoid array
                            if ndim in self.__avoid and suffix_to in self.__avoid[
                                    ndim]:
                                unsupported_types[suffix_from][suffix_to][ndim][
                                    dtype] = "Test skipped, as combination in the tests __avoid array."
                                continue

                            # for each other format, try format to format conversion an check if the meta-data is consistent
                            image_to = '{}/img_to{}'.format(path, suffix_to)
                            try:
                                save(img_from, image_to, hdr_from, True)
                                if not os.path.exists(image_to):
                                    raise Exception(
                                        'Image of type {} with shape={}/dtype={} has been saved without exception, but the file does not exist.'
                                        .format(suffix_to, arr_save.shape,
                                                dtype))
                            except Exception as e:
                                unsupported_types[suffix_from][suffix_from][
                                    ndim][dtype] = e.message if hasattr(
                                        e, 'message') else str(e.args)
                                continue

                            try:
                                _, hdr_to = load(image_to)
                            except Exception as e:
                                _message = e.message if hasattr(
                                    e, 'message') else str(e.args)
                                unsupported_types[suffix_from][suffix_to][ndim][
                                    dtype] = 'Saved testing image of type {} with shape={}/dtype={} could not be loaded. Reason: {}'.format(
                                        suffix_to, arr_save.shape, dtype,
                                        _message)
                                continue

                            msg = self.__diff(hdr_from, hdr_to)
                            if msg:
                                inconsistent_types[suffix_from][suffix_to][
                                    ndim][dtype] = msg
                            else:
                                consistent_types[suffix_from][suffix_to][
                                    ndim].append(dtype)

                            # remove testing image
                            if os.path.exists(image_to): os.remove(image_to)

                        # remove reference image
                        if os.path.exists(image_to): os.remove(image_to)

        except Exception:
            if not os.listdir(path): os.rmdir(path)
            else:
                logger.debug(
                    'Could not delete temporary directory {}. Is not empty.'.
                    format(path))
            raise

        if consistent:
            print(
                '\nthe following format conversions are meta-data consistent:')
            print('from\tto\tndim\tdtypes')
            for suffix_from in consistent_types:
                for suffix_to in consistent_types[suffix_from]:
                    for ndim, dtypes in list(
                            consistent_types[suffix_from][suffix_to].items()):
                        if list == type(dtypes) and not 0 == len(dtypes):
                            print(('{}\t{}\t{}D\t{}'.format(
                                suffix_from, suffix_to, ndim,
                                [str(x).split('.')[-1][:-2] for x in dtypes])))
        if inconsistent:
            print(
                '\nthe following form conversions are not meta-data consistent:'
            )
            print('from\tto\tndim\tdtype\t\terror')
            for suffix_from in inconsistent_types:
                for suffix_to in inconsistent_types[suffix_from]:
                    for ndim in inconsistent_types[suffix_from][suffix_to]:
                        for dtype, msg in list(inconsistent_types[suffix_from]
                                               [suffix_to][ndim].items()):
                            if msg:
                                print(('{}\t{}\t{}D\t{}\t\t{}'.format(
                                    suffix_from, suffix_to, ndim,
                                    str(dtype).split('.')[-1][:-2], msg)))

        if unsupported:
            print(
                '\nthe following form conversions could not be tested due to errors:'
            )
            print('from\tto\tndim\tdtype\t\terror')
            for suffix_from in unsupported_types:
                for suffix_to in unsupported_types[suffix_from]:
                    for ndim in unsupported_types[suffix_from][suffix_to]:
                        for dtype, msg in list(unsupported_types[suffix_from]
                                               [suffix_to][ndim].items()):
                            if msg:
                                print(('{}\t{}\t{}D\t{}\t\t{}'.format(
                                    suffix_from, suffix_to, ndim,
                                    str(dtype).split('.')[-1][:-2], msg)))
def main():
    # parse cmd arguments
    parser = getParser()
    parser.parse_args()
    args = getArguments(parser)

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)

    # load first input image as example
    example_data, example_header = load(args.inputs[0])

    # test if the supplied position is valid
    if args.position > example_data.ndim or args.position < 0:
        raise ArgumentError(
            'The supplied position for the new dimension is invalid. It has to be between 0 and {}.'
            .format(example_data.ndim))

    # prepare empty output volume
    output_data = scipy.zeros([len(args.inputs)] + list(example_data.shape),
                              dtype=example_data.dtype)

    # add first image to output volume
    output_data[0] = example_data

    # load input images and add to output volume
    for idx, image in enumerate(args.inputs[1:]):
        image_data, _ = load(image)
        if not args.ignore and image_data.dtype != example_data.dtype:
            raise ArgumentError(
                'The dtype {} of image {} differs from the one of the first image {}, which is {}.'
                .format(image_data.dtype, image, args.inputs[0],
                        example_data.dtype))
        if image_data.shape != example_data.shape:
            raise ArgumentError(
                'The shape {} of image {} differs from the one of the first image {}, which is {}.'
                .format(image_data.shape, image, args.inputs[0],
                        example_data.shape))
        output_data[idx + 1] = image_data

    # move new dimension to the end or to target position
    for dim in range(output_data.ndim - 1):
        if dim >= args.position: break
        output_data = scipy.swapaxes(output_data, dim, dim + 1)

    # set pixel spacing
    spacing = list(header.get_pixel_spacing(example_header))
    spacing = tuple(spacing[:args.position] + [args.spacing] +
                    spacing[args.position:])

    # !TODO: Find a way to enable this also for PyDicom and ITK images
    if __is_header_nibabel(example_header):
        __update_header_from_array_nibabel(example_header, output_data)
        header.set_pixel_spacing(example_header, spacing)
    else:
        raise ArgumentError(
            "Sorry. Setting the voxel spacing of the new dimension only works with NIfTI images. See the description of this program for more details."
        )

    # save created volume
    save(output_data, args.output, example_header, args.force)

    logger.info("Successfully terminated.")