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 da2ff5e7..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. @@ -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: @@ -120,7 +122,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 @@ -165,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): @@ -186,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): @@ -208,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): @@ -230,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 4932a480..11dd7248 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,44 @@ 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) + + +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()