From 81ea7a6e342d805cc02629369870641b4a3166fa Mon Sep 17 00:00:00 2001 From: "Gregory R. Lee" Date: Thu, 13 Aug 2015 22:11:04 -0400 Subject: [PATCH] MAINT: All modes in _shared.interpolation.pxd were changed to be consistent with numpy.pad naming conventions. Specifically 'nearest' was changed to 'edge' and 'mirror' was changed to 'reflect'. All functions with a mode argument that rely on these functions had their inputs changed accordingly. For now there is a deprecation warning if the user supplies mode 'nearest'. Mode 'mirror' never appeared in an official release of skimage and so has no corresponding deprecation warning. --- skimage/_shared/interpolation.pxd | 51 +++++++++++++-------- skimage/_shared/interpolation.pyx | 5 +- skimage/_shared/tests/test_interpolation.py | 22 +++++---- skimage/_shared/utils.py | 11 +++++ skimage/restoration/_denoise.py | 6 ++- skimage/restoration/_denoise_cy.pyx | 6 +-- skimage/transform/_geometric.py | 16 ++++--- skimage/transform/_warps.py | 33 +++++++++---- skimage/transform/_warps_cy.pyx | 13 +++--- skimage/transform/pyramids.py | 8 ++-- 10 files changed, 109 insertions(+), 62 deletions(-) diff --git a/skimage/_shared/interpolation.pxd b/skimage/_shared/interpolation.pxd index da2d6e6b..aa58684d 100644 --- a/skimage/_shared/interpolation.pxd +++ b/skimage/_shared/interpolation.pxd @@ -2,6 +2,20 @@ #cython: boundscheck=False #cython: nonecheck=False #cython: wraparound=False +""" +Note: All edge modes implemented here follow the corresponding numpy.pad +conventions. + +The table below illustrates the behavior for the array [1, 2, 3, 4], if padded +by 4 values on each side: + + pad original pad + constant (with c=0) : 0 0 0 0 | 1 2 3 4 | 0 0 0 0 + wrap : 1 2 3 4 | 1 2 3 4 | 1 2 3 4 + symmetric : 4 3 2 1 | 1 2 3 4 | 4 3 2 1 + edge : 1 1 1 1 | 1 2 3 4 | 4 4 4 4 + reflect : 3 4 3 2 | 1 2 3 4 | 3 2 1 2 +""" from libc.math cimport ceil, floor @@ -24,8 +38,8 @@ cdef inline double nearest_neighbour_interpolation(double* image, Shape of image. r, c : double Position at which to interpolate. - mode : {'C', 'W', 'R', 'N', 'M'} - Wrapping mode. Constant, Wrap, Reflect, Nearest or Mirror. + mode : {'C', 'W', 'S', 'E', 'R'} + Wrapping mode. Constant, Wrap, Symmetric, Edge or Reflect. cval : double Constant value to use for constant mode. @@ -52,8 +66,8 @@ cdef inline double bilinear_interpolation(double* image, Py_ssize_t rows, Shape of image. r, c : double Position at which to interpolate. - mode : {'C', 'W', 'R', 'N', 'M'} - Wrapping mode. Constant, Wrap, Reflect, Nearest or Mirror. + mode : {'C', 'W', 'S', 'E', 'R'} + Wrapping mode. Constant, Wrap, Symmetric, Edge or Reflect. cval : double Constant value to use for constant mode. @@ -119,8 +133,8 @@ cdef inline double biquadratic_interpolation(double* image, Py_ssize_t rows, Shape of image. r, c : double Position at which to interpolate. - mode : {'C', 'W', 'R', 'N', 'M'} - Wrapping mode. Constant, Wrap, Reflect, Nearest or Mirror. + mode : {'C', 'W', 'S', 'E', 'R'} + Wrapping mode. Constant, Wrap, Symmetric, Edge or Reflect. cval : double Constant value to use for constant mode. @@ -192,8 +206,8 @@ cdef inline double bicubic_interpolation(double* image, Py_ssize_t rows, Shape of image. r, c : double Position at which to interpolate. - mode : {'C', 'W', 'R', 'N', 'M'} - Wrapping mode. Constant, Wrap, Reflect, Nearest or Mirror. + mode : {'C', 'W', 'S', 'E', 'R'} + Wrapping mode. Constant, Wrap, Symmetric, Edge or Reflect. cval : double Constant value to use for constant mode. @@ -248,8 +262,8 @@ cdef inline double get_pixel2d(double* image, Py_ssize_t rows, Py_ssize_t cols, Shape of image. r, c : int Position at which to get the pixel. - mode : {'C', 'W', 'R', 'N', 'M'} - Wrapping mode. Constant, Wrap, Reflect, Nearest or Mirror. + mode : {'C', 'W', 'S', 'E', 'R'} + Wrapping mode. Constant, Wrap, Symmetric, Edge or Reflect. cval : double Constant value to use for constant mode. @@ -281,8 +295,8 @@ cdef inline double get_pixel3d(double* image, Py_ssize_t rows, Py_ssize_t cols, Shape of image. r, c, d : int Position at which to get the pixel. - mode : {'C', 'W', 'R', 'N', 'M'} - Wrapping mode. Constant, Wrap, Reflect, Nearest or Mirror. + mode : {'C', 'W', 'S', 'E', 'R'} + Wrapping mode. Constant, Wrap, Symmetric, Edge or Reflect. cval : double Constant value to use for constant mode. @@ -312,14 +326,13 @@ cdef inline Py_ssize_t coord_map(Py_ssize_t dim, long coord, char mode) nogil: Maximum coordinate. coord : int Coord provided by user. May be < 0 or > dim. - mode : {'W', 'R', 'N', 'M'} - Whether to wrap, reflect, mirror or use the nearest coordinate if it - falls outside [0, dim). - + mode : {'W', 'S', 'R', 'E'} + Whether to wrap, symmetric reflect, reflect or use the nearest + coordinate if `coord` falls outside [0, dim). """ cdef Py_ssize_t cmax cmax = dim - 1 - if mode == 'R': # reflect + if mode == 'S': # symmetric if coord < 0: coord = -coord - 1 if coord > cmax: @@ -332,12 +345,12 @@ cdef inline Py_ssize_t coord_map(Py_ssize_t dim, long coord, char mode) nogil: return (cmax - ((-coord - 1) % dim)) elif coord > cmax: return (coord % dim) - elif mode == 'N': # nearest + elif mode == 'E': # edge if coord < 0: return 0 elif coord > cmax: return cmax - elif mode == 'M': # mirror + elif mode == 'R': # reflect (mirror) if coord < 0: # How many times times does the coordinate wrap? if (-coord / cmax) % 2 != 0: diff --git a/skimage/_shared/interpolation.pyx b/skimage/_shared/interpolation.pyx index b6470651..f5110480 100644 --- a/skimage/_shared/interpolation.pyx +++ b/skimage/_shared/interpolation.pyx @@ -1,6 +1,7 @@ from interpolation cimport coord_map, get_pixel2d import numpy as np cimport numpy as cnp +from .utils import _mode_deprecations def coord_map_py(Py_ssize_t dim, long coord, mode): @@ -18,7 +19,7 @@ def extend_image(image, pad=10, mode='constant', cval=0): Input image. pad : int, optional The number of pixels to pad around the border - mode : {'constant', 'nearest', 'reflect', 'mirror', 'wrap'}, optional + mode : {'constant', 'edge', 'symmetric', 'reflect', 'wrap'}, optional Points outside the boundaries of the input are filled according to the given mode. cval : float, optional @@ -36,7 +37,7 @@ def extend_image(image, pad=10, mode='constant', cval=0): function is intended only for testing get_pixel2d and demonstrating the coordinate mapping modes implemented in ``coord_map``. """ - + mode = _mode_deprecations(mode) cdef: Py_ssize_t rows = image.shape[0] Py_ssize_t cols = image.shape[1] diff --git a/skimage/_shared/tests/test_interpolation.py b/skimage/_shared/tests/test_interpolation.py index 8022e0e1..61a24ea9 100644 --- a/skimage/_shared/tests/test_interpolation.py +++ b/skimage/_shared/tests/test_interpolation.py @@ -3,21 +3,25 @@ from numpy.testing import assert_array_equal def test_coord_map(): - reflect = [coord_map_py(4, n, 'R') for n in range(-6, 6)] - expected_reflect = [2, 3, 3, 2, 1, 0, 0, 1, 2, 3, 3, 2] - assert_array_equal(reflect, expected_reflect) + symmetric = [coord_map_py(4, n, 'S') for n in range(-6, 6)] + expected_symmetric = [2, 3, 3, 2, 1, 0, 0, 1, 2, 3, 3, 2] + assert_array_equal(symmetric, expected_symmetric) wrap = [coord_map_py(4, n, 'W') for n in range(-6, 6)] expected_wrap = [2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1] assert_array_equal(wrap, expected_wrap) - nearest = [coord_map_py(4, n, 'N') for n in range(-6, 6)] - expected_neareset = [0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3] - assert_array_equal(nearest, expected_neareset) + edge = [coord_map_py(4, n, 'E') for n in range(-6, 6)] + expected_edge = [0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3] + assert_array_equal(edge, expected_edge) - mirror = [coord_map_py(4, n, 'M') for n in range(-6, 6)] - expected_mirror = [0, 1, 2, 3, 2, 1, 0, 1, 2, 3, 2, 1] - assert_array_equal(mirror, expected_mirror) + reflect = [coord_map_py(4, n, 'R') for n in range(-6, 6)] + expected_reflect = [0, 1, 2, 3, 2, 1, 0, 1, 2, 3, 2, 1] + assert_array_equal(reflect, expected_reflect) + + constant = [coord_map_py(4, n, 'C') for n in range(-6, 6)] + expected_constant = [0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0] + assert_array_equal(constant, expected_constant) other = [coord_map_py(4, n, 'undefined') for n in range(-6, 6)] assert_array_equal(other, list(range(-6, 6))) diff --git a/skimage/_shared/utils.py b/skimage/_shared/utils.py index 43fda35d..eadc3015 100644 --- a/skimage/_shared/utils.py +++ b/skimage/_shared/utils.py @@ -163,3 +163,14 @@ def assert_nD(array, ndim, arg_name='image'): ndim = [ndim] if not array.ndim in ndim: raise ValueError(msg % (arg_name, '-or-'.join([str(n) for n in ndim]))) + + +def _mode_deprecations(mode): + """ to be used by functions to update deprecated mode names in + `skimage._shared.interpolation.pyx`.""" + if mode.lower() == 'nearest': + warnings.warn(skimage_deprecation( + "Mode 'nearest' has been renamed 'edge'. Mode 'nearest' will be " + "removed in a future release.")) + mode = 'edge' + return mode diff --git a/skimage/restoration/_denoise.py b/skimage/restoration/_denoise.py index 10de6c68..c44ab1f1 100644 --- a/skimage/restoration/_denoise.py +++ b/skimage/restoration/_denoise.py @@ -2,6 +2,7 @@ import numpy as np from .. import img_as_float from ..restoration._denoise_cy import _denoise_bilateral, _denoise_tv_bregman +from .._shared.utils import _mode_deprecations def denoise_bilateral(image, win_size=5, sigma_range=None, sigma_spatial=1, @@ -37,9 +38,9 @@ def denoise_bilateral(image, win_size=5, sigma_range=None, sigma_spatial=1, bins : int Number of discrete values for gaussian weights of color filtering. A larger value results in improved accuracy. - mode : string + mode : {'constant', 'edge', 'symmetric', 'reflect', 'wrap'} How to handle values outside the image borders. See - `scipy.ndimage.map_coordinates` for detail. + `numpy.pad` for detail. cval : string Used in conjunction with mode 'constant', the value outside the image boundaries. @@ -54,6 +55,7 @@ def denoise_bilateral(image, win_size=5, sigma_range=None, sigma_spatial=1, .. [1] http://users.soe.ucsc.edu/~manduchi/Papers/ICCV98.pdf """ + mode = _mode_deprecations(mode) return _denoise_bilateral(image, win_size, sigma_range, sigma_spatial, bins, mode, cval) diff --git a/skimage/restoration/_denoise_cy.pyx b/skimage/restoration/_denoise_cy.pyx index 7d3a82b9..b679b488 100644 --- a/skimage/restoration/_denoise_cy.pyx +++ b/skimage/restoration/_denoise_cy.pyx @@ -105,9 +105,9 @@ def _denoise_bilateral(image, Py_ssize_t win_size, sigma_range, centres = malloc(dims * sizeof(double)) total_values = malloc(dims * sizeof(double)) - if mode not in ('constant', 'wrap', 'reflect', 'nearest'): - raise ValueError("Invalid mode specified. Please use " - "`constant`, `nearest`, `wrap` or `reflect`.") + if mode not in ('constant', 'wrap', 'symmetric', 'reflect', 'edge'): + raise ValueError("Invalid mode specified. Please use `constant`, " + "`edge`, `wrap`, `symmetric` or `reflect`.") cdef char cmode = ord(mode[0].upper()) for r in range(rows): diff --git a/skimage/transform/_geometric.py b/skimage/transform/_geometric.py index 3915a055..db115c08 100644 --- a/skimage/transform/_geometric.py +++ b/skimage/transform/_geometric.py @@ -5,8 +5,10 @@ import numpy as np from scipy import spatial from scipy import ndimage as ndi -from .._shared.utils import get_bound_method_class, safe_as_int +from .._shared.utils import (get_bound_method_class, safe_as_int, + _mode_deprecations) from ..util import img_as_float + from ._warps_cy import _warp_fast @@ -1128,9 +1130,9 @@ def _clip_warp_output(input_image, output_image, order, mode, cval, clip): order : int, optional The order of the spline interpolation, default is 1. The order has to be in the range 0-5. See `skimage.transform.warp` for detail. - mode : {'constant', 'nearest', 'reflect', 'mirror', 'wrap'}, optional + mode : {'constant', 'edge', 'symmetric', 'reflect', 'wrap'}, optional Points outside the boundaries of the input are filled according - to the given mode. + to the given mode. Modes match the behaviour of `numpy.pad`. cval : float, optional Used in conjunction with mode 'constant', the value outside the image boundaries. @@ -1140,7 +1142,7 @@ def _clip_warp_output(input_image, output_image, order, mode, cval, clip): produce values outside the given input range. """ - + mode = _mode_deprecations(mode) if clip and order != 0: min_val = input_image.min() max_val = input_image.max() @@ -1211,9 +1213,9 @@ def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1, - 3: Bi-cubic - 4: Bi-quartic - 5: Bi-quintic - mode : {'constant', 'nearest', 'reflect', 'mirror', 'wrap'}, optional + mode : {'constant', 'edge', 'symmetric', 'reflect', 'wrap'}, optional Points outside the boundaries of the input are filled according - to the given mode. + to the given mode. Modes match the behaviour of `numpy.pad`. cval : float, optional Used in conjunction with mode 'constant', the value outside the image boundaries. @@ -1294,7 +1296,7 @@ def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1, >>> warped = warp(cube, coords) """ - + mode = _mode_deprecations(mode) image = _convert_warp_input(image, preserve_range) input_shape = np.array(image.shape) diff --git a/skimage/transform/_warps.py b/skimage/transform/_warps.py index 8f968777..02d66d91 100644 --- a/skimage/transform/_warps.py +++ b/skimage/transform/_warps.py @@ -4,6 +4,18 @@ from scipy import ndimage as ndi from ..measure import block_reduce from ._geometric import (warp, SimilarityTransform, AffineTransform, _convert_warp_input, _clip_warp_output) +from .._shared.utils import _mode_deprecations + + +def _to_ndimage_mode(mode): + """ Convert from a numpy.pad mode name to the corresponding ndimage + mode. """ + mode = _mode_deprecations(mode.lower()) + mode_translation_dict = dict(edge='nearest', symmetric='reflect', + reflect='mirror') + if mode in mode_translation_dict: + mode = mode_translation_dict[mode] + return mode def resize(image, output_shape, order=1, mode='constant', cval=0, clip=True, @@ -35,9 +47,9 @@ def resize(image, output_shape, order=1, mode='constant', cval=0, clip=True, order : int, optional The order of the spline interpolation, default is 1. The order has to be in the range 0-5. See `skimage.transform.warp` for detail. - mode : {'constant', 'nearest', 'reflect', 'mirror', 'wrap'}, optional + mode : {'constant', 'edge', 'symmetric', 'reflect', 'wrap'}, optional Points outside the boundaries of the input are filled according - to the given mode. + to the given mode. Modes match the behaviour of `numpy.pad`. cval : float, optional Used in conjunction with mode 'constant', the value outside the image boundaries. @@ -51,10 +63,10 @@ def resize(image, output_shape, order=1, mode='constant', cval=0, clip=True, Note ---- - Modes 'mirror' and 'reflect' are similar, but differ in whether the edge + Modes 'reflect' and 'symmetric' are similar, but differ in whether the edge voxels are duplicated during the reflection. As an example, if an array has values [0, 1, 2] and was padded to the right by four values using - reflect, the result would be [0, 1, 2, 2, 1, 0, 0], while for mirror it + symmetric, the result would be [0, 1, 2, 2, 1, 0, 0], while for reflect it would be [0, 1, 2, 1, 0, 1, 2]. Examples @@ -76,6 +88,7 @@ def resize(image, output_shape, order=1, mode='constant', cval=0, clip=True, # 3-dimensional interpolation if len(output_shape) == 3 and (image.ndim == 2 or output_shape[2] != image.shape[2]): + mode = _to_ndimage_mode(mode) dim = output_shape[2] if image.ndim == 2: image = image[:, :, np.newaxis] @@ -146,9 +159,9 @@ def rescale(image, scale, order=1, mode='constant', cval=0, clip=True, order : int, optional The order of the spline interpolation, default is 1. The order has to be in the range 0-5. See `skimage.transform.warp` for detail. - mode : {'constant', 'nearest', 'reflect', 'mirror', 'wrap'}, optional + mode : {'constant', 'edge', 'symmetric', 'reflect', 'wrap'}, optional Points outside the boundaries of the input are filled according - to the given mode. + to the given mode. Modes match the behaviour of `numpy.pad`. cval : float, optional Used in conjunction with mode 'constant', the value outside the image boundaries. @@ -214,9 +227,9 @@ def rotate(image, angle, resize=False, center=None, order=1, mode='constant', order : int, optional The order of the spline interpolation, default is 1. The order has to be in the range 0-5. See `skimage.transform.warp` for detail. - mode : {'constant', 'nearest', 'reflect', 'mirror', 'wrap'}, optional + mode : {'constant', 'edge', 'symmetric', 'reflect', 'wrap'}, optional Points outside the boundaries of the input are filled according - to the given mode. + to the given mode. Modes match the behaviour of `numpy.pad`. cval : float, optional Used in conjunction with mode 'constant', the value outside the image boundaries. @@ -368,9 +381,9 @@ def swirl(image, center=None, strength=1, radius=100, rotation=0, order : int, optional The order of the spline interpolation, default is 1. The order has to be in the range 0-5. See `skimage.transform.warp` for detail. - mode : {'constant', 'nearest', 'reflect', 'mirror', 'wrap'}, optional + mode : {'constant', 'edge', 'symmetric', 'reflect', 'wrap'}, optional Points outside the boundaries of the input are filled according - to the given mode. + to the given mode. Modes match the behaviour of `numpy.pad`. cval : float, optional Used in conjunction with mode 'constant', the value outside the image boundaries. diff --git a/skimage/transform/_warps_cy.pyx b/skimage/transform/_warps_cy.pyx index caab79fd..befc1e89 100644 --- a/skimage/transform/_warps_cy.pyx +++ b/skimage/transform/_warps_cy.pyx @@ -70,18 +70,19 @@ def _warp_fast(cnp.ndarray image, cnp.ndarray H, output_shape=None, * 1: Bi-linear (default) * 2: Bi-quadratic * 3: Bi-cubic - mode : {'constant', 'reflect', 'mirror', 'wrap', 'nearest'}, optional - How to handle values outside the image borders (default is constant). + mode : {'constant', 'edge', 'symmetric', 'reflect', 'wrap'}, optional + Points outside the boundaries of the input are filled according + to the given mode. Modes match the behaviour of `numpy.pad`. cval : string, optional (default 0) Used in conjunction with mode 'C' (constant), the value outside the image boundaries. Note ---- - Modes 'mirror' and 'reflect' are similar, but differ in whether the edge + Modes 'reflect' and 'symmetric' are similar, but differ in whether the edge voxels are duplicated during the reflection. As an example, if an array has values [0, 1, 2] and was padded to the right by four values using - reflect, the result would be [0, 1, 2, 2, 1, 0, 0], while for mirror it + symmetric, the result would be [0, 1, 2, 2, 1, 0, 0], while for reflect it would be [0, 1, 2, 1, 0, 1, 2]. """ @@ -89,9 +90,9 @@ def _warp_fast(cnp.ndarray image, cnp.ndarray H, output_shape=None, cdef double[:, ::1] img = np.ascontiguousarray(image, dtype=np.double) cdef double[:, ::1] M = np.ascontiguousarray(H) - if mode not in ('constant', 'wrap', 'reflect', 'mirror', 'nearest'): + if mode not in ('constant', 'wrap', 'symmetric', 'reflect', 'edge'): raise ValueError("Invalid mode specified. Please use `constant`, " - "`nearest`, `wrap`, `mirror` or `reflect`.") + "`edge`, `wrap`, `reflect` or `symmetric`.") cdef char mode_c = ord(mode[0].upper()) cdef Py_ssize_t out_r, out_c diff --git a/skimage/transform/pyramids.py b/skimage/transform/pyramids.py index fe2d26d1..958e9d65 100644 --- a/skimage/transform/pyramids.py +++ b/skimage/transform/pyramids.py @@ -45,7 +45,7 @@ def pyramid_reduce(image, downscale=2, sigma=None, order=1, order : int, optional Order of splines used in interpolation of downsampling. See `skimage.transform.warp` for detail. - mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional + mode : {'reflect', 'constant', 'edge', 'symmetric', 'wrap'}, optional The mode parameter determines how the array borders are handled, where cval is the value when mode is equal to 'constant'. cval : float, optional @@ -99,7 +99,7 @@ def pyramid_expand(image, upscale=2, sigma=None, order=1, order : int, optional Order of splines used in interpolation of upsampling. See `skimage.transform.warp` for detail. - mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional + mode : {'reflect', 'constant', 'edge', 'symmetric', 'wrap'}, optional The mode parameter determines how the array borders are handled, where cval is the value when mode is equal to 'constant'. cval : float, optional @@ -164,7 +164,7 @@ def pyramid_gaussian(image, max_layer=-1, downscale=2, sigma=None, order=1, order : int, optional Order of splines used in interpolation of downsampling. See `skimage.transform.warp` for detail. - mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional + mode : {'reflect', 'constant', 'edge', 'symmetric', 'wrap'}, optional The mode parameter determines how the array borders are handled, where cval is the value when mode is equal to 'constant'. cval : float, optional @@ -245,7 +245,7 @@ def pyramid_laplacian(image, max_layer=-1, downscale=2, sigma=None, order=1, order : int, optional Order of splines used in interpolation of downsampling. See `skimage.transform.warp` for detail. - mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional + mode : {'reflect', 'constant', 'edge', 'symmetric', 'wrap'}, optional The mode parameter determines how the array borders are handled, where cval is the value when mode is equal to 'constant'. cval : float, optional