From 58a4a43f104d40d6e12019e4c37a602f09b860d6 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Fri, 28 Jun 2013 18:06:10 -0500 Subject: [PATCH 1/3] Change rank.pyx to _rank.py - Cython was unnecessary in the main rank module - Don't shadow package name in the module - Add support for int8 images. --- skimage/filter/rank/__init__.py | 6 +++--- skimage/filter/rank/{rank.pyx => _rank.py} | 20 +++++++++++------- skimage/filter/rank/tests/test_rank.py | 24 ++++++++++++++++++++++ 3 files changed, 40 insertions(+), 10 deletions(-) rename skimage/filter/rank/{rank.pyx => _rank.py} (98%) 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 98% rename from skimage/filter/rank/rank.pyx rename to skimage/filter/rank/_rank.py index 559f1c76..652732dd 100644 --- a/skimage/filter/rank/rank.pyx +++ b/skimage/filter/rank/_rank.py @@ -40,13 +40,10 @@ 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) + 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) elif image.dtype == np.uint16: if func16 is None: raise TypeError("Not implemented for uint16 image.") @@ -63,6 +60,15 @@ def _apply(func8, func16, image, selem, out, mask, shift_x, shift_y): 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..7a515a89 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 from skimage import data, util from skimage.morphology import cmorph, disk from skimage.filter import rank @@ -186,6 +187,29 @@ def test_compare_autolevels_16bit(): assert_array_equal(loc_autolevel, loc_perc_autolevel) +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 From f92f057cbd3690efc3ad98579f435820e3752038 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 29 Jun 2013 11:25:08 -0500 Subject: [PATCH 2/3] Add support for all data types. All dtypes larger than 8-bits are converted to uint16 and then bit-shifted to uint12. --- skimage/filter/rank/_rank.py | 12 +++++------- skimage/filter/rank/tests/test_rank.py | 20 +++++++++++++++++++- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/skimage/filter/rank/_rank.py b/skimage/filter/rank/_rank.py index 652732dd..ea58ad86 100644 --- a/skimage/filter/rank/_rank.py +++ 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 @@ -44,18 +44,16 @@ def _apply(func8, func16, image, selem, out, mask, shift_x, shift_y): if func8 is not None and (is_8bit or func16 is None): out = _apply8(func8, image, selem, out, mask, shift_x, shift_y) - elif image.dtype == np.uint16: - if func16 is None: - raise TypeError("Not implemented for uint16 image.") + 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 diff --git a/skimage/filter/rank/tests/test_rank.py b/skimage/filter/rank/tests/test_rank.py index 7a515a89..d39386c6 100644 --- a/skimage/filter/rank/tests/test_rank.py +++ b/skimage/filter/rank/tests/test_rank.py @@ -1,7 +1,7 @@ import numpy as np from numpy.testing import run_module_suite, assert_array_equal, assert_raises -from skimage import img_as_ubyte +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 @@ -187,6 +187,24 @@ 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 From d96411a7438cd90c7b1284665203643cd0bf06cf Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 29 Jun 2013 12:40:54 -0500 Subject: [PATCH 3/3] Fix build script --- skimage/filter/setup.py | 4 ---- 1 file changed, 4 deletions(-) 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()])