Source code for nntoolbox.vision.transforms.transforms
from scipy.ndimage.filters import gaussian_filter
from scipy.ndimage.interpolation import map_coordinates
from PIL import Image
import numpy as np
import collections
__all__ = ['ElasticDeformation', 'Cutout']
[docs]class ElasticDeformation(object):
"""
Apply elastic deformation on a PIL image (H x W x C)
Adapt from https://gist.github.com/oeway/2e3b989e0343f0884388ed7ed82eb3b0
Paper: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.10.5032&rep=rep1&type=pdf
"""
def __init__(self, alpha, sigma):
self.alpha = alpha
self.sigma = sigma
def __call__(self, image: Image)->Image:
if isinstance(self.alpha, collections.Sequence):
alpha = random_num_generator(self.alpha)
else:
alpha = self.alpha
if isinstance(self.sigma, collections.Sequence):
sigma = random_num_generator(self.sigma)
else:
sigma = self.sigma
return ElasticDeformation.elastic_deform(image, alpha=alpha, sigma=sigma)
[docs] @staticmethod
def elastic_deform(image:Image, alpha=1000, sigma=30, spline_order=1, mode='nearest') -> Image:
"""Elastic deformation of image as described in [Simard2003]_.
.. [Simard2003] 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.
:param image: The image to be deformed
:param alpha: scaling factor that controls the intensity of the deformation
:param sigma: the std of gaussian filters. Smaller sigma implies more random deformation field
:param spline_order
:param mode: interpolation mode
"""
image = np.array(image)
shape = image.shape[:2]
dx = gaussian_filter((np.random.rand(*shape) * 2 - 1),
sigma, mode="constant", cval=0) * alpha
dy = gaussian_filter((np.random.rand(*shape) * 2 - 1),
sigma, mode="constant", cval=0) * alpha
x, y = np.meshgrid(np.arange(shape[0]), np.arange(shape[1]), indexing='ij')
indices = [np.reshape(x + dx, (-1, 1)), np.reshape(y + dy, (-1, 1))]
result = np.empty_like(image)
for i in range(image.shape[2]):
result[:, :, i] = map_coordinates(
image[:, :, i], indices, order=spline_order, mode=mode).reshape(shape)
return Image.fromarray(result)
[docs]class Cutout(object):
'''
https://arxiv.org/pdf/1708.04552.pdf
'''
def __init__(self, n_holes, length):
self._n_holes = n_holes
self._length = length
def __call__(self, image: Image) -> Image:
h, w = image.size
ret = np.array(image)
for _ in range(self._n_holes):
h1 = np.random.choice(h)
h2 = min(h, h1 + self._length)
w1 = np.random.choice(w)
w2 = min(w, w1 + self._length)
ret[h1:h2, w1:w2] = 0
return Image.fromarray(ret)
def random_num_generator(config):
if config[0] == 'uniform':
ret = np.random.uniform(config[1], config[2], 1)[0]
elif config[0] == 'lognormal':
ret = np.random.lognormal(config[1], config[2], 1)[0]
else:
print(config)
raise Exception('unsupported format')
return ret