From b551a49708caf0fd30e057d2c2aeaca4f87aeda0 Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Sat, 20 Apr 2013 05:00:40 +0530 Subject: [PATCH 01/14] First implementation of Integer Up/Downsampling --- skimage/transform/rescale.py | 51 ++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 skimage/transform/rescale.py diff --git a/skimage/transform/rescale.py b/skimage/transform/rescale.py new file mode 100644 index 00000000..69e004a9 --- /dev/null +++ b/skimage/transform/rescale.py @@ -0,0 +1,51 @@ +# TODO : Doc, Tests, PEP8 check + +import numpy as np + +def downsample(image, factors, how = 'sum'): + + is0 = image.shape[0] + is1 = image.shape[1] + f0 = factors[0] + f1 = factors[1] + + if (f0 - int(f0) != 0) or (f1 - int(f1) != 0): + return "Use resample for non-integer downsampling" + cropped = image[: is0 - (is0 % f0), : is1 - (is1 % f1)] + out = np.zeros((cropped.shape[0] / f0, cropped.shape[1] / f1)) + + if how == 'sum': + for i in range(cropped.shape[0]): + for j in range(cropped.shape[1]): + out[int(i / f0)][int(j / f1)] += cropped[i][j] + return out + + if how == 'mean': + for i in range(cropped.shape[0]): + for j in range(cropped.shape[1]): + out[int(i / f0)][int(j / f1)] += cropped[i][j] / float(f0 * f1) + return out + + +def upsample(image, factors, how = 'divide'): + + is0 = image.shape[0] + is1 = image.shape[1] + f0 = factors[0] + f1 = factors[1] + + if (f0 - int(f0) != 0) or (f1 - int(f1) != 0): + return "Use resample for non-integer upsampling" + out = np.zeros((f0 * image.shape[0], f1 * image.shape[1])) + + if how == 'divide': + for i in range(out.shape[0]): + for j in range(out.shape[1]): + out[i][j] = (image[i / f0][j / f1]) / float(f0 * f1) + return out + + if how == 'uniform': + for i in range(out.shape[0]): + for j in range(out.shape[1]): + out[i][j] = (image[i / f0][j / f1]) + return out From 19f0be9fbd4fab690546f23cd7a893d62e34b5d8 Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Sat, 20 Apr 2013 18:19:28 +0530 Subject: [PATCH 02/14] PEP8 corrections --- skimage/transform/rescale.py | 76 ++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/skimage/transform/rescale.py b/skimage/transform/rescale.py index 69e004a9..4ec4ab99 100644 --- a/skimage/transform/rescale.py +++ b/skimage/transform/rescale.py @@ -2,50 +2,52 @@ import numpy as np -def downsample(image, factors, how = 'sum'): +def downsample(image, factors, method='sum'): - is0 = image.shape[0] - is1 = image.shape[1] - f0 = factors[0] - f1 = factors[1] + is0 = image.shape[0] + is1 = image.shape[1] + f0 = factors[0] + f1 = factors[1] - if (f0 - int(f0) != 0) or (f1 - int(f1) != 0): - return "Use resample for non-integer downsampling" - cropped = image[: is0 - (is0 % f0), : is1 - (is1 % f1)] - out = np.zeros((cropped.shape[0] / f0, cropped.shape[1] / f1)) + if (f0 - int(f0) != 0) or (f1 - int(f1) != 0): + print "Use resample for non-integer downsampling" + return + cropped = image[: is0 - (is0 % f0), : is1 - (is1 % f1)] + out = np.zeros((cropped.shape[0] / f0, cropped.shape[1] / f1)) - if how == 'sum': - for i in range(cropped.shape[0]): - for j in range(cropped.shape[1]): - out[int(i / f0)][int(j / f1)] += cropped[i][j] - return out + if method == 'sum': + for i in range(cropped.shape[0]): + for j in range(cropped.shape[1]): + out[int(i / f0)][int(j / f1)] += cropped[i][j] + return out - if how == 'mean': - for i in range(cropped.shape[0]): - for j in range(cropped.shape[1]): - out[int(i / f0)][int(j / f1)] += cropped[i][j] / float(f0 * f1) - return out + if method == 'mean': + for i in range(cropped.shape[0]): + for j in range(cropped.shape[1]): + out[int(i / f0)][int(j / f1)] += cropped[i][j] + return out / float(f0 * f1) -def upsample(image, factors, how = 'divide'): +def upsample(image, factors, method='divide'): - is0 = image.shape[0] - is1 = image.shape[1] - f0 = factors[0] - f1 = factors[1] + is0 = image.shape[0] + is1 = image.shape[1] + f0 = factors[0] + f1 = factors[1] - if (f0 - int(f0) != 0) or (f1 - int(f1) != 0): - return "Use resample for non-integer upsampling" - out = np.zeros((f0 * image.shape[0], f1 * image.shape[1])) + if (f0 - int(f0) != 0) or (f1 - int(f1) != 0): + print "Use resample for non-integer upsampling" + return + out = np.zeros((f0 * image.shape[0], f1 * image.shape[1])) - if how == 'divide': - for i in range(out.shape[0]): - for j in range(out.shape[1]): - out[i][j] = (image[i / f0][j / f1]) / float(f0 * f1) - return out + if method == 'divide': + for i in range(out.shape[0]): + for j in range(out.shape[1]): + out[i][j] = (image[i / f0][j / f1]) + return out / float(f0 * f1) - if how == 'uniform': - for i in range(out.shape[0]): - for j in range(out.shape[1]): - out[i][j] = (image[i / f0][j / f1]) - return out + if method == 'uniform': + for i in range(out.shape[0]): + for j in range(out.shape[1]): + out[i][j] = (image[i / f0][j / f1]) + return out From 8372b0b6aa5a48c38002cf73bdc32cdc9e7273a6 Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Mon, 29 Apr 2013 23:24:07 +0530 Subject: [PATCH 03/14] Removing code repetition --- skimage/transform/rescale.py | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/skimage/transform/rescale.py b/skimage/transform/rescale.py index 4ec4ab99..b7164994 100644 --- a/skimage/transform/rescale.py +++ b/skimage/transform/rescale.py @@ -10,21 +10,17 @@ def downsample(image, factors, method='sum'): f1 = factors[1] if (f0 - int(f0) != 0) or (f1 - int(f1) != 0): - print "Use resample for non-integer downsampling" + print "Use resample() for non-integer downsampling" return - cropped = image[: is0 - (is0 % f0), : is1 - (is1 % f1)] + cropped = image[:is0 - (is0 % f0), :is1 - (is1 % f1)] out = np.zeros((cropped.shape[0] / f0, cropped.shape[1] / f1)) + for i in range(cropped.shape[0]): + for j in range(cropped.shape[1]): + out[int(i / f0)][int(j / f1)] += cropped[i][j] if method == 'sum': - for i in range(cropped.shape[0]): - for j in range(cropped.shape[1]): - out[int(i / f0)][int(j / f1)] += cropped[i][j] return out - - if method == 'mean': - for i in range(cropped.shape[0]): - for j in range(cropped.shape[1]): - out[int(i / f0)][int(j / f1)] += cropped[i][j] + else: return out / float(f0 * f1) @@ -36,18 +32,15 @@ def upsample(image, factors, method='divide'): f1 = factors[1] if (f0 - int(f0) != 0) or (f1 - int(f1) != 0): - print "Use resample for non-integer upsampling" + print "Use resample() for non-integer upsampling" return out = np.zeros((f0 * image.shape[0], f1 * image.shape[1])) - if method == 'divide': - for i in range(out.shape[0]): - for j in range(out.shape[1]): - out[i][j] = (image[i / f0][j / f1]) - return out / float(f0 * f1) - if method == 'uniform': - for i in range(out.shape[0]): - for j in range(out.shape[1]): - out[i][j] = (image[i / f0][j / f1]) + for i in range(out.shape[0]): + for j in range(out.shape[1]): + out[i][j] = (image[i / f0][j / f1]) + if method == 'divide': + return out / float(f0 * f1) + else: return out From b5804214b02a4f68a201263d955bf56e7f05fd02 Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Tue, 30 Apr 2013 19:49:44 +0530 Subject: [PATCH 04/14] Minor refactoring --- skimage/transform/rescale.py | 37 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/skimage/transform/rescale.py b/skimage/transform/rescale.py index b7164994..e974bc8f 100644 --- a/skimage/transform/rescale.py +++ b/skimage/transform/rescale.py @@ -4,43 +4,36 @@ import numpy as np def downsample(image, factors, method='sum'): - is0 = image.shape[0] - is1 = image.shape[1] - f0 = factors[0] - f1 = factors[1] + is = image.shape + f = factors - if (f0 - int(f0) != 0) or (f1 - int(f1) != 0): - print "Use resample() for non-integer downsampling" - return - cropped = image[:is0 - (is0 % f0), :is1 - (is1 % f1)] - out = np.zeros((cropped.shape[0] / f0, cropped.shape[1] / f1)) + if (f[0] - int(f[0]) != 0) or (f[1] - int(f[1]) != 0): + raise ValueError('Use resample() for non-integer downsampling') + cropped = image[:is[0] - (is[0] % f[0]), :is[1] - (is[1] % f[1])] + out = np.zeros((cropped.shape[0] / f[0], cropped.shape[1] / f[1])) for i in range(cropped.shape[0]): for j in range(cropped.shape[1]): - out[int(i / f0)][int(j / f1)] += cropped[i][j] + out[int(i / f[0])][int(j / f[1])] += cropped[i][j] if method == 'sum': return out else: - return out / float(f0 * f1) + return out / float(f[0] * f[1]) def upsample(image, factors, method='divide'): - is0 = image.shape[0] - is1 = image.shape[1] - f0 = factors[0] - f1 = factors[1] - - if (f0 - int(f0) != 0) or (f1 - int(f1) != 0): - print "Use resample() for non-integer upsampling" - return - out = np.zeros((f0 * image.shape[0], f1 * image.shape[1])) + is = image.shape + f = factors + if (f[0] - int(f[0]) != 0) or (f[1] - int(f[1]) != 0): + raise ValueError('Use resample() for non-integer upsampling') + out = np.zeros((f[0] * image.shape[0], f[1] * image.shape[1])) for i in range(out.shape[0]): for j in range(out.shape[1]): - out[i][j] = (image[i / f0][j / f1]) + out[i][j] = (image[i / f[0]][j / f[1]]) if method == 'divide': - return out / float(f0 * f1) + return out / float(f[0] * f[1]) else: return out From bfc2aac3e5771bb669aa7abb5dc510e971783dd2 Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Tue, 30 Apr 2013 21:40:16 +0530 Subject: [PATCH 05/14] Downsampling of nD arrays --- skimage/transform/rescale.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/skimage/transform/rescale.py b/skimage/transform/rescale.py index e974bc8f..c7d77ee7 100644 --- a/skimage/transform/rescale.py +++ b/skimage/transform/rescale.py @@ -1,29 +1,24 @@ # TODO : Doc, Tests, PEP8 check import numpy as np +from skimage.util.shape import view_as_blocks def downsample(image, factors, method='sum'): - is = image.shape - f = factors + # works only if image.shape is perfectly divisible by factors + out = view_as_blocks(image, factors) + block_shape = out.shape - if (f[0] - int(f[0]) != 0) or (f[1] - int(f[1]) != 0): - raise ValueError('Use resample() for non-integer downsampling') - cropped = image[:is[0] - (is[0] % f[0]), :is[1] - (is[1] % f[1])] - out = np.zeros((cropped.shape[0] / f[0], cropped.shape[1] / f[1])) - - for i in range(cropped.shape[0]): - for j in range(cropped.shape[1]): - out[int(i / f[0])][int(j / f[1])] += cropped[i][j] if method == 'sum': - return out + for i in range(len(block_shape)/2): + out = out.sum(-1) else: - return out / float(f[0] * f[1]) - + for i in range(len(block_shape)/2): + out = out.mean(-1) + return out def upsample(image, factors, method='divide'): - is = image.shape f = factors if (f[0] - int(f[0]) != 0) or (f[1] - int(f[1]) != 0): From 329cf37ca2e25679e1b8ceb1032e51a6874b2d8f Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Wed, 1 May 2013 11:59:02 +0530 Subject: [PATCH 06/14] Padding ndarray with zeros to support downsampling by any integer factor --- skimage/transform/rescale.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/skimage/transform/rescale.py b/skimage/transform/rescale.py index c7d77ee7..653bb222 100644 --- a/skimage/transform/rescale.py +++ b/skimage/transform/rescale.py @@ -3,9 +3,35 @@ import numpy as np from skimage.util.shape import view_as_blocks + +def _pad_asymmetric_zeros(arr, pad_amt, axis=-1): + """Pads `arr` by `pad_amt` along specified `axis`""" + if axis == -1: + axis = arr.ndim - 1 + + zeroshape = tuple([x if i != axis else pad_amt + for (i, x) in enumerate(arr.shape)]) + + return np.concatenate((arr, np.zeros(zeroshape, dtype=arr.dtype)), + axis=axis) + + def downsample(image, factors, method='sum'): - # works only if image.shape is perfectly divisible by factors + pad_size = [] + if len(factors) != image.ndim: + raise ValueError("'factors' must have the same length " + "as 'image.shape'") + else: + for i in range(len(factors)): + if image.shape[i] % factors[i] != 0: + pad_size.append(factors[i] - (image.shape[i] % factors[i])) + else: + pad_size.append(0) + + for i in range(len(pad_size)): + image = _pad_asymmetric_zeros(image, pad_size[i], i) + out = view_as_blocks(image, factors) block_shape = out.shape From 7282f561d43312174fdf6b2c82c514e8e87a93ed Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Mon, 6 May 2013 23:45:01 +0530 Subject: [PATCH 07/14] Added docs, tests for downsample() in skimage.transform._warps --- skimage/transform/__init__.py | 3 +- skimage/transform/_warps.py | 61 +++++++++++++++++++++++++++ skimage/transform/rescale.py | 60 -------------------------- skimage/transform/tests/test_warps.py | 29 ++++++++++++- skimage/util/shape.py | 12 ++++++ 5 files changed, 103 insertions(+), 62 deletions(-) delete mode 100644 skimage/transform/rescale.py diff --git a/skimage/transform/__init__.py b/skimage/transform/__init__.py index 859c7515..311801be 100644 --- a/skimage/transform/__init__.py +++ b/skimage/transform/__init__.py @@ -9,7 +9,7 @@ from ._geometric import (warp, warp_coords, estimate_transform, SimilarityTransform, AffineTransform, ProjectiveTransform, PolynomialTransform, PiecewiseAffineTransform) -from ._warps import swirl, resize, rotate, rescale +from ._warps import swirl, resize, rotate, rescale, downscale_local_means from .pyramids import (pyramid_reduce, pyramid_expand, pyramid_gaussian, pyramid_laplacian) @@ -39,6 +39,7 @@ __all__ = ['hough_circle', 'resize', 'rotate', 'rescale', + 'downscale_local_means', 'pyramid_reduce', 'pyramid_expand', 'pyramid_gaussian', diff --git a/skimage/transform/_warps.py b/skimage/transform/_warps.py index caf2baaf..513c8f01 100644 --- a/skimage/transform/_warps.py +++ b/skimage/transform/_warps.py @@ -1,6 +1,8 @@ import numpy as np from scipy import ndimage + from ._geometric import warp, SimilarityTransform, AffineTransform +from skimage.util.shape import view_as_blocks, _pad_asymmetric_zeros def resize(image, output_shape, order=1, mode='constant', cval=0.): @@ -283,3 +285,62 @@ def swirl(image, center=None, strength=1, radius=100, rotation=0, return warp(image, _swirl_mapping, map_args=warp_args, output_shape=output_shape, order=order, mode=mode, cval=cval) + + +def downsample(array, factors, mode='sum'): + """Performs downsampling with integer factors. + + Parameters + ---------- + array : ndarray + Input n-dimensional array. + factors: tuple + Tuple containing downsampling factor along each axis. + mode : string + Decides whether the downsampled element is the sum or mean + of its corresponding constituent elements in the input array. Default + is 'sum'. + + Returns + ------- + array : ndarray + Downsampled array with same number of dimensions as that of input + array. + + Example + ------- + >>> a = np.arange(15).reshape(3, 5) + >>> a + array([[ 0, 1, 2, 3, 4], + [ 5, 6, 7, 8, 9], + [10, 11, 12, 13, 14]]) + >>> downsample(a, (2,3)) + array([[21, 24], + [33, 27]]) + + """ + + pad_size = [] + if len(factors) != array.ndim: + raise ValueError("'factors' must have the same length " + "as 'array.shape'") + else: + for i in range(len(factors)): + if array.shape[i] % factors[i] != 0: + pad_size.append(factors[i] - (array.shape[i] % factors[i])) + else: + pad_size.append(0) + + for i in range(len(pad_size)): + array = _pad_asymmetric_zeros(array, pad_size[i], i) + + out = view_as_blocks(array, factors) + block_shape = out.shape + + if mode == 'sum': + for i in range(len(block_shape)/2): + out = out.sum(-1) + else: + for i in range(len(block_shape)/2): + out = out.mean(-1) + return out diff --git a/skimage/transform/rescale.py b/skimage/transform/rescale.py deleted file mode 100644 index 653bb222..00000000 --- a/skimage/transform/rescale.py +++ /dev/null @@ -1,60 +0,0 @@ -# TODO : Doc, Tests, PEP8 check - -import numpy as np -from skimage.util.shape import view_as_blocks - - -def _pad_asymmetric_zeros(arr, pad_amt, axis=-1): - """Pads `arr` by `pad_amt` along specified `axis`""" - if axis == -1: - axis = arr.ndim - 1 - - zeroshape = tuple([x if i != axis else pad_amt - for (i, x) in enumerate(arr.shape)]) - - return np.concatenate((arr, np.zeros(zeroshape, dtype=arr.dtype)), - axis=axis) - - -def downsample(image, factors, method='sum'): - - pad_size = [] - if len(factors) != image.ndim: - raise ValueError("'factors' must have the same length " - "as 'image.shape'") - else: - for i in range(len(factors)): - if image.shape[i] % factors[i] != 0: - pad_size.append(factors[i] - (image.shape[i] % factors[i])) - else: - pad_size.append(0) - - for i in range(len(pad_size)): - image = _pad_asymmetric_zeros(image, pad_size[i], i) - - out = view_as_blocks(image, factors) - block_shape = out.shape - - if method == 'sum': - for i in range(len(block_shape)/2): - out = out.sum(-1) - else: - for i in range(len(block_shape)/2): - out = out.mean(-1) - return out - -def upsample(image, factors, method='divide'): - - f = factors - - if (f[0] - int(f[0]) != 0) or (f[1] - int(f[1]) != 0): - raise ValueError('Use resample() for non-integer upsampling') - out = np.zeros((f[0] * image.shape[0], f[1] * image.shape[1])) - - for i in range(out.shape[0]): - for j in range(out.shape[1]): - out[i][j] = (image[i / f[0]][j / f[1]]) - if method == 'divide': - return out / float(f[0] * f[1]) - else: - return out diff --git a/skimage/transform/tests/test_warps.py b/skimage/transform/tests/test_warps.py index 93f87320..a2cd2b05 100644 --- a/skimage/transform/tests/test_warps.py +++ b/skimage/transform/tests/test_warps.py @@ -1,4 +1,4 @@ -from numpy.testing import assert_array_almost_equal, run_module_suite +from numpy.testing import assert_array_almost_equal, run_module_suite, assert_array_equal import numpy as np from scipy.ndimage import map_coordinates @@ -193,6 +193,33 @@ def test_warp_coords_example(): coords = warp_coords(tform, (30, 30, 3)) map_coordinates(image[:, :, 0], coords[:2]) +def test_downsample_sum(): + """Verifying downsampling of an array with expected result in sum mode""" + image1 = np.arange(4*6).reshape(4, 6) + out1 = tf.downsample(image1, (2, 3)) + expected1 = np.array([[ 24, 42], + [ 96, 114]]) + assert_array_equal(expected1, out1) + image2 = np.arange(5*8).reshape(5, 8) + out2 = tf.downsample(image2, (3, 3)) + expected2 = np.array([[ 81, 108, 87], + [174, 192, 138]]) + assert_array_equal(expected2, out2) + + +def test_downsample_mean(): + """Verifying downsampling of an array with expected result in mean mode""" + image1 = np.arange(4*6).reshape(4, 6) + out1 = tf.downsample(image1, (2, 3), 'mean') + expected1 = np.array([[ 4., 7.], + [ 16., 19.]]) + assert_array_equal(expected1, out1) + image2 = np.arange(5*8).reshape(5, 8) + out2 = tf.downsample(image2, (4, 5), 'mean') + expected2 = np.array([[ 14. , 10.8], + [ 8.5, 5.7]]) + assert_array_equal(expected2, out2) + if __name__ == "__main__": run_module_suite() diff --git a/skimage/util/shape.py b/skimage/util/shape.py index 0126d2e3..2763d3d4 100644 --- a/skimage/util/shape.py +++ b/skimage/util/shape.py @@ -230,3 +230,15 @@ def view_as_windows(arr_in, window_shape): arr_out = as_strided(arr_in, shape=new_shape, strides=new_strides) return arr_out + + +def _pad_asymmetric_zeros(arr, pad_amt, axis=-1): + """Pads `arr` with zeros by `pad_amt` along specified `axis`""" + if axis == -1: + axis = arr.ndim - 1 + + zeroshape = tuple([x if i != axis else pad_amt + for (i, x) in enumerate(arr.shape)]) + + return np.concatenate((arr, np.zeros(zeroshape, dtype=arr.dtype)), + axis=axis) From 2f817542b86ec3247d2728b6d829a813ba98619d Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Tue, 7 May 2013 00:40:03 +0530 Subject: [PATCH 08/14] Making code compatible with Python 3 --- skimage/transform/_warps.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skimage/transform/_warps.py b/skimage/transform/_warps.py index 513c8f01..2fcfd80d 100644 --- a/skimage/transform/_warps.py +++ b/skimage/transform/_warps.py @@ -338,9 +338,9 @@ def downsample(array, factors, mode='sum'): block_shape = out.shape if mode == 'sum': - for i in range(len(block_shape)/2): + for i in range(len(block_shape)//2): out = out.sum(-1) else: - for i in range(len(block_shape)/2): + for i in range(len(block_shape)//2): out = out.mean(-1) return out From c57c86519672f5c94c89f9c56126980b871d4649 Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Mon, 27 May 2013 07:48:57 +0800 Subject: [PATCH 09/14] Add _sum_blocks --- skimage/transform/__init__.py | 1 + skimage/transform/_warps.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/skimage/transform/__init__.py b/skimage/transform/__init__.py index 311801be..dcd39c31 100644 --- a/skimage/transform/__init__.py +++ b/skimage/transform/__init__.py @@ -9,6 +9,7 @@ from ._geometric import (warp, warp_coords, estimate_transform, SimilarityTransform, AffineTransform, ProjectiveTransform, PolynomialTransform, PiecewiseAffineTransform) + from ._warps import swirl, resize, rotate, rescale, downscale_local_means from .pyramids import (pyramid_reduce, pyramid_expand, pyramid_gaussian, pyramid_laplacian) diff --git a/skimage/transform/_warps.py b/skimage/transform/_warps.py index 2fcfd80d..640a451d 100644 --- a/skimage/transform/_warps.py +++ b/skimage/transform/_warps.py @@ -287,7 +287,7 @@ def swirl(image, center=None, strength=1, radius=100, rotation=0, order=order, mode=mode, cval=cval) -def downsample(array, factors, mode='sum'): +def _downsample(array, factors, mode='sum'): """Performs downsampling with integer factors. Parameters @@ -338,9 +338,9 @@ def downsample(array, factors, mode='sum'): block_shape = out.shape if mode == 'sum': - for i in range(len(block_shape)//2): + for i in range(len(block_shape) // 2): out = out.sum(-1) else: - for i in range(len(block_shape)//2): + for i in range(len(block_shape) // 2): out = out.mean(-1) return out From 6f9d7c5d1af916f6d4d96d6e8debe891ccfdbe83 Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Thu, 4 Jul 2013 16:56:37 +0800 Subject: [PATCH 10/14] Cleaning up downsampling for integer factors --- skimage/measure/__init__.py | 4 +- skimage/measure/_sum_blocks.py | 34 ++++++++++++++ skimage/measure/tests/test_sum_blocks.py | 16 +++++++ skimage/transform/__init__.py | 1 - skimage/transform/_warps.py | 56 +++++++++++++++++------- skimage/transform/tests/test_warps.py | 22 +++------- 6 files changed, 97 insertions(+), 36 deletions(-) create mode 100644 skimage/measure/_sum_blocks.py create mode 100644 skimage/measure/tests/test_sum_blocks.py diff --git a/skimage/measure/__init__.py b/skimage/measure/__init__.py index 89f707c1..a9fbae24 100755 --- a/skimage/measure/__init__.py +++ b/skimage/measure/__init__.py @@ -3,6 +3,7 @@ from ._regionprops import regionprops, perimeter from ._structural_similarity import structural_similarity from ._polygon import approximate_polygon, subdivide_polygon from .fit import LineModel, CircleModel, EllipseModel, ransac +from ._sum_blocks import sum_blocks __all__ = ['find_contours', @@ -14,4 +15,5 @@ __all__ = ['find_contours', 'LineModel', 'CircleModel', 'EllipseModel', - 'ransac'] + 'ransac', + 'sum_blocks'] diff --git a/skimage/measure/_sum_blocks.py b/skimage/measure/_sum_blocks.py new file mode 100644 index 00000000..e6a478e5 --- /dev/null +++ b/skimage/measure/_sum_blocks.py @@ -0,0 +1,34 @@ +from ..transform._warps import _downsample + + +def sum_blocks(array, factors): + """Sums the elements in blocks of integer factors and pads the original + array with zeroes if the dimensions are not perfectly divisible by factors. + + Parameters + ---------- + array : ndarray + Input n-dimensional array. + factors: tuple + Tuple containing integer values representing block length along each + axis. + + Returns + ------- + array : ndarray + Downsampled array with same number of dimensions as that of input + array. + + Example + ------- + >>> a = np.arange(15).reshape(3, 5) + >>> a + array([[ 0, 1, 2, 3, 4], + [ 5, 6, 7, 8, 9], + [10, 11, 12, 13, 14]]) + >>> sum_blocks(a, (2,3)) + array([[21, 24], + [33, 27]]) + + """ + return _downsample(array, factors) diff --git a/skimage/measure/tests/test_sum_blocks.py b/skimage/measure/tests/test_sum_blocks.py new file mode 100644 index 00000000..b4910495 --- /dev/null +++ b/skimage/measure/tests/test_sum_blocks.py @@ -0,0 +1,16 @@ +import numpy as np +from numpy.testing import assert_array_equal +from skimage.measure._sum_blocks import sum_blocks + +def test_downsample_sum_blocks(): + """Verifying downsampling of an array with expected result in sum mode""" + image1 = np.arange(4*6).reshape(4, 6) + out1 = sum_blocks(image1, (2, 3)) + expected1 = np.array([[ 24, 42], + [ 96, 114]]) + assert_array_equal(expected1, out1) + image2 = np.arange(5*8).reshape(5, 8) + out2 = sum_blocks(image2, (3, 3)) + expected2 = np.array([[ 81, 108, 87], + [174, 192, 138]]) + assert_array_equal(expected2, out2) diff --git a/skimage/transform/__init__.py b/skimage/transform/__init__.py index dcd39c31..311801be 100644 --- a/skimage/transform/__init__.py +++ b/skimage/transform/__init__.py @@ -9,7 +9,6 @@ from ._geometric import (warp, warp_coords, estimate_transform, SimilarityTransform, AffineTransform, ProjectiveTransform, PolynomialTransform, PiecewiseAffineTransform) - from ._warps import swirl, resize, rotate, rescale, downscale_local_means from .pyramids import (pyramid_reduce, pyramid_expand, pyramid_gaussian, pyramid_laplacian) diff --git a/skimage/transform/_warps.py b/skimage/transform/_warps.py index 640a451d..cca5d1d9 100644 --- a/skimage/transform/_warps.py +++ b/skimage/transform/_warps.py @@ -287,7 +287,7 @@ def swirl(image, center=None, strength=1, radius=100, rotation=0, order=order, mode=mode, cval=cval) -def _downsample(array, factors, mode='sum'): +def _downsample(array, factors, sum=True): """Performs downsampling with integer factors. Parameters @@ -296,10 +296,9 @@ def _downsample(array, factors, mode='sum'): Input n-dimensional array. factors: tuple Tuple containing downsampling factor along each axis. - mode : string - Decides whether the downsampled element is the sum or mean - of its corresponding constituent elements in the input array. Default - is 'sum'. + sum : bool + If True, downsampled element is the sum of its corresponding + constituent elements in the input array. Default is True. Returns ------- @@ -307,17 +306,6 @@ def _downsample(array, factors, mode='sum'): Downsampled array with same number of dimensions as that of input array. - Example - ------- - >>> a = np.arange(15).reshape(3, 5) - >>> a - array([[ 0, 1, 2, 3, 4], - [ 5, 6, 7, 8, 9], - [10, 11, 12, 13, 14]]) - >>> downsample(a, (2,3)) - array([[21, 24], - [33, 27]]) - """ pad_size = [] @@ -337,10 +325,44 @@ def _downsample(array, factors, mode='sum'): out = view_as_blocks(array, factors) block_shape = out.shape - if mode == 'sum': + if sum: for i in range(len(block_shape) // 2): out = out.sum(-1) else: for i in range(len(block_shape) // 2): out = out.mean(-1) return out + + +def downscale_local_means(array, factors): + """Downsamples the array in blocks of input integer factors after padding + the original array with zeroes if the dimensions are not perfectly + divisible by factors and replaces it with mean i.e. average value. + + Parameters + ---------- + array : ndarray + Input n-dimensional array. + factors: tuple + Tuple containing integer values representing block length along each + axis. + + Returns + ------- + array : ndarray + Downsampled array with same number of dimensions as that of input + array. + + Example + ------- + >>> a = np.arange(15).reshape(3, 5) + >>> a + array([[ 0, 1, 2, 3, 4], + [ 5, 6, 7, 8, 9], + [10, 11, 12, 13, 14]]) + >>> downscale_local_means(a, (2,3)) + array([[3.5, 4.], + [5.5, 4.5]]) + + """ + return _downsample(array, factors, False) diff --git a/skimage/transform/tests/test_warps.py b/skimage/transform/tests/test_warps.py index a2cd2b05..88ede530 100644 --- a/skimage/transform/tests/test_warps.py +++ b/skimage/transform/tests/test_warps.py @@ -5,7 +5,8 @@ from scipy.ndimage import map_coordinates from skimage.transform import (warp, warp_coords, rotate, resize, rescale, AffineTransform, ProjectiveTransform, - SimilarityTransform) + SimilarityTransform, + downscale_local_means) from skimage import transform as tf, data, img_as_float from skimage.color import rgb2gray @@ -193,29 +194,16 @@ def test_warp_coords_example(): coords = warp_coords(tform, (30, 30, 3)) map_coordinates(image[:, :, 0], coords[:2]) -def test_downsample_sum(): - """Verifying downsampling of an array with expected result in sum mode""" - image1 = np.arange(4*6).reshape(4, 6) - out1 = tf.downsample(image1, (2, 3)) - expected1 = np.array([[ 24, 42], - [ 96, 114]]) - assert_array_equal(expected1, out1) - image2 = np.arange(5*8).reshape(5, 8) - out2 = tf.downsample(image2, (3, 3)) - expected2 = np.array([[ 81, 108, 87], - [174, 192, 138]]) - assert_array_equal(expected2, out2) - -def test_downsample_mean(): +def test_downscale_local_means(): """Verifying downsampling of an array with expected result in mean mode""" image1 = np.arange(4*6).reshape(4, 6) - out1 = tf.downsample(image1, (2, 3), 'mean') + out1 = downscale_local_means(image1, (2, 3)) expected1 = np.array([[ 4., 7.], [ 16., 19.]]) assert_array_equal(expected1, out1) image2 = np.arange(5*8).reshape(5, 8) - out2 = tf.downsample(image2, (4, 5), 'mean') + out2 = downscale_local_means(image2, (4, 5)) expected2 = np.array([[ 14. , 10.8], [ 8.5, 5.7]]) assert_array_equal(expected2, out2) From 8ac4427d9b6996079dbb213c030c775e85df75a2 Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Fri, 5 Jul 2013 00:21:11 +0800 Subject: [PATCH 11/14] Expanding the documentation in transform._warps --- skimage/measure/_sum_blocks.py | 6 ++++++ skimage/transform/_warps.py | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/skimage/measure/_sum_blocks.py b/skimage/measure/_sum_blocks.py index e6a478e5..b349f3e6 100644 --- a/skimage/measure/_sum_blocks.py +++ b/skimage/measure/_sum_blocks.py @@ -5,6 +5,12 @@ def sum_blocks(array, factors): """Sums the elements in blocks of integer factors and pads the original array with zeroes if the dimensions are not perfectly divisible by factors. + This function is different from resize and rescale in transform._warps in + the sense that they use interpolation to upsample or downsample on a 2D + array, while this function performs only dawnsampling but on any + n-dimensional array and returns the sum of elements in a block of size + factors in the original array. + Parameters ---------- array : ndarray diff --git a/skimage/transform/_warps.py b/skimage/transform/_warps.py index cca5d1d9..487ed760 100644 --- a/skimage/transform/_warps.py +++ b/skimage/transform/_warps.py @@ -8,6 +8,12 @@ from skimage.util.shape import view_as_blocks, _pad_asymmetric_zeros def resize(image, output_shape, order=1, mode='constant', cval=0.): """Resize image to match a certain size. + Resize performs interpolation to upsample or downsample 2D arrays. For + downsampling any n-dimensional array by performing arithmetic sum or + arithmetic mean, see meassure._sum_blocks.sum_blocks and + transform._warps.downscale_local_means respectively. + + Parameters ---------- image : ndarray @@ -89,6 +95,11 @@ def resize(image, output_shape, order=1, mode='constant', cval=0.): def rescale(image, scale, order=1, mode='constant', cval=0.): """Scale image by a certain factor. + Rescale performs interpolation to upsample or downsample 2D arrays. For + downsampling any n-dimensional array by performing arithmetic sum or + arithmetic mean, see meassure._sum_blocks.sum_blocks and + transform._warps.downscale_local_means respectively. + Parameters ---------- image : ndarray @@ -339,6 +350,12 @@ def downscale_local_means(array, factors): the original array with zeroes if the dimensions are not perfectly divisible by factors and replaces it with mean i.e. average value. + This function is different from resize and rescale in the sense that they + use interpolation to upsample or downsample on a 2D array, while this + function performs only dawnsampling but on any n-dimensional array and + returns the arithmetic mean of elements in a block of size factors in the + original array. + Parameters ---------- array : ndarray From f79582ecb476e660c9fbcb846ac0deedb90f2313 Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Fri, 5 Jul 2013 01:34:17 +0800 Subject: [PATCH 12/14] Attempt to solve circular import issue --- skimage/measure/_sum_blocks.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/skimage/measure/_sum_blocks.py b/skimage/measure/_sum_blocks.py index b349f3e6..bfba0089 100644 --- a/skimage/measure/_sum_blocks.py +++ b/skimage/measure/_sum_blocks.py @@ -1,9 +1,6 @@ -from ..transform._warps import _downsample - - def sum_blocks(array, factors): - """Sums the elements in blocks of integer factors and pads the original - array with zeroes if the dimensions are not perfectly divisible by factors. + """Sums the elements in blocks of integer factors and pads the original + array with zeroes if the dimensions are not perfectly divisible by factors. This function is different from resize and rescale in transform._warps in the sense that they use interpolation to upsample or downsample on a 2D @@ -37,4 +34,6 @@ def sum_blocks(array, factors): [33, 27]]) """ - return _downsample(array, factors) + return _downsample(array, factors) + +from ..transform._warps import _downsample From 70d180408f257bcbe08d2c95fd0729bc09644441 Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Fri, 5 Jul 2013 10:46:45 +0800 Subject: [PATCH 13/14] Another attempt at solving circular import issue --- skimage/measure/_sum_blocks.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/skimage/measure/_sum_blocks.py b/skimage/measure/_sum_blocks.py index bfba0089..c65c121e 100644 --- a/skimage/measure/_sum_blocks.py +++ b/skimage/measure/_sum_blocks.py @@ -34,6 +34,5 @@ def sum_blocks(array, factors): [33, 27]]) """ + from ..transform._warps import _downsample return _downsample(array, factors) - -from ..transform._warps import _downsample From 971e7beec5a906c22f11469cb1afc0e2c2d322cc Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Fri, 5 Jul 2013 11:10:12 +0800 Subject: [PATCH 14/14] Correcting typos --- skimage/transform/_warps.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skimage/transform/_warps.py b/skimage/transform/_warps.py index 487ed760..c2727c74 100644 --- a/skimage/transform/_warps.py +++ b/skimage/transform/_warps.py @@ -10,7 +10,7 @@ def resize(image, output_shape, order=1, mode='constant', cval=0.): Resize performs interpolation to upsample or downsample 2D arrays. For downsampling any n-dimensional array by performing arithmetic sum or - arithmetic mean, see meassure._sum_blocks.sum_blocks and + arithmetic mean, see measure._sum_blocks.sum_blocks and transform._warps.downscale_local_means respectively. @@ -97,7 +97,7 @@ def rescale(image, scale, order=1, mode='constant', cval=0.): Rescale performs interpolation to upsample or downsample 2D arrays. For downsampling any n-dimensional array by performing arithmetic sum or - arithmetic mean, see meassure._sum_blocks.sum_blocks and + arithmetic mean, see measure._sum_blocks.sum_blocks and transform._warps.downscale_local_means respectively. Parameters