Source code for dltk.io.augmentation

from __future__ import unicode_literals
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import

import numpy as np
from scipy.ndimage.interpolation import map_coordinates
from scipy.ndimage.filters import gaussian_filter


[docs]def flip(imagelist, axis=1): """Randomly flip spatial dimensions Args: imagelist (np.ndarray or list or tuple): image(s) to be flipped axis (int): axis along which to flip the images Returns: np.ndarray or list or tuple: same as imagelist but randomly flipped along axis """ # Check if a single image or a list of images has been passed was_singular = False if isinstance(imagelist, np.ndarray): imagelist = [imagelist] was_singular = True # With a probility of 0.5 flip the image(s) across `axis` do_flip = np.random.random(1) if do_flip > 0.5: for i in range(len(imagelist)): imagelist[i] = np.flip(imagelist[i], axis=axis) if was_singular: return imagelist[0] return imagelist
[docs]def add_gaussian_offset(image, sigma=0.1): """ Add Gaussian offset to an image. Adds the offset to each channel independently. Args: image (np.ndarray): image to add noise to sigma (float): stddev of the Gaussian distribution to generate noise from Returns: np.ndarray: same as image but with added offset to each channel """ offsets = np.random.normal(0, sigma, ([1] * (image.ndim - 1) + [image.shape[-1]])) image += offsets return image
[docs]def add_gaussian_noise(image, sigma=0.05): """ Add Gaussian noise to an image Args: image (np.ndarray): image to add noise to sigma (float): stddev of the Gaussian distribution to generate noise from Returns: np.ndarray: same as image but with added offset to each channel """ image += np.random.normal(0, sigma, image.shape) return image
[docs]def elastic_transform(image, alpha, sigma): """ Elastic deformation of images as described in [1]. [1] Simard, Steinkraus and Platt, "Best Practices for Convolutional Neural Networks applied to Visual Document Analysis", in Proc. of the International Conference on Document Analysis and Recognition, 2003. Based on gist https://gist.github.com/erniejunior/601cdf56d2b424757de5 Args: image (np.ndarray): image to be deformed alpha (list): scale of transformation for each dimension, where larger values have more deformation sigma (list): Gaussian window of deformation for each dimension, where smaller values have more localised deformation Returns: np.ndarray: deformed image """ assert len(alpha) == len(sigma), \ "Dimensions of alpha and sigma are different" channelbool = image.ndim - len(alpha) out = np.zeros((len(alpha) + channelbool, ) + image.shape) # Generate a Gaussian filter, leaving channel dimensions zeroes for jj in range(len(alpha)): array = (np.random.rand(*image.shape) * 2 - 1) out[jj] = gaussian_filter(array, sigma[jj], mode="constant", cval=0) * alpha[jj] # Map mask to indices shapes = list(map(lambda x: slice(0, x, None), image.shape)) grid = np.broadcast_arrays(*np.ogrid[shapes]) indices = list(map((lambda x: np.reshape(x, (-1, 1))), grid + np.array(out))) # Transform image based on masked indices transformed_image = map_coordinates(image, indices, order=0, mode='reflect').reshape(image.shape) return transformed_image
[docs]def extract_class_balanced_example_array(image, label, example_size=[1, 64, 64], n_examples=1, classes=2, class_weights=None): """Extract training examples from an image (and corresponding label) subject to class balancing. Returns an image example array and the corresponding label array. Args: image (np.ndarray): image to extract class-balanced patches from label (np.ndarray): labels to use for balancing the classes example_size (list or tuple): shape of the patches to extract n_examples (int): number of patches to extract in total classes (int or list or tuple): number of classes or list of classes to extract Returns: np.ndarray, np.ndarray: class-balanced patches extracted from full images with the shape [batch, example_size..., image_channels] """ assert image.shape[:-1] == label.shape, 'Image and label shape must match' assert image.ndim - 1 == len(example_size), \ 'Example size doesnt fit image size' assert all([i_s >= e_s for i_s, e_s in zip(image.shape, example_size)]), \ 'Image must be larger than example shape' rank = len(example_size) if isinstance(classes, int): classes = tuple(range(classes)) n_classes = len(classes) assert n_examples >= n_classes, \ 'n_examples need to be greater than n_classes' if class_weights is None: n_ex_per_class = np.ones(n_classes).astype(int) * int(np.round(n_examples / n_classes)) else: assert len(class_weights) == n_classes, \ 'Class_weights must match number of classes' class_weights = np.array(class_weights) n_ex_per_class = np.round((class_weights / class_weights.sum()) * n_examples).astype(int) # Compute an example radius to define the region to extract around a # center location ex_rad = np.array(list(zip(np.floor(np.array(example_size) / 2.0), np.ceil(np.array(example_size) / 2.0))), dtype=np.int) class_ex_images = [] class_ex_lbls = [] min_ratio = 1. for c_idx, c in enumerate(classes): # Get valid, random center locations belonging to that class idx = np.argwhere(label == c) ex_images = [] ex_lbls = [] if len(idx) == 0 or n_ex_per_class[c_idx] == 0: class_ex_images.append([]) class_ex_lbls.append([]) continue # Extract random locations r_idx_idx = np.random.choice(len(idx), size=min(n_ex_per_class[c_idx], len(idx)), replace=False).astype(int) r_idx = idx[r_idx_idx] # Shift the random to valid locations if necessary r_idx = np.array( [np.array([max(min(r[dim], image.shape[dim] - ex_rad[dim][1]), ex_rad[dim][0]) for dim in range(rank)]) for r in r_idx]) for i in range(len(r_idx)): # Extract class-balanced examples from the original image slicer = [slice(r_idx[i][dim] - ex_rad[dim][0], r_idx[i][dim] + ex_rad[dim][1]) for dim in range(rank)] ex_image = image[slicer][np.newaxis, :] ex_lbl = label[slicer][np.newaxis, :] # Concatenate them and return the examples ex_images = np.concatenate((ex_images, ex_image), axis=0) \ if (len(ex_images) != 0) else ex_image ex_lbls = np.concatenate((ex_lbls, ex_lbl), axis=0) \ if (len(ex_lbls) != 0) else ex_lbl class_ex_images.append(ex_images) class_ex_lbls.append(ex_lbls) ratio = n_ex_per_class[c_idx] / len(ex_images) min_ratio = ratio if ratio < min_ratio else min_ratio indices = np.floor(n_ex_per_class * min_ratio).astype(int) ex_images = np.concatenate([cimage[:idxs] for cimage, idxs in zip(class_ex_images, indices) if len(cimage) > 0], axis=0) ex_lbls = np.concatenate([clbl[:idxs] for clbl, idxs in zip(class_ex_lbls, indices) if len(clbl) > 0], axis=0) return ex_images, ex_lbls
[docs]def extract_random_example_array(image_list, example_size=[1, 64, 64], n_examples=1): """Randomly extract training examples from image (and a corresponding label). Returns an image example array and the corresponding label array. Args: image_list (np.ndarray or list or tuple): image(s) to extract random patches from example_size (list or tuple): shape of the patches to extract n_examples (int): number of patches to extract in total Returns: np.ndarray, np.ndarray: class-balanced patches extracted from full images with the shape [batch, example_size..., image_channels] """ assert n_examples > 0 was_singular = False if isinstance(image_list, np.ndarray): image_list = [image_list] was_singular = True assert all([i_s >= e_s for i_s, e_s in zip(image_list[0].shape, example_size)]), \ 'Image must be bigger than example shape' assert (image_list[0].ndim - 1 == len(example_size) or image_list[0].ndim == len(example_size)), \ 'Example size doesnt fit image size' for i in image_list: if len(image_list) > 1: assert (i.ndim - 1 == image_list[0].ndim or i.ndim == image_list[0].ndim or i.ndim + 1 == image_list[0].ndim),\ 'Example size doesnt fit image size' assert all([i0_s == i_s for i0_s, i_s in zip(image_list[0].shape, i.shape)]), \ 'Image shapes must match' rank = len(example_size) # Extract random examples from image and label valid_loc_range = [image_list[0].shape[i] - example_size[i] for i in range(rank)] rnd_loc = [np.random.randint(valid_loc_range[dim], size=n_examples) if valid_loc_range[dim] > 0 else np.zeros(n_examples, dtype=int) for dim in range(rank)] examples = [[]] * len(image_list) for i in range(n_examples): slicer = [slice(rnd_loc[dim][i], rnd_loc[dim][i] + example_size[dim]) for dim in range(rank)] for j in range(len(image_list)): ex_image = image_list[j][slicer][np.newaxis] # Concatenate and return the examples examples[j] = np.concatenate((examples[j], ex_image), axis=0) \ if (len(examples[j]) != 0) else ex_image if was_singular: return examples[0] return examples