def getArguments(parser):
    "Provides additional validation of the arguments collected by argparse."
    args = parser.parse_args()
    if args.paintc and None == args.contour:
        raise ArgumentError('If the "-p" switch is set, a contour file must be provide.')
    elif args.type in ['es'] and None == args.contour:
        raise ArgumentError('If the type is set to "es", a contour file must be provide.')
    return args
예제 #2
0
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:])
    example_header.set_voxel_spacing(spacing)

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

    logger.info("Successfully terminated.")
예제 #3
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 dicom slices
    [series] = pydicom_series.read_files(
        args.input, False,
        True)  # second to not show progress bar, third to retrieve data
    #print series.sampling # Note: The first value is the mean of all differences between ImagePositionPatient-values of the DICOM slices - of course total bullshit
    data_3d = series.get_pixel_array()

    # check parameters
    if args.dimension >= data_3d.ndim or args.dimension < 0:
        raise ArgumentError(
            'The image has only {} dimensions. The supplied target dimension {} exceeds this number.'
            .format(data_3d.ndim, args.dimension))
    if not 0 == data_3d.shape[args.dimension] % args.offset:
        raise ArgumentError(
            'The number of slices {} in the target dimension {} of the image shape {} is not dividable by the supplied number of consecutive slices {}.'
            .format(data_3d.shape[args.dimension], args.dimension,
                    data_3d.shape, args.offset))

    # prepare empty target volume
    volumes_3d = data_3d.shape[args.dimension] / args.offset
    shape_4d = list(data_3d.shape)
    shape_4d[args.dimension] = volumes_3d
    data_4d = scipy.zeros([args.offset] + shape_4d, dtype=data_3d.dtype)

    logger.debug(
        'Separating {} slices into {} 3D volumes of thickness {}.'.format(
            data_3d.shape[args.dimension], volumes_3d, args.offset))

    # iterate over 3D image and create sub volumes which are then added to the 4d volume
    for idx in range(args.offset):
        # collect the slices
        for sl in range(volumes_3d):
            idx_from = [slice(None), slice(None), slice(None)]
            idx_from[args.dimension] = slice(idx + sl * args.offset,
                                             idx + sl * args.offset + 1)
            idx_to = [slice(None), slice(None), slice(None)]
            idx_to[args.dimension] = slice(sl, sl + 1)
            #print 'Slice {} to {}.'.format(idx_from, idx_to)
            data_4d[idx][idx_to] = data_3d[idx_from]

    # flip dimensions such that the newly created is the last
    data_4d = scipy.swapaxes(data_4d, 0, 3)

    # save resulting 4D volume
    save(data_4d, args.output, False, args.force)

    logger.info("Successfully terminated.")
예제 #4
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 3d image
    data_3d, header_3d = load(args.input)

    # check if supplied dimension parameter is inside the images dimensions
    if args.dimension >= data_3d.ndim or args.dimension < 0:
        raise ArgumentError(
            'The supplied cut-dimension {} exceeds the number of input volume dimensions {}.'
            .format(args.dimension, data_3d.ndim))

    # check if the supplied offset parameter is a divider of the cut-dimensions slice number
    if not 0 == data_3d.shape[args.dimension] % args.offset:
        raise ArgumentError(
            'The offset is not a divider of the number of slices in cut dimension ({} / {}).'
            .format(data_3d.shape[args.dimension], args.offset))

    # prepare empty target volume
    volumes_3d = data_3d.shape[args.dimension] / args.offset
    shape_4d = list(data_3d.shape)
    shape_4d[args.dimension] = volumes_3d
    data_4d = scipy.zeros([args.offset] + shape_4d, dtype=data_3d.dtype)

    logger.debug(
        'Separating {} slices into {} 3D volumes of thickness {}.'.format(
            data_3d.shape[args.dimension], volumes_3d, args.offset))

    # iterate over 3D image and create sub volumes which are then added to the 4d volume
    for idx in range(args.offset):
        # collect the slices
        for sl in range(volumes_3d):
            idx_from = [slice(None), slice(None), slice(None)]
            idx_from[args.dimension] = slice(idx + sl * args.offset,
                                             idx + sl * args.offset + 1)
            idx_to = [slice(None), slice(None), slice(None)]
            idx_to[args.dimension] = slice(sl, sl + 1)
            #print 'Slice {} to {}.'.format(idx_from, idx_to)
            data_4d[idx][idx_to] = data_3d[idx_from]

    # flip dimensions such that the newly created is the last
    data_4d = scipy.swapaxes(data_4d, 0, args.dimension + 1)
    data_4d = scipy.rollaxis(data_4d, 0, 4)

    # save resulting 4D volume
    save(data_4d, args.output, header_3d, args.force)

    logger.info("Successfully terminated.")
예제 #5
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)
    
    data_3d, _ = load(args.input)
    
    # check parameters
    if args.dimension >= data_3d.ndim or args.dimension < 0:
        raise ArgumentError('The image has only {} dimensions. The supplied target dimension {} exceeds this number.'.format(
                    data_3d.ndim,
                    args.dimension))
    if not 0 == data_3d.shape[args.dimension] % args.offset:
        raise ArgumentError('The number of slices {} in the target dimension {} of the image shape {} is not dividable by the supplied number of consecutive slices {}.'.format(
                    data_3d.shape[args.dimension],
                    args.dimension,
                    data_3d.shape,
                    args.offset))
    
    # prepare empty target volume
    volumes_3d = data_3d.shape[args.dimension] / args.offset
    shape_4d = list(data_3d.shape)
    shape_4d[args.dimension] = volumes_3d
    data_4d = scipy.zeros([args.offset] + shape_4d, dtype=data_3d.dtype)
    
    logger.debug('Separating {} slices into {} 3D volumes of thickness {}.'.format(data_3d.shape[args.dimension], volumes_3d, args.offset))
        
    # iterate over 3D image and create sub volumes which are then added to the 4d volume
    for idx in range(args.offset):
        # collect the slices
        for sl in range(volumes_3d):
            idx_from = [slice(None), slice(None), slice(None)]
            idx_from[args.dimension] = slice(idx + sl * args.offset, idx + sl * args.offset + 1)
            idx_to = [slice(None), slice(None), slice(None)]
            idx_to[args.dimension] = slice(sl, sl+1)
            #print 'Slice {} to {}.'.format(idx_from, idx_to)
            data_4d[idx][idx_to] = data_3d[idx_from]
        
    # flip dimensions such that the newly created is the last
    data_4d = scipy.swapaxes(data_4d, 0, 3)
        
    # save resulting 4D volume
    save(data_4d, args.output, False, args.force)
    
    logger.info("Successfully terminated.")
예제 #6
0
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)

    # check if output image exists (will also be performed before saving, but as the gradient might be time intensity, a initial check can save frustration)
    if not args.force:
        if os.path.exists(args.output):
            raise ArgumentError('The output image {} already exists.'.format(
                args.output))

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

    logger.debug('Input array: dtype={}, shape={}'.format(
        data_input.dtype, data_input.shape))

    # execute the gradient map filter
    logger.info('Applying gradient map filter...')
    data_output = filter.gradient_magnitude(
        data_input, header.get_pixel_spacing(header_input))

    logger.debug('Resulting array: dtype={}, shape={}'.format(
        data_output.dtype, data_output.shape))

    # save image
    save(data_output, args.output, header_input, args.force)

    logger.info('Successfully terminated.')
예제 #7
0
def getArguments(parser):
    "Provides additional validation of the arguments collected by argparse."
    args = parser.parse_args()
    if not '{}' in args.output:
        raise ArgumentError(
            args.output,
            'The output argument string must contain the sequence "{}".')
    return args
예제 #8
0
def __create_markers(marker_data, marker_dim, di, do):
    """
    Takes an image with markers inside the RV wall and returns two images with the inner
    repectively outer BG and FG markers.
    """ 
    # constants
    contour_dimension = 2
    time_dimension = 3
    distance_inner = di # within the images 01-05 takes values down to 5, standard is 9 to leave some space
    distance_outer = do # within the images 01-05 takes values down to 6, standard is 10 to leave some space
    
    # prepare output volumes
    inner_data = scipy.zeros(marker_data.shape, scipy.uint8)
    outer_data = scipy.zeros(marker_data.shape, scipy.uint8)
    
    # prepare slicer
    slicer = [slice(None)] * marker_data.ndim
    
    # iterate over contour dimension and process each subvolume slice separately
    if marker_dim == contour_dimension: iter_dimension = time_dimension
    else: iter_dimension = contour_dimension
    for slice_id in range(marker_data.shape[iter_dimension]):
        slicer[contour_dimension] = slice(slice_id, slice_id+1)
        # extract subvolume
        marker_subvolume = scipy.squeeze(marker_data[slicer])
        # skip step if no marker data present
        if 0 == len(marker_subvolume.nonzero()[0]): continue
        
        # check if the marker forms a closed circle
        marker_data_filled = scipy.ndimage.binary_fill_holes(marker_subvolume)
        if (marker_subvolume == marker_data_filled).all():
            raise ArgumentError('The supplied marker does not form a closed structure!')
        
        # COMPUTE MARKERS FOR INNER WALL
        inner_data_subvolume = scipy.squeeze(inner_data[slicer])
        marker_data_dilated = scipy.ndimage.binary_dilation(marker_subvolume, iterations = distance_inner) # dilate by distance_inner
        marker_data_dilated_filled = scipy.ndimage.binary_fill_holes(marker_data_dilated) # fill hole
        # fill hole of original and take inverse as inner bg marker (but erode first once for security)
        inner_data_subvolume[scipy.ndimage.binary_erosion(~scipy.ndimage.binary_fill_holes(marker_subvolume))] = 2
        # if the dilation was to large to keep any markers, use less
        c = 0
        while 0 == len(scipy.logical_xor(marker_data_dilated, marker_data_dilated_filled).nonzero()[0]):
            c += 1
            marker_data_dilated = scipy.ndimage.binary_dilation(marker_subvolume, iterations = distance_inner - c)
            marker_data_dilated_filled = scipy.ndimage.binary_fill_holes(marker_data_dilated) # fill hole
        # take hole as inner fg marker
        inner_data_subvolume[scipy.logical_xor(marker_data_dilated, marker_data_dilated_filled)] = 1
        
        # COMPUTE MARKERS FOR OUTER WALL
        outer_data_subvolume = scipy.squeeze(outer_data[slicer])
        marker_data_dilated = scipy.ndimage.binary_dilation(marker_subvolume, iterations = distance_outer) # dilate by distance_outer
        marker_data_dilated_filled = scipy.ndimage.binary_fill_holes(marker_data_dilated) # fill hole
        # take inverse as outer bg marker
        outer_data_subvolume[~marker_data_dilated_filled] = 2
        # fill hole of original and take as outer fg marker (but erode first twice for security)
        outer_data_subvolume[scipy.ndimage.binary_erosion(scipy.ndimage.binary_fill_holes(marker_subvolume), iterations=2)] = 1
        
    return inner_data, outer_data
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.")
예제 #10
0
파일: adc.py 프로젝트: xzf125244170/medpy
def main():
    args = getArguments(getParser())

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

    # loading input images
    b0img, b0hdr = load(args.b0image)
    bximg, bxhdr = load(args.bximage)

    # check if image are compatible
    if not b0img.shape == bximg.shape:
        raise ArgumentError(
            'The input images shapes differ i.e. {} != {}.'.format(
                b0img.shape, bximg.shape))
    if not header.get_pixel_spacing(b0hdr) == header.get_pixel_spacing(bxhdr):
        raise ArgumentError(
            'The input images voxel spacing differs i.e. {} != {}.'.format(
                header.get_pixel_spacing(b0hdr),
                header.get_pixel_spacing(bxhdr)))

    # check if supplied threshold value as well as the b value is above 0
    if args.threshold is not None and not args.threshold >= 0:
        raise ArgumentError(
            'The supplied threshold value must be greater than 0, otherwise a division through 0 might occur.'
        )
    if not args.b > 0:
        raise ArgumentError('The supplied b-value must be greater than 0.')

    # compute threshold value if not supplied
    if args.threshold is None:
        b0thr = otsu(b0img, 32) / 2.  # divide by 2 to decrease impact
        bxthr = otsu(bximg, 32) / 2.
        if 0 >= b0thr:
            raise ArgumentError(
                'The supplied b0image seems to contain negative values.')
        if 0 >= bxthr:
            raise ArgumentError(
                'The supplied bximage seems to contain negative values.')
    else:
        b0thr = bxthr = args.threshold

    logger.debug('thresholds={}/{}, b-value={}'.format(b0thr, bxthr, args.b))

    # threshold b0 + bx DW image to obtain a mask
    # b0 mask avoid division through 0, bx mask avoids a zero in the ln(x) computation
    mask = (b0img > b0thr) & (bximg > bxthr)

    logger.debug(
        'excluding {} of {} voxels from the computation and setting them to zero'
        .format(scipy.count_nonzero(mask), scipy.prod(mask.shape)))

    # compute the ADC
    adc = scipy.zeros(b0img.shape, b0img.dtype)
    adc[mask] = -1. * args.b * scipy.log(bximg[mask] / b0img[mask])

    # saving the resulting image
    save(adc, args.output, b0hdr, args.force)
예제 #11
0
def extract_basal_slice(arr, head):
    """
    Takes a 3D array, iterates over the first dimension and returns the first 2D plane
    which contains any non-zero values as we as its voxel spacing.
    """
    for plane in arr:
        if scipy.any(plane):
            # get voxel spacing
            spacing = list(header.get_pixel_spacing(head))
            spacing = spacing[1:3]
            # return plane and spacing
            return (plane, spacing)

    raise ArgumentError('The supplied array does not contain any object.')
def main():
    args = getArguments(getParser())

    # prepare logger
    logger = Logger.getInstance()
    if args.debug: logger.setLevel(logging.DEBUG)
    elif args.verbose: logger.setLevel(logging.INFO)
    
    # loading input images (as image, header pairs)
    images = []
    headers = []
    for image_name in args.images:
        i, h = load(image_name)
        images.append(i)
        headers.append(h)
    
    # loading binary foreground masks if supplied, else create masks from threshold value
    if args.masks:
        masks = [load(mask_name)[0].astype(numpy.bool) for mask_name in args.masks]
    else:
        masks = [i > args.threshold for i in images]
    
    # if in application mode, load the supplied model and apply it to the images
    if args.lmodel:
        logger.info('Loading the model and transforming images...')
        with open(args.lmodel, 'r') as f:
            trained_model = pickle.load(f)
            if not isinstance(trained_model, IntensityRangeStandardization):
                raise ArgumentError('{} does not seem to be a valid pickled instance of an IntensityRangeStandardization object'.format(args.lmodel))
            transformed_images = [trained_model.transform(i[m], surpress_mapping_check = args.ignore) for i, m in zip(images, masks)]
            
    # in in training mode, train the model, apply it to the images and save it
    else:
        logger.info('Training the average intensity model...')
        irs = IntensityRangeStandardization()
        trained_model, transformed_images = irs.train_transform([i[m] for i, m in zip(images, masks)], surpress_mapping_check = args.ignore)
        logger.info('Saving the trained model as {}...'.format(args.smodel))
        with open(args.smodel, 'wb') as f:
                pickle.dump(trained_model, f)
                
    # save the transformed images
    if args.simages:
        logger.info('Saving intensity transformed images to {}...'.format(args.simages))
        for ti, i, m, h, image_name in zip(transformed_images, images, masks, headers, args.images):
            i[m] = ti
            save(i, '{}/{}'.format(args.simages, image_name.split('/')[-1]), h, args.force)
    
    logger.info('Terminated.')
예제 #13
0
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.")
예제 #14
0
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
    logger.info('Loading source image {}...'.format(args.input))
    try:
        input_data = scipy.squeeze(load(args.input).get_data()).astype(
            scipy.bool_)
    except ImageFileError as e:
        logger.critical(
            'The region image does not exist or its file type is unknown.')
        raise ArgumentError(
            'The region image does not exist or its file type is unknown.', e)

    # iterate over designated dimension and create for each such extracted slice a text file
    logger.info('Processing per-slice and writing to files...')
    idx = [slice(None)] * input_data.ndim
    for slice_idx in range(input_data.shape[args.dimension]):
        idx[args.dimension] = slice(slice_idx, slice_idx + 1)
        # 2009: IM-0001-0027-icontour-manual
        file_name = '{}/IM-0001-{:04d}-{}contour-auto.txt'.format(
            args.target, slice_idx + args.offset, args.ctype)
        # 2012: P01-0080-icontour-manual.txt
        file_name = '{}/P{}-{:04d}-{}contour-auto.txt'.format(
            args.target, args.id, slice_idx + args.offset, args.ctype)

        # check if output file already exists
        if not args.force:
            if os.path.exists(file_name):
                logger.warning(
                    'The output file {} already exists. Skipping.'.format(
                        file_name))
                continue

        # extract current slice
        image_slice = scipy.squeeze(input_data[idx])

        # remove all objects except the largest
        image_labeled, labels = scipy.ndimage.label(image_slice)
        if labels > 1:
            logger.info(
                'The slice {} contains more than one object. Removing the smaller ones.'
                .format(file_name))
            # determine biggest
            biggest = 0
            biggest_size = 0
            for i in range(1, labels + 1):
                if len((image_labeled == i).nonzero()[0]) > biggest_size:
                    biggest_size = len((image_labeled == i).nonzero()[0])
                    biggest = i
            # remove others
            for i in range(1, labels + 1):
                if i == biggest: continue
                image_labeled[image_labeled == i] = 0
            # save to slice
            image_slice = image_labeled.astype(scipy.bool_)

        # perform some additional morphological operations
        image_slice = scipy.ndimage.morphology.binary_fill_holes(image_slice)
        footprint = scipy.ndimage.morphology.generate_binary_structure(
            image_slice.ndim, 3)
        image_slice = scipy.ndimage.morphology.binary_closing(image_slice,
                                                              footprint,
                                                              iterations=7)
        #image_slice = scipy.ndimage.morphology.binary_opening(image_slice, footprint, iterations=3)

        # if type == o, perform a dilation to increase the size slightly
        #if 'o' == args.ctype:
        #    footprint = scipy.ndimage.morphology.generate_binary_structure(image_slice.ndim, 3)
        #    image_slice = scipy.ndimage.morphology.binary_dilation(image_slice, iterations=3)

        # erode contour in slice
        input_eroded = scipy.ndimage.morphology.binary_erosion(image_slice,
                                                               border_value=1)
        image_slice ^= input_eroded  # xor

        # extract contour positions and put into right order
        contour_tmp = image_slice.nonzero()
        contour = [[] for i in range(len(contour_tmp[0]))]
        for i in range(len(contour_tmp[0])):
            for j in range(len(contour_tmp)):
                contour[i].append(contour_tmp[j][i])  # x, y, z, ....

        if 0 == len(contour):
            logger.warning(
                'Empty contour for file {}. Skipping.'.format(file_name))
            continue

        # create final points following along the contour (incl. linear sub-voxel precision)
        divider = 2
        point = contour[0]
        point_pos = 0
        processed = [point_pos]
        contour_final = []
        while point:
            nearest_pos = __find_nearest(point, contour, processed)
            if False == nearest_pos: break
            contour_final.extend(
                __draw_line(point, contour[nearest_pos], divider))
            processed.append(nearest_pos)
            point = contour[nearest_pos]
        # make connection between last and first point
        contour_final.extend(__draw_line(point, contour[0], divider))

        # save contour to file
        logger.debug('Creating file {}...'.format(file_name))
        with open(file_name, 'w') as f:
            for line in contour_final:
                f.write('{}\n'.format(' '.join(map(str, line))))

    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
    temporal_dimension = 3

    # check parameters
    if args.bg_threshold > 0.5:
        logger.warning(
            'The supplied BG threshold is rather high. This might corrupt the results.'
        )
    if args.fg_threshold < 0.5:
        logger.warning(
            'The supplied FG threshold is rather low. This might corrupt the results.'
        )

    # check if output file already exists
    if not args.force and os.path.exists(args.output):
        raise ArgumentError(
            'The supplied output file {} already exists.'.format(args.output))

    # loading atlas image
    atlas_data, atlas_header = load(args.atlas)

    # prepare neutral slice object
    slicer = [slice(None) for _ in range(atlas_data.ndim)]

    logger.info('Extracting ED and ES phase volumes...')

    # extract ED 3D volume
    slicer[temporal_dimension] = slice(0, 1)  # first is ed phase
    ed_data_fg = scipy.squeeze(atlas_data[slicer])

    # extract ES 3D volume
    es_data_fg = None
    for slidx in range(1, atlas_data.shape[temporal_dimension]):
        slicer[temporal_dimension] = slice(slidx, slidx + 1)
        if scipy.any(atlas_data[slicer]):
            es_data_fg = scipy.squeeze(
                atlas_data[slicer])  # first after ed phase is es phase
            es_slidx = slidx  # conserve for later usage
            continue

    if None == es_data_fg:
        raise AttributeError(
            'Could not find any data in the ES phase of the atlas image.')

    logger.debug('Extracted ed volume {} and es volume {}.'.format(
        ed_data_fg.shape, es_data_fg.shape))

    # split into FG and BG volumes and convert to bool type
    logger.info(
        'Splitting into FG and BG binary volumes representing the markers...')
    ed_data_bg = ed_data_fg.copy()
    es_data_bg = es_data_fg.copy()
    ed_data_fg[ed_data_fg <= args.fg_threshold] = 0
    es_data_fg[es_data_fg <= args.fg_threshold] = 0
    ed_data_bg[ed_data_bg <= args.bg_threshold] = 0
    es_data_bg[es_data_bg <= args.bg_threshold] = 0
    ed_data_fg = ed_data_fg.astype(scipy.bool_)
    es_data_fg = es_data_fg.astype(scipy.bool_)
    ed_data_bg = ~ed_data_bg.astype(scipy.bool_)
    es_data_bg = ~es_data_bg.astype(scipy.bool_)

    # see if any is empty
    if not scipy.any(es_data_fg):
        raise AttributeError('No foreground marker data for es phase.')
    elif not scipy.any(es_data_bg):
        raise AttributeError('No background marker data for es phase.')
    elif not scipy.any(ed_data_fg):
        raise AttributeError('No foreground marker data for ed phase.')
    elif not scipy.any(ed_data_bg):
        raise AttributeError('No background marker data for ed phase.')

    marker_data = scipy.zeros(atlas_data.shape, scipy.uint8)

    # enhance the markers by interpolating between binary foreground objects of ED and ES phase
    logger.info('Enhancing the markers from ED-first to ES phase...')
    slicer[temporal_dimension] = slice(0, es_slidx + 1)  # first half
    marker_data[slicer] = interpolateBetweenBinaryObjects(
        ed_data_fg, es_data_fg, es_slidx - 1)
    logger.info('Enhancing the markers from ES to ED-second phase...')
    slicer[temporal_dimension] = slice(es_slidx + 1, None)  # second half
    marker_data[slicer] = interpolateBetweenBinaryObjects(
        es_data_fg, ed_data_fg,
        marker_data.shape[temporal_dimension] - es_slidx - 3)

    # setting the background markers
    logger.info('Setting bg markers...')
    slicer[temporal_dimension] = slice(0, 1)  # first ed phase
    marker_data[slicer][ed_data_bg] = 2
    slicer[temporal_dimension] = slice(-1, None)  # second ed phase
    marker_data[slicer][ed_data_bg] = 2
    slicer[temporal_dimension] = slice(es_slidx, es_slidx + 1)  # es phase
    marker_data[slicer][es_data_bg] = 2

    save(marker_data, args.output, atlas_header, args.force)

    logger.info("Successfully terminated.")
예제 #16
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 original example volume
    original_data, original_header = load(args.original)
    
    # prepare execution
    result_data = scipy.zeros(original_data.shape, scipy.uint8)
    del original_data
    
    # First step: Combine all marker images
    basename_old = False
    # iterate over marker images
    for marker_image in args.input:
        # extract information from filename and prepare slicr object
        basename, slice_dimension, slice_number = re.match(r'.*m(.*)_d([0-9])_s([0-9]{4}).*', marker_image).groups()
        slice_dimension = int(slice_dimension)
        slice_number = int(slice_number)
        # check basenames
        if basename_old and not basename_old == basename:
            logger.warning('The marker seem to come from different sources. Encountered basenames {} and {}. Continuing anyway.'.format(basename, basename_old))
        basename_old = basename
        # prepare slicer
        slicer = [slice(None)] * result_data.ndim
        slicer[slice_dimension] = slice_number
        # load marker image
        marker_data, _ = load(marker_image)
        # add to marker image ONLY where this is zero!
        result_data_subvolume = result_data[slicer]
        mask_array = result_data_subvolume == 0
        result_data_subvolume[mask_array] = marker_data[mask_array]
        if not 0 == len(marker_data[~mask_array].nonzero()[0]):
            logger.warning('The mask volume {} showed some intersection with previous mask volumes. Up to {} marker voxels might be lost.'.format(marker_image, len(marker_data[~mask_array].nonzero()[0])))
        
    # Second step: Normalize and determine type of markers
    result_data[result_data >= 10] = 0 # remove markers with indices higher than 10
    #result_data = relabel_non_zero(result_data) # relabel starting from 1, 0's are kept where encountered
    marker_count = len(scipy.unique(result_data))
    
    if 3 > marker_count: # less than two markers
        raise ArgumentError('A minimum of two markers must be contained in the conjunction of all markers files (excluding the neutral markers of index 0).')
    
    # assuming here that 1 == inner marker, 2 = border marker and 3 = background marker
    inner_name = args.output.format('i')
    inner_data = scipy.zeros_like(result_data)
    inner_data[result_data == 1] = 1
    inner_data[result_data == 2] = 2
    inner_data[result_data == 3] = 2
    save(inner_data, inner_name, original_header, args.force)
    
    outer_name = args.output.format('o')
    outer_data = scipy.zeros_like(result_data)
    outer_data[result_data == 1] = 1
    outer_data[result_data == 2] = 1
    outer_data[result_data == 3] = 2
    save(outer_data, outer_name, original_header, args.force)    
    
#    for marker in scipy.unique(result_data)[1:-1]: # first is neutral marker (0) and last overall background marker
#        output = args.output.format(marker)
#        _data = scipy.zeros_like(result_data)
#        _data += 2 # set all as BG markers
#        _data[result_data == marker] = 1
#        _data[result_data == 0] = 0
#        save(_data, output, original_header, args.force)
        
    logger.info("Successfully terminated.")
예제 #17
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
    spatial_dimension = 0

    # loading input image
    input_data, input_header = load(args.input)

    # check if it has the right dimensions, otherwise cut down to expected number
    if 3 < input_data.ndim:
        _tmp = input_data.shape
        slicer = [slice(0, 1) for _ in range(input_data.ndim)]
        for slid in range(3):
            slicer[slid] = slice(None)
        input_data = scipy.squeeze(input_data[slicer])
        logger.warning(
            'Found more than the expected number of 3 dimensions in the input image. Reduced from shape {} to {}.'
            .format(_tmp, input_data.shape))
    elif 3 > input_data.ndim:
        raise ArgumentError(
            'Invalid number of image dimensions {}, expects 3.'.format(
                input_data.ndim))

    # if input volume is of type manual markers, remove all above 1
    if 'manual' == args.type:
        logger.info('Extracting foreground markers...')
        input_data[input_data > 1] = 0

    # select the first basal slice
    logger.info('Selecting basal slice...')
    slicer = [slice(None) for _ in range(input_data.ndim)]
    basal_slice = None
    for slid in range(input_data.shape[spatial_dimension]):
        slicer[spatial_dimension] = slice(slid, slid + 1)
        if input_data[slicer].any():
            basal_slice = input_data[slicer].astype(scipy.bool_)
            break

    # raise error if no slice with values found
    if None == basal_slice:
        raise ArgumentError(
            'The supported input image contains no non-zero data.')

    logger.debug(
        'Found basal slice of shape {} at spatial position {} with {} voxels.'.
        format(basal_slice.shape, slid, len(basal_slice.nonzero()[0])))

    # filling eventual holes
    logger.debug('Filling eventual holes...')
    basal_slice[0] = binary_fill_holes(scipy.squeeze(
        basal_slice))  # !TODO: Use spatial_dimension instead of the 0 here

    # dilate the slice
    logger.info('Dilating...')
    basal_slice = scipy.ndimage.binary_dilation(basal_slice,
                                                iterations=args.dilations)

    logger.debug('{} voxels after dilation with {} iterations.'.format(
        len(basal_slice.nonzero()[0]), args.dilations))

    # build up the tube
    logger.info('Constructing tube...')
    output_data = scipy.concatenate(
        [basal_slice for _ in range(input_data.shape[spatial_dimension])],
        axis=spatial_dimension)

    logger.debug('Shape of constructed tube is {}.'.format(output_data.shape))

    # check if resulting shape is valid
    if input_data.shape != output_data.shape:
        raise Exception(
            'The shape of the final data differs with {} of the shape of the original image {}.'
            .format(output_data.shape, input_data.shape))

    # save resulting image
    save(output_data, args.output, input_header, args.force)

    logger.info("Successfully terminated.")
예제 #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
    colours = {'i': 10, 'o': 11}

    # prepare parameters
    image_directory = False
    contour_directory = False
    for item in os.listdir(args.input):
        if not os.path.isdir('{}/{}'.format(args.input, item)): continue
        if 'dicom' in item:
            image_directory = '{}/{}'.format(args.input, item)
        elif 'contours-manual' in item:
            contour_directory = '{}/{}'.format(args.input, item)

    if not (image_directory and contour_directory):
        raise ArgumentError(
            'The supplied source directory {} is invalid.'.format(args.input))

    # count total number of slices via counting the dicom files
    slice_number = len([
        name for name in os.listdir(image_directory)
        if os.path.isfile('{}/{}'.format(image_directory, name))
    ])

    # extract other dimensionalities from first dicom image
    dicom_files = [
        '{}/{}'.format(image_directory, name)
        for name in os.listdir(image_directory)
        if os.path.isfile('{}/{}'.format(image_directory, name))
        and '.dcm' in name
    ]
    example_data, _ = load(dicom_files[0])

    # create target image
    result_data = scipy.zeros(tuple([slice_number]) + example_data.shape,
                              dtype=scipy.uint8)

    # iterate over contour files
    for contour_file in os.listdir(contour_directory):
        logger.info('Parsing contour file {}...'.format(contour_file))

        # determine type of contour by filename
        _, slice_number, contour_type, _ = contour_file.split('-')
        slice_number = int(slice_number)
        contour_type = contour_type[0]

        # select "colour"
        colour = colours[contour_type]

        last_x = False
        last_y = False

        # iterate over file content
        with open('{}/{}'.format(contour_directory, contour_file), 'r') as f:
            for line in f.readlines():
                # clean and test line
                line = line.strip()
                if 0 == len(line) or '#' == line[0]: continue
                # extract contour coordinates and round to full number
                x, y = map(int, map(round, map(float, line.split(' '))))
                # paint contours
                result_data[slice_number, x, y] = colour
                # paint additional contours if jump was to large
                if last_x and last_y:
                    for _x, _y in __get_hole_coordinates(last_x, last_y, x, y):
                        result_data[slice_number, _x, _y] = colour
                # set current coordinates as last
                last_x = x
                last_y = y

    # save result contour volume
    save(result_data, args.output, False, args.force)

    logger.info("Successfully terminated.")
예제 #19
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 original example volume
    original_data, original_header = load(args.original)

    # prepare execution
    inner_data = scipy.zeros(original_data.shape, scipy.uint8)
    outer_data = scipy.zeros(original_data.shape, scipy.uint8)
    del original_data

    # First step: Constructing all marker images
    logger.info('Constructing marker information...')
    basename_old = False
    # iterate over marker images
    for marker_image in args.input:
        # extract information from filename and prepare slicer object
        basename, slice_dimension, slice_number = re.match(
            r'.*m(.*)_d([0-9])_s([0-9]{4}).*', marker_image).groups()
        slice_dimension = int(slice_dimension)
        slice_number = int(slice_number)
        # check basenames
        if basename_old and not basename_old == basename:
            logger.warning(
                'The marker seem to come from different sources. Encountered basenames {} and {}. Continuing anyway.'
                .fromat(basename, basename_old))
        basename_old = basename
        # prepare slicer
        slicer = [slice(None)] * inner_data.ndim
        slicer[slice_dimension] = slice_number
        # load marker image and prepare (deleting markers with indices > 10 and mergin all others)
        marker_data, _ = load(marker_image)
        marker_data[marker_data >= 10] = 0
        # create markers from sparse marker data
        marker_data_inner, marker_data_outer = __create_markers(
            marker_data.astype(scipy.bool_), slice_dimension, args.di, args.do)
        # add to resulting final marker images
        inner_data[slicer] += marker_data_inner
        outer_data[slicer] += marker_data_outer

    # check if any marker present
    if 0 == len(inner_data.nonzero()[0]):
        raise ArgumentError('No markers for the inner wall could be created.')
    if 0 == len(outer_data.nonzero()[0]):
        raise ArgumentError('No markers for the outer wall could be created.')

    # Second step: Thin out marker information by emptying every second slice
    if args.thinning:
        logger.info('Thinning out every second slice...')
        inner_data = __thin_out_markers(inner_data)
        outer_data = __thin_out_markers(outer_data)

    # Third step: Enhance marker information by propagating it over time
    logger.info('Enhancing marker information...')
    inner_data = __enhance_markers(inner_data, args.efg, args.ebg)
    outer_data = __enhance_markers(outer_data, args.di, args.do)

    # saving
    inner_name = args.output.format('i')
    save(inner_data, inner_name, original_header, args.force)

    outer_name = args.output.format('o')
    save(outer_data, outer_name, original_header, args.force)

    logger.info("Successfully terminated.")
예제 #20
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
    temporal_dimension = 3
    spatial_dimension = 0
    
    # slice-wise threshold values (starting from first basal slice downwards)
    # the first value of the tuple represents the threshold, the second the erosion's iterations
    
    # generic fixed to 0.0 (single threshold)
    #bg_ed = [(0.0, 0), (0.0, 0), (0.0, 0), (0.0, 0), (0.0, 0), (0.0, 0), (0.0, 0), (0.0, 0)]
    #bg_es = [(0.0, 0), (0.0, 0), (0.0, 0), (0.0, 0), (0.0, 0), (0.0, 0), (0.0, 0)]
    
    
    # rigid01-bg
    bg_ed = [(0.14, 10), (0.14, 8), (0.21, 10), (0.14, 6), (0.21, 6), (0.21, 8), (0.14, 6), (0.21, 6), (0.21, 4)]
    bg_es = [(0.0, 8), (0.07, 10), (0.14, 10), (0.14, 8), (0.21, 10), (0.07, 2), (0.21, 8), (0.07, 6)]
    # rigid01-fg maxcover
    fg_ed = [(0.52, 2), (0.52, 4), (0.52, 4), (0.52, 4), (0.72, 0), (0.72, 0), (0.52, 2), (0.32, 10), (0.39, 2)]
    fg_es = [(0.26, 8), (0.39, 4), (0.59, 4), (0.39, 8), (0.72, 2), (0.79, 2), (0.65, 0), (0.45, 2)]
    # rigid01-fg minerror
    #fg_ed = [(0.52, 4), (0.52, 4), (0.65, 4), (0.52, 6), (0.72, 2), (0.72, 2), (0.45, 4), (0.39, 10), (0.45, 0)]
    #fg_es = [(0.32, 8), (0.39, 6), (0.79, 2), (0.79, 2), (0.72, 4), (0.79, 2), (0.79, 0), (0.79, 2)]
    
    # rigid08-bg
    #bg_ed = [(0.07, 8),  (0.07, 8),  (0.21, 10),  (0.14, 6),  (0.21, 6),  (0.14, 4),  (0.14, 6),  (0.21, 8),  (0.21, 4)]
    #bg_es = [(0.0, 8),  (0.07, 10),  (0.14, 10),  (0.21, 10),  (0.21, 8),  (0.07, 2),  (0.21, 8),  (0.07, 6)]
    # rigid08-fg maxcover
    #fg_ed = [(0.45, 4),  (0.45, 4),  (0.65, 2),  (0.52, 4),  (0.59, 2),  (0.59, 2),  (0.52, 2),  (0.39, 8),  (0.39, 2)]
    #fg_es = [(0.26, 8),  (0.39, 4),  (0.39, 8),  (0.45, 6),  (0.72, 2),  (0.72, 2),  (0.59, 2),  (0.65, 0)]
    
    # rigid09-bg
    #bg_ed = [(0.07, 6),  (0.14, 10),  (0.21, 10),  (0.21, 6),  (0.21, 6),  (0.14, 6),  (0.21, 10),  (0.21, 8),  (0.21, 2)]
    #bg_es = [(0.0, 8),  (0.07, 10),  (0.14, 10),  (0.21, 10),  (0.21, 8),  (0.14, 4),  (0.21, 6),  (0.07, 6)]
    # rigid09-fg maxcover
    #fg_ed = [(0.52, 2),  (0.45, 4),  (0.52, 4),  (0.52, 4),  (0.52, 4),  (0.52, 4),  (0.52, 2),  (0.85, 0),  (0.26, 4)]
    #fg_es = [(0.19, 8),  (0.32, 6),  (0.39, 8),  (0.39, 8),  (0.59, 6),  (0.85, 0),  (0.59, 2),  (0.52, 0)]
    
    # check if output file already exists
    if not args.force and os.path.exists(args.output):
        raise ArgumentError('The supplied output file {} already exists.'.format(args.output))
    
    # loading atlas image
    atlas_data, atlas_header = load(args.atlas)
    
    logger.info('Extracting ED and ES phase volumes...')
    
    # extract ED 3D volume
    slicer = [slice(None) for _ in range(atlas_data.ndim)]
    slicer[temporal_dimension] = slice(0,1) # first is ed phase
    ed_data_fg = scipy.squeeze(atlas_data[slicer])
    
    # extract ES 3D volume
    es_data_fg = None
    for slidx in range(1, atlas_data.shape[temporal_dimension]):
        slicer[temporal_dimension] = slice(slidx, slidx + 1)
        if scipy.any(atlas_data[slicer]):
            es_data_fg = scipy.squeeze(atlas_data[slicer]) # first after ed phase is es phase
            es_slidx = slidx # conserve for later usage
            continue
        
    if None == es_data_fg:
        raise AttributeError('Could not find any data in the ES phase of the atlas image.')
        
    logger.debug('Extracted ed volume {} and es volume {}.'.format(ed_data_fg.shape, es_data_fg.shape))
    
    # iterate over slices of ED and ES phases and apply the respective thresholding and erosion operations
    logger.info('Splitting into FG and BG binary volumes representing the markers...')
    ed_data_bg = ed_data_fg.copy()
    es_data_bg = es_data_fg.copy()
    for slid in range(0, ed_data_fg.shape[spatial_dimension]):
        # create fg markers
        if slid < len(fg_ed): ed_data_fg[slid] = extract_fg_markers(ed_data_fg[slid], *fg_ed[slid])
        else: ed_data_fg[slid] = 0
        if slid < len(fg_es): es_data_fg[slid] = extract_fg_markers(es_data_fg[slid], *fg_es[slid])
        else: es_data_fg[slid] = 0
        # create bg markers
        if slid < len(bg_ed): ed_data_bg[slid] = extract_bg_markers(ed_data_bg[slid], *bg_ed[slid])
        else: ed_data_bg[slid] = 0
        if slid < len(bg_es): es_data_bg[slid] = extract_bg_markers(es_data_bg[slid], *bg_es[slid])
        else: es_data_bg[slid] = 0
    
    ed_data_fg = ed_data_fg.astype(scipy.bool_)
    es_data_fg = es_data_fg.astype(scipy.bool_)
    ed_data_bg = ed_data_bg.astype(scipy.bool_)
    es_data_bg = es_data_bg.astype(scipy.bool_)
    
    # see if any is empty
    if not scipy.any(es_data_fg):
        raise AttributeError('No foreground marker data for es phase.')
    elif not scipy.any(es_data_bg):
        raise AttributeError('No background marker data for es phase.')
    elif not scipy.any(ed_data_fg):
        raise AttributeError('No foreground marker data for ed phase.')
    elif not scipy.any(ed_data_bg):
        raise AttributeError('No background marker data for ed phase.')
    
    marker_data = scipy.zeros(atlas_data.shape, scipy.uint8)
    
    # enhance the markers by interpolating between binary foreground objects of ED and ES phase
    logger.info('Enhancing the markers from ED-first to ES phase...')
    slicer[temporal_dimension] = slice(0, es_slidx + 1) # first half
    marker_data[slicer] = interpolateBetweenBinaryObjects(ed_data_fg, es_data_fg, es_slidx - 1)
    logger.info('Enhancing the markers from ES to ED-second phase...')
    slicer[temporal_dimension] = slice(es_slidx + 1, None) # second half
    marker_data[slicer] = interpolateBetweenBinaryObjects(es_data_fg, ed_data_fg, marker_data.shape[temporal_dimension] - es_slidx - 3)
    
    # setting the background markers
    logger.info('Setting bg markers...')
    slicer[temporal_dimension] = slice(0,1) # first ed phase
    marker_data[slicer][ed_data_bg] = 2
    slicer[temporal_dimension] = slice(-1, None) # second ed phase
    marker_data[slicer][ed_data_bg] = 2
    slicer[temporal_dimension] = slice(es_slidx, es_slidx + 1) # es phase
    marker_data[slicer][es_data_bg] = 2
        
    save(marker_data, args.output, atlas_header, args.force)
        
    logger.info("Successfully terminated.")
예제 #21
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 images
    data_fixed, header_fixed = load(args.fixed)
    data_moving, header_moving = load(args.moving)

    # convert to binary arrays
    data_fixed = data_fixed.astype(scipy.bool_)
    data_moving = data_moving.astype(scipy.bool_)

    # check that they are 3D volumes and contain an object
    if not 3 == data_fixed.ndim:
        raise ArgumentError(
            'The fixed image has {} instead of the expected 3 dimensions.'.
            format(data_fixed.ndim))
    if not 3 == data_moving.ndim:
        raise ArgumentError(
            'The moving image has {} instead of the expected 3 dimensions.'.
            format(data_moving.ndim))
    if not scipy.any(data_fixed):
        raise ArgumentError('The fixed image contains no binary object.')
    if not scipy.any(data_moving):
        raise ArgumentError('The moving image contains no binary object.')

    # get voxel spacing of fixed image
    fixed_spacing = header.get_pixel_spacing(header_fixed)

    # extract the first basal slices form both RV objects
    basal_fixed, basal_fixed_spacing = extract_basal_slice(
        data_fixed, header_fixed)
    basal_moving, basal_moving_spacing = extract_basal_slice(
        data_moving, header_moving)
    logger.debug(
        'Extracted basal slices fixed: {} and moving: {} with voxel spacing {} resp. {}.'
        .format(basal_fixed.shape, basal_moving.shape, basal_fixed_spacing,
                basal_moving_spacing))

    # get points of interest
    fixed_basep, fixed_otherp = get_points_of_interest(basal_fixed)
    moving_basep, moving_otherp = get_points_of_interest(basal_moving)
    logger.debug(
        'Points of interest found are fixed: {} / {} and moving: {} / {}.'.
        format(fixed_basep, fixed_otherp, moving_basep, moving_otherp))

    # translate all points of interest to physical coordinate system
    fixed_basep = [x * y for x, y in zip(fixed_basep, basal_fixed_spacing)]
    fixed_otherp = [x * y for x, y in zip(fixed_otherp, basal_fixed_spacing)]
    moving_basep = [x * y for x, y in zip(moving_basep, basal_moving_spacing)]
    moving_otherp = [
        x * y for x, y in zip(moving_otherp, basal_moving_spacing)
    ]
    logger.debug(
        'Points of interest translated to real-world coordinates are fixed: {} / {} and moving: {} / {}.'
        .format(fixed_basep, fixed_otherp, moving_basep, moving_otherp))

    # determine shift to unite the two base-points
    shift = (fixed_basep[0] - moving_basep[0],
             fixed_basep[1] - moving_basep[1])
    logger.debug('Shift to unite base-point is {}.'.format(shift))

    # shift the vector end-point of the moving object's vector so that it shares the
    # same base as the fixed vector
    moving_otherp = shiftp(moving_otherp, shift)
    logger.debug('Shifted vector end-point of moving image is {}.'.format(
        moving_otherp))

    # assure correctness of shift
    if not scipy.all(
        [x == y for x, y in zip(fixed_basep, shiftp(moving_basep, shift))]):
        raise ArgumentError(
            'Aligning base-point through shifting failed due to unknown reason: {} does not equal {}.'
            .format(shiftp(moving_basep, shift), fixed_basep))

    # shift both vector end-points to origin base
    fixed_otherp = shiftp(fixed_otherp, map(lambda x: -1. * x, fixed_basep))
    moving_otherp = shiftp(moving_otherp, map(lambda x: -1. * x, fixed_basep))

    # determine angle
    angle = angle_between_vectors(fixed_otherp, moving_otherp)
    logger.debug('Angle set to {} degree in radians.'.format(
        math.degrees(angle)))

    # determine angle turn point
    turn_point = fixed_basep
    logger.debug(
        'Turn point set to {} in real-world coordinates.'.format(turn_point))

    # reverse shift to fit into the 'elastix' view
    shift = [-1. * x for x in shift]

    # print results
    print '// {}'.format(math.degrees(angle))
    print """
//# SOME NOTES ON 'ELASTIX'
//# 1. 'elastix' performs shifting before rotation!
//# 2. 'elastix' works on the real world coordinates (i.e. with voxel spacing of 0)
"""
    print transform_string(data_fixed.shape, fixed_spacing, [0] + list(shift),
                           [angle, 0, 0], [0] + list(turn_point))

    logger.info("Successfully terminated.")