Skip to content

Commit

Permalink
add multichannel deprecation warning and clarify shape handling in re…
Browse files Browse the repository at this point in the history
…scale
  • Loading branch information
grlee77 committed May 21, 2015
1 parent 3a45e3c commit 5de0f39
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 47 deletions.
4 changes: 2 additions & 2 deletions doc/examples/plot_pyramid.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
The `pyramid_gaussian` function takes an image and yields successive images
shrunk by a constant scale factor. Image pyramids are often used, e.g., to
implement algorithms for denoising, texture discrimination, and scale- invariant
implement algorithms for denoising, texture discrimination, and scale-invariant
detection.
"""
Expand All @@ -18,7 +18,7 @@

image = data.astronaut()
rows, cols, dim = image.shape
pyramid = tuple(pyramid_gaussian(image, downscale=2))
pyramid = tuple(pyramid_gaussian(image, downscale=2, multichannel=True))

composite_image = np.zeros((rows, cols + cols / 2, 3), dtype=np.double)

Expand Down
2 changes: 1 addition & 1 deletion doc/examples/plot_radon_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
from skimage.transform import radon, rescale

image = imread(data_dir + "/phantom.png", as_grey=True)
image = rescale(image, scale=0.4)
image = rescale(image, scale=0.4, multichannel=False)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4.5))

Expand Down
3 changes: 2 additions & 1 deletion skimage/feature/orb.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ def __init__(self, downscale=1.2, n_scales=8,

def _build_pyramid(self, image):
image = _prepare_grayscale_input_2D(image)
return list(pyramid_gaussian(image, self.n_scales - 1, self.downscale))
return list(pyramid_gaussian(image, self.n_scales - 1,
self.downscale, multichannel=False))

def _detect_octave(self, octave_image):
# Extract keypoints for current octave
Expand Down
55 changes: 32 additions & 23 deletions skimage/transform/_warps.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import warnings
import numpy as np
from scipy import ndimage

Expand All @@ -7,11 +8,16 @@


def _multichannel_default(multichannel, ndim):
# utility for maintaining previous color image default behavior
if ndim == 3:
return True
if multichannel is not None:
return multichannel
else:
return False
warnings.warn('default multichannel argument (None) is deprecated. '
'Specifiy True or False explicitly.')
# utility for maintaining previous color image default behavior
if ndim == 3:
return True
else:
return False


def resize(image, output_shape, order=1, mode='constant', cval=0, clip=True,
Expand Down Expand Up @@ -67,27 +73,31 @@ def resize(image, output_shape, order=1, mode='constant', cval=0, clip=True,
"""
output_shape = np.asarray(output_shape)
orig_shape = image.shape
if len(orig_shape) < len(output_shape):
orig_shape = orig_shape + (1, ) * (len(output_shape) - len(orig_shape))
elif len(output_shape) < len(orig_shape):
orig_shape = orig_shape[:-1]
orig_shape = np.asarray(orig_shape)

dim_scales = orig_shape.astype('f8') / output_shape

ndim_out = len(output_shape)
input_shape = image.shape
if ndim_out > image.ndim:
# append dimensions to input_shape
input_shape = input_shape + (1, ) * (ndim_out - image.ndim)
elif ndim_out == image.ndim - 1:
# multichannel case: append shape of last axis
output_shape = np.concatenate((output_shape, [image.shape[-1], ]))
ndim_out += 1
elif ndim_out < image.ndim - 1:
raise ValueError("len(output_shape) cannot be smaller than the image "
"dimensions")

input_shape = np.asarray(input_shape, dtype=float)
dim_scales = input_shape / output_shape

# 2-dimensional interpolation
if ndim_out == 2 or (ndim_out == 3 and
(image.ndim == 2 or
output_shape[2] == image.shape[2])):
if ndim_out == 2 or (ndim_out == 3 and output_shape[2] == input_shape[2]):
rows = output_shape[0]
cols = output_shape[1]
orig_rows = orig_shape[0]
orig_cols = orig_shape[1]
input_rows = input_shape[0]
input_cols = input_shape[1]
if rows == 1 and cols == 1:
tform = AffineTransform(translation=(orig_cols / 2.0 - 0.5,
orig_rows / 2.0 - 0.5))
tform = AffineTransform(translation=(input_cols / 2.0 - 0.5,
input_rows / 2.0 - 0.5))
else:
# 3 control points necessary to estimate exact AffineTransform
src_corners = np.array([[1, 1], [1, rows], [cols, rows]]) - 1
Expand Down Expand Up @@ -178,13 +188,12 @@ def rescale(image, scale, order=1, mode='constant', cval=0, clip=True,
(256, 256)
"""
if multichannel is None:
multichannel = _multichannel_default(multichannel, image.ndim)
multichannel = _multichannel_default(multichannel, image.ndim)
scale = np.atleast_1d(scale)
if len(scale) > 1 and len(scale) != image.ndim:
raise ValueError("must supply a single scale or one value per axis.")
orig_shape = np.asarray(image.shape)
output_shape = np.round(scale * orig_shape).astype('i8')
output_shape = np.round(scale * orig_shape)
if multichannel: # don't scale channel dimension
output_shape[-1] = orig_shape[-1]

Expand Down
13 changes: 5 additions & 8 deletions skimage/transform/pyramids.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@

def _smooth(image, sigma, mode, cval, multichannel=None):
"""Return image with each channel smoothed by the Gaussian filter."""
if multichannel is None:
multichannel = _multichannel_default(multichannel, image.ndim)
multichannel = _multichannel_default(multichannel, image.ndim)
smoothed = np.empty(image.shape, dtype=np.double)

# apply Gaussian filter to all channels independently
Expand Down Expand Up @@ -52,6 +51,7 @@ def pyramid_reduce(image, downscale=2, sigma=None, order=1,
cval is the value when mode is equal to 'constant'.
cval : float, optional
Value to fill past edges of input if mode is 'constant'.
multichannel : bool, optional
If True, last axis will not be rescaled. The default is True for 3D
(2D+color) inputs, False otherwise.
Expand All @@ -65,8 +65,7 @@ def pyramid_reduce(image, downscale=2, sigma=None, order=1,
.. [1] http://web.mit.edu/persci/people/adelson/pub_pdfs/pyramid83.pdf
"""
if multichannel is None:
multichannel = _multichannel_default(multichannel, image.ndim)
multichannel = _multichannel_default(multichannel, image.ndim)
_check_factor(downscale)

image = img_as_float(image)
Expand Down Expand Up @@ -121,8 +120,7 @@ def pyramid_expand(image, upscale=2, sigma=None, order=1,
.. [1] http://web.mit.edu/persci/people/adelson/pub_pdfs/pyramid83.pdf
"""
if multichannel is None:
multichannel = _multichannel_default(multichannel, image.ndim)
multichannel = _multichannel_default(multichannel, image.ndim)
_check_factor(upscale)

image = img_as_float(image)
Expand Down Expand Up @@ -270,8 +268,7 @@ def pyramid_laplacian(image, max_layer=-1, downscale=2, sigma=None, order=1,
.. [2] http://sepwww.stanford.edu/~morgan/texturematch/paper_html/node3.html
"""
if multichannel is None:
multichannel = _multichannel_default(multichannel, image.ndim)
multichannel = _multichannel_default(multichannel, image.ndim)
_check_factor(downscale)

# cast to float for consistent data type in pyramid
Expand Down
8 changes: 8 additions & 0 deletions skimage/transform/tests/test_pyramids.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@
import numpy as np
from skimage import data
from skimage.transform import pyramids
from skimage._shared._warnings import expected_warnings


image = data.astronaut()
image_gray = image[..., 0]


@expected_warnings(['default multichannel'])
def test_pyramid_reduce_rgb():
rows, cols, dim = image.shape
out = pyramids.pyramid_reduce(image, downscale=2)
assert_array_equal(out.shape, (rows / 2, cols / 2, dim))


@expected_warnings(['default multichannel'])
def test_pyramid_reduce_gray():
rows, cols = image_gray.shape
out = pyramids.pyramid_reduce(image_gray, downscale=2)
Expand All @@ -29,12 +32,14 @@ def test_pyramid_reduce_nd():
assert_array_equal(out.shape, expected_shape)


@expected_warnings(['default multichannel'])
def test_pyramid_expand_rgb():
rows, cols, dim = image.shape
out = pyramids.pyramid_expand(image, upscale=2)
assert_array_equal(out.shape, (rows * 2, cols * 2, dim))


@expected_warnings(['default multichannel'])
def test_pyramid_expand_gray():
rows, cols = image_gray.shape
out = pyramids.pyramid_expand(image_gray, upscale=2)
Expand All @@ -50,6 +55,7 @@ def test_pyramid_expand_nd():
assert_array_equal(out.shape, expected_shape)


@expected_warnings(['default multichannel'])
def test_build_gaussian_pyramid_rgb():
rows, cols, dim = image.shape
pyramid = pyramids.pyramid_gaussian(image, downscale=2)
Expand All @@ -58,6 +64,7 @@ def test_build_gaussian_pyramid_rgb():
assert_array_equal(out.shape, layer_shape)


@expected_warnings(['default multichannel'])
def test_build_gaussian_pyramid_gray():
rows, cols = image_gray.shape
pyramid = pyramids.pyramid_gaussian(image_gray, downscale=2)
Expand All @@ -77,6 +84,7 @@ def test_build_gaussian_pyramid_nd():
assert_array_equal(out.shape, layer_shape)


@expected_warnings(['default multichannel'])
def test_build_laplacian_pyramid_rgb():
rows, cols, dim = image.shape
pyramid = pyramids.pyramid_laplacian(image, downscale=2)
Expand Down
4 changes: 2 additions & 2 deletions skimage/transform/tests/test_radon_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@


PHANTOM = imread(os.path.join(data_dir, "phantom.png"),
as_grey=True)[::2, ::2]
PHANTOM = rescale(PHANTOM, 0.5, order=1)
as_grey=True)[::2, ::2]
PHANTOM = rescale(PHANTOM, 0.5, order=1, multichannel=False)


def _debug_plot(original, result, sinogram=None):
Expand Down
32 changes: 23 additions & 9 deletions skimage/transform/tests/test_warps.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def test_warp_nd():
assert_almost_equal(outx, refx)


@expected_warnings(['default multichannel'])
def test_warp_clip():
x = np.zeros((5, 5), dtype=np.double)
x[2, 2] = 1
Expand Down Expand Up @@ -130,6 +131,7 @@ def test_rotate_center():
assert_almost_equal(x0, x)


@expected_warnings(['default multichannel'])
def test_rescale():
# same scale factor
x = np.zeros((5, 5), dtype=np.double)
Expand All @@ -156,9 +158,6 @@ def test_rescale_multichannel():
# 2D
scaled = rescale(x, 2, order=0, multichannel=False)
assert_equal(scaled.shape, (16, 6))
# multichannel defaults to False
scaled = rescale(x, 2, order=0)
assert_equal(scaled.shape, (16, 6))

# 2D + channels
x = np.zeros((8, 8, 3), dtype=np.double)
Expand All @@ -177,6 +176,21 @@ def test_rescale_multichannel():
assert_equal(scaled.shape, (16, 16, 16, 6))


@expected_warnings(['default multichannel'])
def test_rescale_multichannel_defaults():
# Tests to ensure multichannel=None matches the previous default behaviour

# 2D: multichannel should default to False
x = np.zeros((8, 3), dtype=np.double)
scaled = rescale(x, 2, order=0)
assert_equal(scaled.shape, (16, 6))

# 3D: multichannel should default to True
x = np.zeros((8, 8, 3), dtype=np.double)
scaled = rescale(x, 2, order=0,)
assert_equal(scaled.shape, (16, 16, 3))


def test_resize2d():
x = np.zeros((5, 5), dtype=np.double)
x[1, 1] = 1
Expand Down Expand Up @@ -269,7 +283,7 @@ def test_warp_identity():
assert np.allclose(img, warp(img, AffineTransform(rotation=0)))
assert not np.allclose(img, warp(img, AffineTransform(rotation=0.1)))
rgb_img = np.transpose(np.asarray([img, np.zeros_like(img), img]),
(1, 2, 0))
(1, 2, 0))
warped_rgb_img = warp(rgb_img, AffineTransform(rotation=0.1))
assert np.allclose(rgb_img, warp(rgb_img, AffineTransform(rotation=0)))
assert not np.allclose(rgb_img, warped_rgb_img)
Expand All @@ -288,14 +302,14 @@ def test_warp_coords_example():
def test_downscale_local_mean():
image1 = np.arange(4 * 6).reshape(4, 6)
out1 = downscale_local_mean(image1, (2, 3))
expected1 = np.array([[ 4., 7.],
[ 16., 19.]])
expected1 = np.array([[ 4., 7.],
[16., 19.]])
assert_equal(expected1, out1)

image2 = np.arange(5 * 8).reshape(5, 8)
out2 = downscale_local_mean(image2, (4, 5))
expected2 = np.array([[ 14. , 10.8],
[ 8.5, 5.7]])
expected2 = np.array([[14. , 10.8],
[ 8.5, 5.7]])
assert_equal(expected2, out2)


Expand All @@ -320,6 +334,7 @@ def test_slow_warp_nonint_oshape():
warp(image, lambda xy: xy, output_shape=(13.0001, 19.9999))


@expected_warnings(['default multichannel'])
def test_keep_range():
image = np.linspace(0, 2, 25).reshape(5, 5)

Expand All @@ -337,6 +352,5 @@ def test_keep_range():
assert out.max() == 2 / 255.0



if __name__ == "__main__":
run_module_suite()
2 changes: 1 addition & 1 deletion skimage/viewer/tests/test_viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def make_key_event(key):
def test_collection_viewer():

img = data.astronaut()
img_collection = tuple(pyramid_gaussian(img))
img_collection = tuple(pyramid_gaussian(img, multichannel=True))

view = CollectionViewer(img_collection)
make_key_event(48)
Expand Down

0 comments on commit 5de0f39

Please sign in to comment.