Skip to content
This repository has been archived by the owner on Mar 17, 2021. It is now read-only.

Commit

Permalink
Merge pull request #246 from NifTK/242-gaussian-smoothing
Browse files Browse the repository at this point in the history
Add Gaussian smoothing before undersampling
  • Loading branch information
wyli committed Oct 16, 2018
2 parents c21fe62 + 5e306a6 commit f337825
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 15 deletions.
25 changes: 25 additions & 0 deletions doc/source/config_spec.md
Expand Up @@ -567,7 +567,15 @@ Value should be in `[0, 1]`.
---- | ---- | ------- | -------
[rotation_angle](#rotation-angle) | `float array` | `rotation_angle=-10.0,10.0` | `''`
[scaling_percentage](#scaling-percentage) | `float array` | `scaling_percentage=-20.0,20.0` | `''`
[antialiasing](#scaling-percentage) | `boolean` | `antialiasing=True` | `True`
[random_flipping_axes](#random-flipping-axes) | `integer array` | `random_flipping_axes=1,2` | `-1`
[do_elastic_deformation](#do-elastic-deformation) | `boolean` | `do_elastic_deformation=True` | `False`
[num_ctrl_points](#do-elastic-deformation) | `integer` | `num_ctrl_points=1` | `4`
[deformation_sigma](#do-elastic-deformation) | `float` | `deformation_sigma=1` | `15`
[proportion_to_deform](#do-elastic-deformation) | `float` | `proportion_to_deform=0.7` | `0.5`
[bias_field_range](#bias-field-range) | `float array` | `bias_field_range=-10.0,10.0` | `''`
[bf_order](#bias-field-range) | `integer` | `bf_order=1` | `3`


###### `rotation_angle`
Float array, indicates a random rotation operation should be applied to the
Expand All @@ -580,12 +588,29 @@ The option accepts percentages relative to 100 (the original input size).
E.g, `(-50, 50)` indicates transforming
image (size `d`) to image with its size in between `0.5*d` and `1.5d`.

When random scaling is enabled, it is possible to further specify:
- `antialiasing` indicating if antialiasing should be performed
when randomly downsampling the input images.

###### `random_flipping_axes`
The axes which can be flipped to augment the data.
Supply as comma-separated values within single quotes, e.g. '0,1'.
Note that these are 0-indexed, so choose some combination of 0, 1.

###### `do_elastic_deformation`
Boolean value indicates data augmentation using elastic deformations

When `do_elastic_deformation=True`, it is possible to further specify:
- `num_ctrl_points` -- number of control points for the elastic deformation,
- `deformation_sigma` -- the standard deviation for the elastic deformation,
- `proportion_to_deform` -- what fraction of samples to deform elastically.

###### `bias_field_range`
Float array, indicates data augmentation with randomised bias field

When `bias_field_range` is not None, it is possible to further specify:
- `bf_order` -- maximal polynomial order to use for the bias field augmentation.


### INFERENCE

Expand Down
3 changes: 2 additions & 1 deletion niftynet/application/classification_application.py
Expand Up @@ -135,7 +135,8 @@ def initialise_dataset_loader(
if train_param.scaling_percentage:
augmentation_layers.append(RandomSpatialScalingLayer(
min_percentage=train_param.scaling_percentage[0],
max_percentage=train_param.scaling_percentage[1]))
max_percentage=train_param.scaling_percentage[1],
antialiasing=train_param.antialiasing))
if train_param.rotation_angle or \
self.action_param.rotation_angle_x or \
self.action_param.rotation_angle_y or \
Expand Down
3 changes: 2 additions & 1 deletion niftynet/application/gan_application.py
Expand Up @@ -103,7 +103,8 @@ def initialise_dataset_loader(
if self.action_param.scaling_percentage:
augmentation_layers.append(RandomSpatialScalingLayer(
min_percentage=self.action_param.scaling_percentage[0],
max_percentage=self.action_param.scaling_percentage[1]))
max_percentage=self.action_param.scaling_percentage[1],
antialiasing=self.action_param.antialiasing))
if self.action_param.rotation_angle:
augmentation_layers.append(RandomRotationLayer())
augmentation_layers[-1].init_uniform_angle(
Expand Down
3 changes: 2 additions & 1 deletion niftynet/application/regression_application.py
Expand Up @@ -124,7 +124,8 @@ def initialise_dataset_loader(
if train_param.scaling_percentage:
augmentation_layers.append(RandomSpatialScalingLayer(
min_percentage=train_param.scaling_percentage[0],
max_percentage=train_param.scaling_percentage[1]))
max_percentage=train_param.scaling_percentage[1],
antialiasing=train_param.antialiasing))
if train_param.rotation_angle:
rotation_layer = RandomRotationLayer()
if train_param.rotation_angle:
Expand Down
3 changes: 2 additions & 1 deletion niftynet/application/segmentation_application.py
Expand Up @@ -150,7 +150,8 @@ def initialise_dataset_loader(
if train_param.scaling_percentage:
augmentation_layers.append(RandomSpatialScalingLayer(
min_percentage=train_param.scaling_percentage[0],
max_percentage=train_param.scaling_percentage[1]))
max_percentage=train_param.scaling_percentage[1],
antialiasing=train_param.antialiasing))
if train_param.rotation_angle or \
train_param.rotation_angle_x or \
train_param.rotation_angle_y or \
Expand Down
3 changes: 2 additions & 1 deletion niftynet/contrib/preprocessors/preprocessing.py
Expand Up @@ -57,7 +57,8 @@ def prepare_augmentation_layers(self):
if self.action_param.scaling_percentage:
augmentation_layers.append(RandomSpatialScalingLayer(
min_percentage=self.action_param.scaling_percentage[0],
max_percentage=self.action_param.scaling_percentage[1]))
max_percentage=self.action_param.scaling_percentage[1],
antialiasing=self.action_param.antialiasing))
if self.action_param.rotation_angle or \
self.action_param.rotation_angle_x or \
self.action_param.rotation_angle_y or \
Expand Down
40 changes: 30 additions & 10 deletions niftynet/layer/rand_spatial_scaling.py
Expand Up @@ -4,7 +4,7 @@
import warnings

import numpy as np
import scipy.ndimage
import scipy.ndimage as ndi

from niftynet.layer.base_layer import RandomisedLayer

Expand All @@ -20,11 +20,13 @@ class RandomSpatialScalingLayer(RandomisedLayer):
def __init__(self,
min_percentage=-10.0,
max_percentage=10.0,
antialiasing=True,
name='random_spatial_scaling'):
super(RandomSpatialScalingLayer, self).__init__(name=name)
assert min_percentage < max_percentage
assert min_percentage <= max_percentage
self._min_percentage = max(min_percentage, -99.9)
self._max_percentage = max_percentage
self.antialiasing = antialiasing
self._rand_zoom = None

def randomise(self, spatial_rank=3):
Expand All @@ -34,27 +36,45 @@ def randomise(self, spatial_rank=3):
size=(spatial_rank,))
self._rand_zoom = (rand_zoom + 100.0) / 100.0

def _get_sigma(self, zoom):
"""
Compute optimal standard deviation for Gaussian kernel.
Cardoso et al., "Scale factor point spread function matching:
beyond aliasing in image resampling", MICCAI 2015
"""
k = 1 / zoom
variance = (k ** 2 - 1 ** 2) * (2 * np.sqrt(2 * np.log(2))) ** (-2)
sigma = np.sqrt(variance)
return sigma

def _apply_transformation(self, image, interp_order=3):
if interp_order < 0:
return image
assert self._rand_zoom is not None
full_zoom = np.array(self._rand_zoom)
while len(full_zoom) < image.ndim:
full_zoom = np.hstack((full_zoom, [1.0]))
is_undersampling = all(full_zoom[:3] < 1)
run_antialiasing_filter = self.antialiasing and is_undersampling
if run_antialiasing_filter:
sigma = self._get_sigma(full_zoom[:3])
if image.ndim == 4:
output = []
for mod in range(image.shape[-1]):
scaled = scipy.ndimage.zoom(image[..., mod],
full_zoom[:3],
order=interp_order)
to_scale = ndi.gaussian_filter(image[..., mod], sigma) if \
run_antialiasing_filter else image[..., mod]
scaled = ndi.zoom(to_scale, full_zoom[:3], order=interp_order)
output.append(scaled[..., np.newaxis])
return np.concatenate(output, axis=-1)
if image.ndim == 3:
scaled = scipy.ndimage.zoom(image,
full_zoom[:3],
order=interp_order)
elif image.ndim == 3:
to_scale = ndi.gaussian_filter(image, sigma) \
if run_antialiasing_filter else image
scaled = ndi.zoom(
to_scale, full_zoom[:3], order=interp_order)
return scaled[..., np.newaxis]
raise NotImplementedError('not implemented random scaling')
else:
raise NotImplementedError('not implemented random scaling')

def layer_op(self, inputs, interp_orders, *args, **kwargs):
if inputs is None:
Expand Down
10 changes: 10 additions & 0 deletions niftynet/utilities/user_parameters_default.py
Expand Up @@ -484,6 +484,13 @@ def add_training_args(parser):
type=float_array,
default=())

parser.add_argument(
"--antialiasing",
help="Indicates if antialiasing must be performed "
"when randomly scaling the input images",
type=str2boolean,
default=True)

parser.add_argument(
"--bias_field_range",
help="[Training only] The range of bias field coeffs in [min_coeff, "
Expand Down Expand Up @@ -513,16 +520,19 @@ def add_training_args(parser):
help="Enables elastic deformation",
type=str2boolean,
default=False)

parser.add_argument(
"--num_ctrl_points",
help="Number of control points for the elastic deformation",
type=int,
default=4)

parser.add_argument(
"--deformation_sigma",
help="The standard deviation for elastic deformation.",
type=float,
default=15)

parser.add_argument(
"--proportion_to_deform",
help="What fraction of samples to deform elastically.",
Expand Down

0 comments on commit f337825

Please sign in to comment.