Helper Module for Deep Learning.
Source code for pynet.augmentation.transform
# -*- coding: utf-8 -*-
##########################################################################
# NSAp - Copyright (C) CEA, 2020
# Distributed under the terms of the CeCILL-B license, as published by
# the CEA-CNRS-INRIA. Refer to the LICENSE file or to
# http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
# for details.
##########################################################################
"""
Module that defines common tools to deal with spatial transformations.
Code: https://github.com/matthew-brett/transforms3d
Code: https://github.com/bsciolla/gaussian-random-fields
"""
# Import
import math
import numpy as np
import scipy.fftpack
# Caching dictionary for common shear Ns, indices
_shearers = {}
for n in range(1, 11):
x = (n**2 + n) / 2.0
i = n + 1
_shearers[x] = (i, np.triu(np.ones((i, i)), 1).astype(bool))
[docs]def affine_flow(affine, shape):
""" Generates a flow field given an affine matrix.
Parameters
----------
affine: Tensor (4, 4)
an affine transform.
shape: tuple
the target output image shape.
Returns
-------
flow: Tensor
the generated affine flow field.
"""
ranges = [np.arange(size) for size in shape]
ranges[0], ranges[1] = ranges[1], ranges[0]
mesh = np.asarray(np.meshgrid(*ranges))
mesh[[0, 1]] = mesh[[1, 0]]
locs = np.asarray(mesh)
offset = (np.array(shape) - 1) / 2
locs = np.transpose(locs.T - offset)
ones = np.ones([1] + list(locs.shape)[1:], dtype=locs.dtype)
homography_grid = np.concatenate([locs, ones], axis=0)
shape = homography_grid.shape
homography_grid = homography_grid.reshape(shape[0], -1)
flow = np.dot(affine, homography_grid)
flow = flow.reshape(shape)
flow = flow[:(len(shape) - 1)]
flow = np.transpose(flow.T + offset)
return flow
[docs]def compose(T, R, Z, S=None):
""" Compose translations, rotations, zooms, [shears] to affine
Parameters
----------
T: array (N,)
translations, where N is usually 3 (3D case)
R: array (N, N)
rotation matrix where N is usually 3 (3D case)
Z: array (N,)
zooms, where N is usually 3 (3D case)
S: array (P,), default None
shear vector, such that shears fill upper triangle above
diagonal to form shear matrix. P is the (N-2)th Triangular
number, which happens to be 3 for a 4x4 affine (3D case)
Returns
-------
A: array (N+1, N+1)
affine transformation matrix where N usually == 3
(3D case)
"""
n = len(T)
R = np.asarray(R)
if R.shape != (n, n):
raise ValueError("Wrong rotation matrix shape.")
A = np.eye(n + 1)
if S is not None:
Smat = striu2mat(S)
ZS = np.dot(np.diag(Z), Smat)
else:
ZS = np.diag(Z)
A[:n, :n] = np.dot(R, ZS)
A[:n, n] = T[:]
return A
[docs]def striu2mat(striu):
""" Construct shear matrix from upper triangular vector.
Parameters
----------
striu: array (N,)
vector giving triangle above diagonal of shear matrix.
Returns
-------
SM: array (N, N)
shear matrix.
"""
n = len(striu)
if n in _shearers:
N, inds = _shearers[n]
else:
N = ((-1 + math.sqrt(8 * n + 1)) / 2.0) + 1 # n+1 th root
if N != math.floor(N):
raise ValueError(
"{0} is a strange number of shear elements".format(n))
N = int(N)
inds = np.triu(np.ones((N, N)), 1).astype(bool)
M = np.eye(N)
M[inds] = striu
return M
[docs]def fftind(shape):
""" Returns a numpy array of shifted Fourier coordinates.
Parameters
----------
shape: uplet
the shape of the coordinate array to create.
Returns
-------
k_ind: array (2, size, size) with:
shifted Fourier coordinates.
"""
half_shape = (np.array(shape) + 1) / 2
k_ind = np.mgrid[[slice(0, size) for size in shape]]
k_ind = np.transpose(k_ind.T - half_shape)
k_ind = scipy.fftpack.fftshift(k_ind)
return k_ind
[docs]def gaussian_random_field(shape, alpha=3.0, normalize=True, seed=None):
""" Generates 3D gaussian random maps.
The probability distribution of each variable follows a Normal
distribution.
Parameters
----------
shape: uplet,
the shape of the output Gaussian random fields.
alpha: flaot, default 3
the power of the power-law momentum distribution.
normalize: bool, default True
normalizes the Gaussian field to have an average of 0.0 and a standard
deviation of 1.0.
seed: int, default None
seed to control random number generator.
Returns
-------
gfield: array
the gaussian random field.
"""
# Defines momentum indices
k_idx = fftind(shape)
# Defines the amplitude as a power law 1/|k|^(alpha/2)
amplitude = np.power(np.sum(k_idx**2, axis=0) + 1e-10, -alpha/4.0)
amplitude[0, 0] = 0
# Draws a complex gaussian random noise with normal (circular) distribution
if seed is not None:
np.random.seed(seed)
noise = np.random.normal(size=shape) + 0j
if seed is not None:
np.random.seed(seed + 1)
noise += 1j * np.random.normal(size=shape)
# To real space
gfield = np.fft.ifft2(noise * amplitude).real
# Sets the standard deviation to one
if normalize:
gfield = gfield - np.mean(gfield)
gfield = gfield / np.std(gfield)
return gfield
Follow us
© 2019, pynet developers .
Inspired by AZMIND template.
Inspired by AZMIND template.