Refactor local block functions and add additional functionality.

This commit is contained in:
Johannes Schönberger
2013-07-05 17:34:38 +02:00
parent e5e0c73755
commit 04b14b42c9
9 changed files with 286 additions and 158 deletions
+1 -1
View File
@@ -3,7 +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
from .blocks import block_sum, block_median, block_mean, block_min, block_max
__all__ = ['find_contours',
-38
View File
@@ -1,38 +0,0 @@
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
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]])
"""
from ..transform._warps import _downsample
return _downsample(array, factors)
+210
View File
@@ -0,0 +1,210 @@
import numpy as np
from ..util.shape import view_as_blocks, _pad_asymmetric_zeros
def _block_func(image, factors, func):
"""Down-sample image by integer factors.
Parameters
----------
image : ndarray
N-dimensional input image.
factors : array_like
Array containing down-sampling integer factor along each axis.
func : object
Function object which is used to calculate the return value for each
local block, e.g. `numpy.sum`.
Returns
-------
image : ndarray
Down-sampled image with same number of dimensions as input image.
"""
pad_size = []
if len(factors) != image.ndim:
raise ValueError("`factors` must have the same length "
"as `image.shape`.")
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
for i in range(len(block_shape) // 2):
out = func(out, axis=-1)
return out
def block_sum(image, block_size):
"""Sum elements in local blocks.
The image is padded with zeros if it is not perfectly divisible by integer
factors.
Parameters
----------
image : ndarray
N-dimensional input image.
block_size : array_like
Array containing down-sampling integer factor along each axis.
Returns
-------
image : ndarray
Down-sampled image with same number of dimensions as input image.
Example
-------
>>> a = np.arange(15).reshape(3, 5)
>>> a
image([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> sum_blocks(a, (2, 3))
image([[21, 24],
[33, 27]])
"""
return _block_func(image, block_size, np.sum)
def block_mean(image, block_size):
"""Average elements in local blocks.
The image is padded with zeros if it is not perfectly divisible by integer
factors.
Parameters
----------
image : ndarray
N-dimensional input image.
block_size : array_like
Array containing down-sampling integer factor along each axis.
Returns
-------
image : ndarray
Down-sampled image with same number of dimensions as input image.
Example
-------
>>> a = np.arange(15).reshape(3, 5)
>>> a
image([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> sum_blocks(a, (2, 3))
image([[21, 24],
[33, 27]])
"""
return _block_func(image, block_size, np.mean)
def block_median(image, block_size):
"""Median element in local blocks.
The image is padded with zeros if it is not perfectly divisible by integer
factors.
Parameters
----------
image : ndarray
N-dimensional input image.
block_size : array_like
Array containing down-sampling integer factor along each axis.
Returns
-------
image : ndarray
Down-sampled image with same number of dimensions as input image.
Example
-------
>>> a = np.arange(15).reshape(3, 5)
>>> a
image([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> sum_blocks(a, (2, 3))
image([[21, 24],
[33, 27]])
"""
return _block_func(image, block_size, np.median)
def block_min(image, block_size):
"""Minimum element in local blocks.
The image is padded with zeros if it is not perfectly divisible by integer
factors.
Parameters
----------
image : ndarray
N-dimensional input image.
block_size : array_like
Array containing down-sampling integer factor along each axis.
Returns
-------
image : ndarray
Down-sampled image with same number of dimensions as input image.
Example
-------
>>> a = np.arange(15).reshape(3, 5)
>>> a
image([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> sum_blocks(a, (2, 3))
image([[21, 24],
[33, 27]])
"""
return _block_func(image, block_size, np.min)
def block_max(image, block_size):
"""Maximum element in local blocks.
The image is padded with zeros if it is not perfectly divisible by integer
factors.
Parameters
----------
image : ndarray
N-dimensional input image.
block_size : array_like
Array containing down-sampling integer factor along each axis.
Returns
-------
image : ndarray
Down-sampled image with same number of dimensions as input image.
Example
-------
>>> a = np.arange(15).reshape(3, 5)
>>> a
image([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> sum_blocks(a, (2, 3))
image([[21, 24],
[33, 27]])
"""
return _block_func(image, block_size, np.max)
+21
View File
@@ -0,0 +1,21 @@
import numpy as np
from numpy.testing import assert_array_equal
from skimage.measure import block_sum
def test_block_sum():
image1 = np.arange(4 * 6).reshape(4, 6)
out1 = block_sum(image1, (2, 3))
expected1 = np.array([[ 24, 42],
[ 96, 114]])
assert_array_equal(expected1, out1)
image2 = np.arange(5 * 8).reshape(5, 8)
out2 = block_sum(image2, (3, 3))
expected2 = np.array([[ 81, 108, 87],
[174, 192, 138]])
assert_array_equal(expected2, out2)
if __name__ == "__main__":
np.testing.run_module_suite()
@@ -68,5 +68,6 @@ def test_invalid_input():
assert_raises(ValueError, ssim, X, X, win_size=8)
if __name__ == "__main__":
np.testing.run_module_suite()
-16
View File
@@ -1,16 +0,0 @@
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)
+2 -2
View File
@@ -9,7 +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 ._warps import swirl, resize, rotate, rescale, downscale_local_mean
from .pyramids import (pyramid_reduce, pyramid_expand,
pyramid_gaussian, pyramid_laplacian)
@@ -40,7 +40,7 @@ __all__ = ['hough_circle',
'resize',
'rotate',
'rescale',
'downscale_local_means',
'downscale_local_mean',
'pyramid_reduce',
'pyramid_expand',
'pyramid_gaussian',
+47 -97
View File
@@ -2,17 +2,16 @@ 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
from ..measure.blocks import _block_func
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 measure._sum_blocks.sum_blocks and
transform._warps.downscale_local_means respectively.
Performs interpolation to up-size or down-size images. For down-sampling
N-dimensional images by applying the arithmetic sum or mean, see
`skimage.measure.sum_blocks` and `skimage.transform.downscale_local_means`,
respectively.
Parameters
----------
@@ -95,10 +94,10 @@ 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 measure._sum_blocks.sum_blocks and
transform._warps.downscale_local_means respectively.
Performs interpolation to upscale or down-scale images. For down-sampling
N-dimensional images with integer factors by applying the arithmetic sum or
mean, see `skimage.measure.sum_blocks` and
`skimage.transform.downscale_local_means`, respectively.
Parameters
----------
@@ -226,6 +225,44 @@ def rotate(image, angle, resize=False, order=1, mode='constant', cval=0.):
mode=mode, cval=cval)
def downscale_local_mean(image, factors):
"""Down-sample N-dimensional image by local averaging.
The image is padded with zeros if it is not perfectly divisible by integer
factors.
In contrast to the 2-D interpolation in `skimage.transform.resize` and
`skimage.transform.rescale` this function may be applied to N-dimensional
images and calculates the local mean of elements in each block of size
`factors` in the input image.
Parameters
----------
image : ndarray
N-dimensional input image.
factors : array_like
Array containing down-sampling integer factor along each axis.
Returns
-------
image : ndarray
Down-sampled image with same number of dimensions as input image.
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 _block_func(image, factors, np.mean)
def _swirl_mapping(xy, center, rotation, strength, radius):
x, y = xy.T
x0, y0 = center
@@ -296,90 +333,3 @@ 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, sum=True):
"""Performs downsampling with integer factors.
Parameters
----------
array : ndarray
Input n-dimensional array.
factors: tuple
Tuple containing downsampling factor along each axis.
sum : bool
If True, downsampled element is the sum of its corresponding
constituent elements in the input array. Default is True.
Returns
-------
array : ndarray
Downsampled array with same number of dimensions as that of input
array.
"""
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 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.
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
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)
+4 -4
View File
@@ -6,7 +6,7 @@ from skimage.transform import (warp, warp_coords, rotate, resize, rescale,
AffineTransform,
ProjectiveTransform,
SimilarityTransform,
downscale_local_means)
downscale_local_mean)
from skimage import transform as tf, data, img_as_float
from skimage.color import rgb2gray
@@ -195,15 +195,15 @@ def test_warp_coords_example():
map_coordinates(image[:, :, 0], coords[:2])
def test_downscale_local_means():
def test_downscale_local_mean():
"""Verifying downsampling of an array with expected result in mean mode"""
image1 = np.arange(4*6).reshape(4, 6)
out1 = downscale_local_means(image1, (2, 3))
out1 = downscale_local_mean(image1, (2, 3))
expected1 = np.array([[ 4., 7.],
[ 16., 19.]])
assert_array_equal(expected1, out1)
image2 = np.arange(5*8).reshape(5, 8)
out2 = downscale_local_means(image2, (4, 5))
out2 = downscale_local_mean(image2, (4, 5))
expected2 = np.array([[ 14. , 10.8],
[ 8.5, 5.7]])
assert_array_equal(expected2, out2)