From 078ed488c8703c103fda1bac3513c8b5b4218095 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Fri, 3 Feb 2012 15:46:43 -0500 Subject: [PATCH 1/4] Fix casting error when converting from int8 to uint16. This error only comes up in development versions of numpy, which refuses to cast the input to the correct type for inplace operations. --- skimage/util/dtype.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/util/dtype.py b/skimage/util/dtype.py index da2ff5e7..11199cc8 100644 --- a/skimage/util/dtype.py +++ b/skimage/util/dtype.py @@ -120,7 +120,7 @@ def _convert(image, dtype): # int8 -> uint32 image = dtype(image) image *= 2**16 + 2**8 + 1 - result += image + result += dtype(image) return result if kind == 'i': # signed integer -> signed integer From 74b0f41e1533692abd9e96b08c136240597019f1 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Fri, 3 Feb 2012 16:25:11 -0500 Subject: [PATCH 2/4] ENH: Add tests for full coverage. The extra dtype tests are not exhaustive, but, instead, were added to test specific code paths in `_convert`. --- skimage/util/tests/test_dtype.py | 35 +++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/skimage/util/tests/test_dtype.py b/skimage/util/tests/test_dtype.py index 4932a480..dabfe044 100644 --- a/skimage/util/tests/test_dtype.py +++ b/skimage/util/tests/test_dtype.py @@ -1,7 +1,9 @@ import numpy as np -from numpy.testing import assert_equal +from numpy.testing import assert_equal, assert_raises from skimage import img_as_int, img_as_float, \ img_as_uint, img_as_ubyte +from skimage.util.dtype import _convert + dtype_range = {np.uint8: (0, 255), np.uint16: (0, 65535), @@ -34,6 +36,37 @@ def test_range(): "From %s to %s" % (np.dtype(dtype), np.dtype(dt)), \ y, omin, omax + +def test_range_extra_dtypes(): + """Test code paths that are not skipped by `test_range`""" + + # Add non-standard data types that are allowed by the `_convert` function. + dtype_range_extra = dtype_range.copy() + dtype_range_extra.update({np.int32: (-2147483648, 2147483647), + np.uint32: (0, 4294967295)}) + + dtype_pairs = [(np.uint8, np.uint32), + (np.int8, np.uint32), + (np.int8, np.int32), + (np.int32, np.int8), + (np.float64, np.float32), + (np.int32, np.float32)] + + for dtype_in, dt in dtype_pairs: + imin, imax = dtype_range_extra[dtype_in] + x = np.linspace(imin, imax, 10).astype(dtype_in) + y = _convert(x, dt) + omin, omax = dtype_range_extra[dt] + yield _verify_range, \ + "From %s to %s" % (np.dtype(dtype_in), np.dtype(dt)), \ + y, omin, omax + + +def test_unsupported_dtype(): + x = np.arange(10).astype(np.uint64) + assert_raises(ValueError, img_as_int, x) + + if __name__ == '__main__': np.testing.run_module_suite() From 0dcd5dc38e20efc8195b5339bd6512a15d10ded8 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Fri, 3 Feb 2012 17:36:06 -0500 Subject: [PATCH 3/4] ENH: Check that float images are between 0 and 1. --- skimage/util/dtype.py | 2 ++ skimage/util/tests/test_dtype.py | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/skimage/util/dtype.py b/skimage/util/dtype.py index 11199cc8..98d74167 100644 --- a/skimage/util/dtype.py +++ b/skimage/util/dtype.py @@ -63,6 +63,8 @@ def _convert(image, dtype): "%s to %s" % (dtypeobj_in, dtypeobj)) if kind_in == 'f': + if np.min(image) < 0 or np.max(image) > 1: + raise ValueError("Images of type float must be between 0 and 1") if kind == 'f': # floating point -> floating point if itemsize_in > itemsize: diff --git a/skimage/util/tests/test_dtype.py b/skimage/util/tests/test_dtype.py index dabfe044..c6b38d0f 100644 --- a/skimage/util/tests/test_dtype.py +++ b/skimage/util/tests/test_dtype.py @@ -67,6 +67,13 @@ def test_unsupported_dtype(): assert_raises(ValueError, img_as_int, x) +def test_float_out_of_range(): + too_high = np.array([2], dtype=np.float32) + assert_raises(ValueError, img_as_int, too_high) + too_low = np.array([-1], dtype=np.float32) + assert_raises(ValueError, img_as_int, too_low) + + if __name__ == '__main__': np.testing.run_module_suite() From 5ed5c070c7740e10b2380285ad2cb90c5e0259a2 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 4 Feb 2012 14:20:37 -0500 Subject: [PATCH 4/4] Fix test: clip image that exceeded float range. --- skimage/filter/tests/test_tv_denoise.py | 2 ++ skimage/util/dtype.py | 10 +++++----- skimage/util/tests/test_dtype.py | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/skimage/filter/tests/test_tv_denoise.py b/skimage/filter/tests/test_tv_denoise.py index 9869d992..f851f4f1 100644 --- a/skimage/filter/tests/test_tv_denoise.py +++ b/skimage/filter/tests/test_tv_denoise.py @@ -15,6 +15,8 @@ class TestTvDenoise(): lena = color.rgb2gray(data.lena())[:256, :256] # add noise to lena lena += 0.5 * lena.std()*np.random.randn(*lena.shape) + # clip noise so that it does not exceed allowed range for float images. + lena = np.clip(lena, 0, 1) # denoise denoised_lena = filter.tv_denoise(lena, weight=60.0) # which dtype? diff --git a/skimage/util/dtype.py b/skimage/util/dtype.py index 98d74167..ac8f305b 100644 --- a/skimage/util/dtype.py +++ b/skimage/util/dtype.py @@ -20,7 +20,7 @@ _supported_types = (np.uint8, np.uint16, np.uint32, np.float16, np.float32, np.float64) -def _convert(image, dtype): +def convert(image, dtype): """ Convert an image to the requested data-type. @@ -167,7 +167,7 @@ def img_as_float(image): Negative input values will be shifted to the positive domain. """ - return _convert(image, np.float64) + return convert(image, np.float64) def img_as_uint(image): @@ -188,7 +188,7 @@ def img_as_uint(image): Negative input values will be shifted to the positive domain. """ - return _convert(image, np.uint16) + return convert(image, np.uint16) def img_as_int(image): @@ -210,7 +210,7 @@ def img_as_int(image): the output image will still only have positive values. """ - return _convert(image, np.int16) + return convert(image, np.int16) def img_as_ubyte(image): @@ -232,4 +232,4 @@ def img_as_ubyte(image): the output image will still only have positive values. """ - return _convert(image, np.uint8) + return convert(image, np.uint8) diff --git a/skimage/util/tests/test_dtype.py b/skimage/util/tests/test_dtype.py index c6b38d0f..11dd7248 100644 --- a/skimage/util/tests/test_dtype.py +++ b/skimage/util/tests/test_dtype.py @@ -2,7 +2,7 @@ import numpy as np from numpy.testing import assert_equal, assert_raises from skimage import img_as_int, img_as_float, \ img_as_uint, img_as_ubyte -from skimage.util.dtype import _convert +from skimage.util.dtype import convert dtype_range = {np.uint8: (0, 255), @@ -40,7 +40,7 @@ def test_range(): def test_range_extra_dtypes(): """Test code paths that are not skipped by `test_range`""" - # Add non-standard data types that are allowed by the `_convert` function. + # Add non-standard data types that are allowed by the `convert` function. dtype_range_extra = dtype_range.copy() dtype_range_extra.update({np.int32: (-2147483648, 2147483647), np.uint32: (0, 4294967295)}) @@ -55,7 +55,7 @@ def test_range_extra_dtypes(): for dtype_in, dt in dtype_pairs: imin, imax = dtype_range_extra[dtype_in] x = np.linspace(imin, imax, 10).astype(dtype_in) - y = _convert(x, dt) + y = convert(x, dt) omin, omax = dtype_range_extra[dt] yield _verify_range, \ "From %s to %s" % (np.dtype(dtype_in), np.dtype(dt)), \