From b5c159d26dd21ea6e8365882992af5b2def901d1 Mon Sep 17 00:00:00 2001 From: Marianne Corvellec Date: Sat, 29 Jun 2013 15:29:44 -0400 Subject: [PATCH 1/9] Improving docstring + style/convention edits --- skimage/filter/ctmf.py | 46 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/skimage/filter/ctmf.py b/skimage/filter/ctmf.py index 267c5b6c..f8c5c8be 100644 --- a/skimage/filter/ctmf.py +++ b/skimage/filter/ctmf.py @@ -1,4 +1,5 @@ -'''ctmf.py - constant time per pixel median filtering with an octagonal shape +""" +ctmf.py - constant time per pixel median filtering with an octagonal shape Reference: S. Perreault and P. Hebert, "Median Filtering in Constant Time", IEEE Transactions on Image Processing, September 2007. @@ -9,7 +10,7 @@ Copyright (c) 2003-2009 Massachusetts Institute of Technology Copyright (c) 2009-2011 Broad Institute All rights reserved. Original author: Lee Kamentsky -''' +""" import numpy as np from . import _ctmf @@ -17,29 +18,28 @@ from ._rank_order import rank_order def median_filter(image, radius=2, mask=None, percent=50): - '''Masked median filter with octagon shape. + """Masked median filter with octagon shape. Parameters ---------- - image : (M,N) ndarray, dtype uint8 + image : (M,N) ndarray Input image. - radius : {int, 2}, optional - The radius of a circle inscribed into the filtering - octagon. Must be at least 2. Default radius is 2. - mask : (M,N) ndarray, dtype uint8, optional - A value of 1 indicates a significant pixel, 0 - that a pixel is masked. By default, all pixels - are considered. - percent : {int, 50}, optional + radius : int + Radius (in pixels) of a circle inscribed into the filtering + octagon. Must be at least 2. Default radius is 2. + mask : (M,N) ndarray + Mask with 1's for significant pixels, 0's for masked pixels. + By default, all pixels are considered significant. + percent : int The unmasked pixels within the octagon are sorted, and the - value at the `percent`-th index chosen. For example, the - default value of 50 chooses the median pixel. + value at `percent` percent of the index range is chosen. + Default value of 50 gives the median pixel. Returns ------- - out : (M,N) ndarray, dtype uint8 - Filtered array. In areas where the median filter does - not overlap the mask, the filtered result is underfined, but + out : (M,N) ndarray + Filtered array. In areas where the median filter does + not overlap the mask, the filtered result is undefined, but in practice, it will be the lowest value in the valid area. Examples @@ -49,13 +49,13 @@ def median_filter(image, radius=2, mask=None, percent=50): >>> b = median_filter(a) >>> b[2, 2] # the median filter is good at removing outliers 1.0 - ''' + """ if image.ndim != 2: - raise TypeError("The input 'image' must be a two dimensional array.") + raise TypeError("Input 'image' must be a two-dimensional array.") if radius < 2: - raise ValueError("The input 'radius' must be >= 2.") + raise ValueError("Input 'radius' must be >= 2.") if mask is None: mask = np.ones(image.shape, dtype=np.bool) @@ -78,13 +78,13 @@ def median_filter(image, radius=2, mask=None, percent=50): else: ranked_image = image[mask] was_ranked = False - input = np.zeros(image.shape, np.uint8) - input[mask] = ranked_image + input_ = np.zeros(image.shape, np.uint8) + input_[mask] = ranked_image mask.dtype = np.uint8 output = np.zeros(image.shape, np.uint8) - _ctmf.median_filter(input, mask, output, radius, percent) + _ctmf.median_filter(input_, mask, output, radius, percent) if was_ranked: # # The translation gives the original value at each ranking. From 9a45f82f1a9960411fe1ee035bfcd8e593041d7e Mon Sep 17 00:00:00 2001 From: Marianne Corvellec Date: Sat, 29 Jun 2013 17:49:36 -0400 Subject: [PATCH 2/9] Added some warnings for transparency. --- skimage/filter/ctmf.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/skimage/filter/ctmf.py b/skimage/filter/ctmf.py index f8c5c8be..b36b56a1 100644 --- a/skimage/filter/ctmf.py +++ b/skimage/filter/ctmf.py @@ -12,6 +12,7 @@ All rights reserved. Original author: Lee Kamentsky """ +import warnings import numpy as np from . import _ctmf from ._rank_order import rank_order @@ -62,16 +63,19 @@ def median_filter(image, radius=2, mask=None, percent=50): mask = np.ascontiguousarray(mask, dtype=np.bool) if np.all(~ mask): + warnings.warn('Mask is all over image! Returning copy of input image.') return image.copy() # - # Normalize the ranked image to 0-255 + # Some manipulation to handle float images and integer values outside + # range(256). # if (not np.issubdtype(image.dtype, np.int) or np.min(image) < 0 or np.max(image) > 255): ranked_image, translation = rank_order(image[mask]) max_ranked_image = np.max(ranked_image) if max_ranked_image == 0: - return image + warnings.warn('Very particular case? Returning copy of input image.') + return image.copy() if max_ranked_image > 255: ranked_image = ranked_image * 255 // max_ranked_image was_ranked = True From 4cf8e2744a69551e483665c2d922c7ffff43647c Mon Sep 17 00:00:00 2001 From: Marianne Corvellec Date: Sat, 29 Jun 2013 17:55:54 -0400 Subject: [PATCH 3/9] PEP8 compliance! --- skimage/filter/ctmf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/skimage/filter/ctmf.py b/skimage/filter/ctmf.py index b36b56a1..1ff261ce 100644 --- a/skimage/filter/ctmf.py +++ b/skimage/filter/ctmf.py @@ -74,7 +74,8 @@ def median_filter(image, radius=2, mask=None, percent=50): ranked_image, translation = rank_order(image[mask]) max_ranked_image = np.max(ranked_image) if max_ranked_image == 0: - warnings.warn('Very particular case? Returning copy of input image.') + warnings.warn('Very particular case? Returning copy of input + image.') return image.copy() if max_ranked_image > 255: ranked_image = ranked_image * 255 // max_ranked_image From 2b7b472fe9bf450e4463614594b2b221fe914ee9 Mon Sep 17 00:00:00 2001 From: Marianne Corvellec Date: Sat, 29 Jun 2013 18:10:38 -0400 Subject: [PATCH 4/9] One line. --- skimage/filter/ctmf.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/skimage/filter/ctmf.py b/skimage/filter/ctmf.py index 1ff261ce..06311286 100644 --- a/skimage/filter/ctmf.py +++ b/skimage/filter/ctmf.py @@ -74,8 +74,7 @@ def median_filter(image, radius=2, mask=None, percent=50): ranked_image, translation = rank_order(image[mask]) max_ranked_image = np.max(ranked_image) if max_ranked_image == 0: - warnings.warn('Very particular case? Returning copy of input - image.') + warnings.warn('Particular case? Returning copy of input image.') return image.copy() if max_ranked_image > 255: ranked_image = ranked_image * 255 // max_ranked_image From e836f07a707b9f70f2c5a5401472b2921f2b0a8e Mon Sep 17 00:00:00 2001 From: Marianne Corvellec Date: Sun, 30 Jun 2013 22:39:29 -0400 Subject: [PATCH 5/9] Changed variable names so they actually make sense (followed Tony's suggestion). --- skimage/filter/ctmf.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/skimage/filter/ctmf.py b/skimage/filter/ctmf.py index 06311286..d7f0e979 100644 --- a/skimage/filter/ctmf.py +++ b/skimage/filter/ctmf.py @@ -71,33 +71,33 @@ def median_filter(image, radius=2, mask=None, percent=50): # if (not np.issubdtype(image.dtype, np.int) or np.min(image) < 0 or np.max(image) > 255): - ranked_image, translation = rank_order(image[mask]) - max_ranked_image = np.max(ranked_image) - if max_ranked_image == 0: + ranked_values, translation = rank_order(image[mask]) + max_ranked_values = np.max(ranked_values) + if max_ranked_values == 0: warnings.warn('Particular case? Returning copy of input image.') return image.copy() - if max_ranked_image > 255: - ranked_image = ranked_image * 255 // max_ranked_image + if max_ranked_values > 255: + ranked_values = ranked_values * 255 // max_ranked_values was_ranked = True else: - ranked_image = image[mask] + ranked_values = image[mask] was_ranked = False - input_ = np.zeros(image.shape, np.uint8) - input_[mask] = ranked_image + ranked_image = np.zeros(image.shape, np.uint8) + ranked_image[mask] = ranked_values mask.dtype = np.uint8 output = np.zeros(image.shape, np.uint8) - _ctmf.median_filter(input_, mask, output, radius, percent) + _ctmf.median_filter(ranked_image, mask, output, radius, percent) if was_ranked: # # The translation gives the original value at each ranking. # We rescale the output to the original ranking and then # use the translation to look up the original value in the image. # - if max_ranked_image > 255: + if max_ranked_values > 255: result = translation[output.astype(np.uint32) * - max_ranked_image // 255] + max_ranked_values // 255] else: result = translation[output] else: From 1b1cbe246ec183b5328a5c41e3e5281c811741a4 Mon Sep 17 00:00:00 2001 From: Marianne Corvellec Date: Sat, 5 Oct 2013 16:28:27 -0400 Subject: [PATCH 6/9] Tiny formatting edits --- skimage/filter/ctmf.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/skimage/filter/ctmf.py b/skimage/filter/ctmf.py index d7f0e979..1763e536 100644 --- a/skimage/filter/ctmf.py +++ b/skimage/filter/ctmf.py @@ -23,7 +23,7 @@ def median_filter(image, radius=2, mask=None, percent=50): Parameters ---------- - image : (M,N) ndarray + image : (M, N) ndarray Input image. radius : int Radius (in pixels) of a circle inscribed into the filtering @@ -38,7 +38,7 @@ def median_filter(image, radius=2, mask=None, percent=50): Returns ------- - out : (M,N) ndarray + out : (M, N) ndarray Filtered array. In areas where the median filter does not overlap the mask, the filtered result is undefined, but in practice, it will be the lowest value in the valid area. @@ -103,3 +103,4 @@ def median_filter(image, radius=2, mask=None, percent=50): else: result = output return result + From e49a2168f0b23cbb41a3393bc806d838d5a6d634 Mon Sep 17 00:00:00 2001 From: Marianne Corvellec Date: Sat, 12 Oct 2013 20:25:56 -0400 Subject: [PATCH 7/9] Added deprecation decorator according to discussion. --- skimage/filter/ctmf.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/skimage/filter/ctmf.py b/skimage/filter/ctmf.py index 1763e536..e39aa8bc 100644 --- a/skimage/filter/ctmf.py +++ b/skimage/filter/ctmf.py @@ -1,5 +1,4 @@ -""" -ctmf.py - constant time per pixel median filtering with an octagonal shape +"""ctmf.py - constant time per pixel median filtering with an octagonal shape Reference: S. Perreault and P. Hebert, "Median Filtering in Constant Time", IEEE Transactions on Image Processing, September 2007. @@ -16,8 +15,10 @@ import warnings import numpy as np from . import _ctmf from ._rank_order import rank_order +from .._shared.utils import deprecated +@deprecated('filter.rank.median') def median_filter(image, radius=2, mask=None, percent=50): """Masked median filter with octagon shape. @@ -28,7 +29,7 @@ def median_filter(image, radius=2, mask=None, percent=50): radius : int Radius (in pixels) of a circle inscribed into the filtering octagon. Must be at least 2. Default radius is 2. - mask : (M,N) ndarray + mask : (M, N) ndarray Mask with 1's for significant pixels, 0's for masked pixels. By default, all pixels are considered significant. percent : int @@ -43,6 +44,11 @@ def median_filter(image, radius=2, mask=None, percent=50): not overlap the mask, the filtered result is undefined, but in practice, it will be the lowest value in the valid area. + Notes + ----- + Because of the histogram implementation, the number of unique values + for the output is limited to 256. + Examples -------- >>> a = np.ones((5, 5)) @@ -65,10 +71,7 @@ def median_filter(image, radius=2, mask=None, percent=50): if np.all(~ mask): warnings.warn('Mask is all over image! Returning copy of input image.') return image.copy() - # - # Some manipulation to handle float images and integer values outside - # range(256). - # + if (not np.issubdtype(image.dtype, np.int) or np.min(image) < 0 or np.max(image) > 255): ranked_values, translation = rank_order(image[mask]) From d2d4b12e1bb4ce31a4a6270502259ce3a791bf7d Mon Sep 17 00:00:00 2001 From: Marianne Corvellec Date: Mon, 14 Oct 2013 11:31:04 -0400 Subject: [PATCH 8/9] Added item to to-do list for version 0.10 --- TODO.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TODO.txt b/TODO.txt index 9bc05571..cdf50ba0 100644 --- a/TODO.txt +++ b/TODO.txt @@ -8,7 +8,8 @@ Version 0.10 depending on which optional dependencies are available. * Remove deprecated `out` parameter of `skimage.morphology.binary_*` * Remove deprecated parameter `depth` in `skimage.segmentation.random_walker` -* Remove deprecated logger function in ``skimage/__init__.py`` +* Remove deprecated logger function in `skimage/__init__.py` +* Remove deprecated function `filter.rank.median` Version 0.9 ----------- From 3122c35d3bab960d45329d23783ae8e656ed2696 Mon Sep 17 00:00:00 2001 From: Marianne Corvellec Date: Mon, 14 Oct 2013 11:38:47 -0400 Subject: [PATCH 9/9] Oops! Typed one instead of the other again! Fixed. --- TODO.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO.txt b/TODO.txt index cdf50ba0..d62c1c09 100644 --- a/TODO.txt +++ b/TODO.txt @@ -9,7 +9,7 @@ Version 0.10 * Remove deprecated `out` parameter of `skimage.morphology.binary_*` * Remove deprecated parameter `depth` in `skimage.segmentation.random_walker` * Remove deprecated logger function in `skimage/__init__.py` -* Remove deprecated function `filter.rank.median` +* Remove deprecated function `filter.median_filter` Version 0.9 -----------