diff --git a/skimage/filter/rank/__init__.py b/skimage/filter/rank/__init__.py index 906566b7..deceaade 100644 --- a/skimage/filter/rank/__init__.py +++ b/skimage/filter/rank/__init__.py @@ -1,6 +1,6 @@ -from .rank import (autolevel, bottomhat, equalize, gradient, maximum, mean, - meansubtraction, median, minimum, modal, morph_contr_enh, - pop, threshold, tophat, noise_filter, entropy, otsu) +from ._rank import (autolevel, bottomhat, equalize, gradient, maximum, mean, + meansubtraction, median, minimum, modal, morph_contr_enh, + pop, threshold, tophat, noise_filter, entropy, otsu) from .percentile_rank import (percentile_autolevel, percentile_gradient, percentile_mean, percentile_mean_subtraction, percentile_morph_contr_enh, percentile, diff --git a/skimage/filter/rank/rank.pyx b/skimage/filter/rank/_rank.py similarity index 97% rename from skimage/filter/rank/rank.pyx rename to skimage/filter/rank/_rank.py index 559f1c76..ea58ad86 100644 --- a/skimage/filter/rank/rank.pyx +++ b/skimage/filter/rank/_rank.py @@ -17,7 +17,7 @@ References """ import numpy as np -from skimage import img_as_ubyte +from skimage import img_as_ubyte, img_as_uint from skimage.filter.rank import _crank8, _crank16 from skimage.filter.rank.generic import find_bitdepth @@ -40,29 +40,33 @@ def _apply(func8, func16, image, selem, out, mask, shift_x, shift_y): if image is out: raise NotImplementedError("Cannot perform rank operation in place.") - if image.dtype == np.uint8: - if func8 is None: - raise TypeError("Not implemented for uint8 image.") - if out is None: - out = np.zeros(image.shape, dtype=np.uint8) - func8(image, selem, shift_x=shift_x, shift_y=shift_y, - mask=mask, out=out) - elif image.dtype == np.uint16: - if func16 is None: - raise TypeError("Not implemented for uint16 image.") + is_8bit = image.dtype in (np.uint8, np.int8) + + if func8 is not None and (is_8bit or func16 is None): + out = _apply8(func8, image, selem, out, mask, shift_x, shift_y) + else: + image = img_as_uint(image) if out is None: out = np.zeros(image.shape, dtype=np.uint16) bitdepth = find_bitdepth(image) if bitdepth > 11: - raise ValueError("Only uint16 <4096 image (12bit) supported.") + image = image >> 4 + bitdepth = find_bitdepth(image) func16(image, selem, shift_x=shift_x, shift_y=shift_y, mask=mask, bitdepth=bitdepth + 1, out=out) - else: - raise TypeError("Only uint8 and uint16 image supported.") return out +def _apply8(func8, image, selem, out, mask, shift_x, shift_y): + if out is None: + out = np.zeros(image.shape, dtype=np.uint8) + image = img_as_ubyte(image) + func8(image, selem, shift_x=shift_x, shift_y=shift_y, + mask=mask, out=out) + return out + + def autolevel(image, selem, out=None, mask=None, shift_x=False, shift_y=False): """Autolevel image using local histogram. diff --git a/skimage/filter/rank/tests/test_rank.py b/skimage/filter/rank/tests/test_rank.py index d7748d0f..d39386c6 100644 --- a/skimage/filter/rank/tests/test_rank.py +++ b/skimage/filter/rank/tests/test_rank.py @@ -1,6 +1,7 @@ import numpy as np from numpy.testing import run_module_suite, assert_array_equal, assert_raises +from skimage import img_as_ubyte, img_as_uint, img_as_float from skimage import data, util from skimage.morphology import cmorph, disk from skimage.filter import rank @@ -186,6 +187,47 @@ def test_compare_autolevels_16bit(): assert_array_equal(loc_autolevel, loc_perc_autolevel) +def test_compare_uint_vs_float(): + # filters applied on 8-bit image ore 16-bit image (having only real 8-bit of + # dynamic) should be identical + + # Create signed int8 image that and convert it to uint8 + image_uint = img_as_uint(data.camera()) + image_float = img_as_float(image_uint) + + methods = ['autolevel', 'bottomhat', 'equalize', 'gradient', 'threshold', + 'meansubtraction', 'morph_contr_enh', 'pop', 'tophat'] + + for method in methods: + func = getattr(rank, method) + out_u = func(image_uint, disk(3)) + out_f = func(image_float, disk(3)) + assert_array_equal(out_u, out_f) + + +def test_compare_8bit_unsigned_vs_signed(): + # filters applied on 8-bit image ore 16-bit image (having only real 8-bit of + # dynamic) should be identical + + # Create signed int8 image that and convert it to uint8 + image = img_as_ubyte(data.camera()) + image[image > 127] = 0 + image_s = image.astype(np.int8) + image_u = img_as_ubyte(image_s) + + assert_array_equal(image_u, img_as_ubyte(image_s)) + + methods = ['autolevel', 'bottomhat', 'equalize', 'gradient', 'maximum', + 'mean', 'meansubtraction', 'median', 'minimum', 'modal', + 'morph_contr_enh', 'pop', 'threshold', 'tophat'] + + for method in methods: + func = getattr(rank, method) + out_u = func(image_u, disk(3)) + out_s = func(image_s, disk(3)) + assert_array_equal(out_u, out_s) + + def test_compare_8bit_vs_16bit(): # filters applied on 8-bit image ore 16-bit image (having only real 8-bit of # dynamic) should be identical diff --git a/skimage/filter/setup.py b/skimage/filter/setup.py index b1d070fc..c70730f0 100644 --- a/skimage/filter/setup.py +++ b/skimage/filter/setup.py @@ -21,7 +21,6 @@ def configuration(parent_package='', top_path=None): cython(['rank/_crank16.pyx'], working_path=base_path) cython(['rank/_crank16_percentiles.pyx'], working_path=base_path) cython(['rank/_crank16_bilateral.pyx'], working_path=base_path) - cython(['rank/rank.pyx'], working_path=base_path) cython(['rank/percentile_rank.pyx'], working_path=base_path) cython(['rank/bilateral_rank.pyx'], working_path=base_path) @@ -46,9 +45,6 @@ def configuration(parent_package='', top_path=None): config.add_extension( 'rank._crank16_bilateral', sources=['rank/_crank16_bilateral.c'], include_dirs=[get_numpy_include_dirs()]) - config.add_extension( - 'rank.rank', sources=['rank/rank.c'], - include_dirs=[get_numpy_include_dirs()]) config.add_extension( 'rank.percentile_rank', sources=['rank/percentile_rank.c'], include_dirs=[get_numpy_include_dirs()])