mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-16 11:21:25 +08:00
Remove deprecated skimage.filter.median_filter
This commit is contained in:
@@ -14,4 +14,3 @@ Version 0.10
|
||||
------------
|
||||
* Remove backwards-compatability of `skimage.measure.regionprops`
|
||||
* Remove deprecated logger function in `skimage/__init__.py`
|
||||
* Remove deprecated function `filter.median_filter`
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from .lpi_filter import inverse, wiener, LPIFilter2D
|
||||
from .ctmf import median_filter
|
||||
from ._gaussian import gaussian_filter
|
||||
from ._canny import canny
|
||||
from .edges import (sobel, hsobel, vsobel, scharr, hscharr, vscharr, prewitt,
|
||||
@@ -25,7 +24,6 @@ denoise_tv_chambolle = deprecated('skimage.restoration.denoise_tv_chambolle')\
|
||||
__all__ = ['inverse',
|
||||
'wiener',
|
||||
'LPIFilter2D',
|
||||
'median_filter',
|
||||
'gaussian_filter',
|
||||
'canny',
|
||||
'sobel',
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
"""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.
|
||||
|
||||
Originally part of CellProfiler, code licensed under both GPL and BSD licenses.
|
||||
Website: http://www.cellprofiler.org
|
||||
Copyright (c) 2003-2009 Massachusetts Institute of Technology
|
||||
Copyright (c) 2009-2011 Broad Institute
|
||||
All rights reserved.
|
||||
Original author: Lee Kamentsky
|
||||
"""
|
||||
|
||||
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.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : (M, N) ndarray
|
||||
Input image.
|
||||
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 `percent` percent of the index range is chosen.
|
||||
Default value of 50 gives the median pixel.
|
||||
|
||||
Returns
|
||||
-------
|
||||
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.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Because of the histogram implementation, the number of unique values
|
||||
for the output is limited to 256.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> a = np.ones((5, 5))
|
||||
>>> a[2, 2] = 10 # introduce outlier
|
||||
>>> b = median_filter(a)
|
||||
>>> b[2, 2] # the median filter is good at removing outliers
|
||||
1.0
|
||||
"""
|
||||
|
||||
if image.ndim != 2:
|
||||
raise TypeError("Input 'image' must be a two-dimensional array.")
|
||||
|
||||
if radius < 2:
|
||||
raise ValueError("Input 'radius' must be >= 2.")
|
||||
|
||||
if mask is None:
|
||||
mask = np.ones(image.shape, dtype=np.bool)
|
||||
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()
|
||||
|
||||
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])
|
||||
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_values > 255:
|
||||
ranked_values = ranked_values * 255 // max_ranked_values
|
||||
was_ranked = True
|
||||
else:
|
||||
ranked_values = image[mask]
|
||||
was_ranked = False
|
||||
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(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_values > 255:
|
||||
result = translation[output.astype(np.uint32) *
|
||||
max_ranked_values // 255]
|
||||
else:
|
||||
result = translation[output]
|
||||
else:
|
||||
result = output
|
||||
return result
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
import numpy as np
|
||||
from nose.tools import raises
|
||||
|
||||
from skimage.filter import median_filter
|
||||
|
||||
|
||||
def test_00_00_zeros():
|
||||
'''The median filter on an array of all zeros should be zero'''
|
||||
result = median_filter(np.zeros((10, 10)), 3, np.ones((10, 10), bool))
|
||||
assert np.all(result == 0)
|
||||
|
||||
|
||||
def test_00_01_all_masked():
|
||||
'''Test a completely masked image
|
||||
|
||||
Regression test of IMG-1029'''
|
||||
result = median_filter(np.zeros((10, 10)), 3, np.zeros((10, 10), bool))
|
||||
assert (np.all(result == 0))
|
||||
|
||||
|
||||
def test_00_02_all_but_one_masked():
|
||||
mask = np.zeros((10, 10), bool)
|
||||
mask[5, 5] = True
|
||||
median_filter(np.zeros((10, 10)), 3, mask)
|
||||
|
||||
|
||||
def test_01_01_mask():
|
||||
'''The median filter, masking a single value'''
|
||||
img = np.zeros((10, 10))
|
||||
img[5, 5] = 1
|
||||
mask = np.ones((10, 10), bool)
|
||||
mask[5, 5] = False
|
||||
result = median_filter(img, 3, mask)
|
||||
assert (np.all(result[mask] == 0))
|
||||
np.testing.assert_equal(result[5, 5], 1)
|
||||
|
||||
|
||||
def test_02_01_median():
|
||||
'''A median filter larger than the image = median of image'''
|
||||
np.random.seed(0)
|
||||
img = np.random.uniform(size=(9, 9))
|
||||
result = median_filter(img, 20, np.ones((9, 9), bool))
|
||||
np.testing.assert_equal(result[0, 0], np.median(img))
|
||||
assert (np.all(result == np.median(img)))
|
||||
|
||||
|
||||
def test_02_02_median_bigger():
|
||||
'''Use an image of more than 255 values to test approximation'''
|
||||
np.random.seed(0)
|
||||
img = np.random.uniform(size=(20, 20))
|
||||
result = median_filter(img, 40, np.ones((20, 20), bool))
|
||||
sorted = np.ravel(img)
|
||||
sorted.sort()
|
||||
min_acceptable = sorted[198]
|
||||
max_acceptable = sorted[202]
|
||||
assert (np.all(result >= min_acceptable))
|
||||
assert (np.all(result <= max_acceptable))
|
||||
|
||||
|
||||
def test_03_01_shape():
|
||||
'''Make sure the median filter is the expected octagonal shape'''
|
||||
|
||||
radius = 5
|
||||
a_2 = int(radius / 2.414213)
|
||||
i, j = np.mgrid[-10:11, -10:11]
|
||||
octagon = np.ones((21, 21), bool)
|
||||
#
|
||||
# constrain the octagon mask to be the points that are on
|
||||
# the correct side of the 8 edges
|
||||
#
|
||||
octagon[i < -radius] = False
|
||||
octagon[i > radius] = False
|
||||
octagon[j < -radius] = False
|
||||
octagon[j > radius] = False
|
||||
octagon[i + j < -radius - a_2] = False
|
||||
octagon[j - i > radius + a_2] = False
|
||||
octagon[i + j > radius + a_2] = False
|
||||
octagon[i - j > radius + a_2] = False
|
||||
np.random.seed(0)
|
||||
img = np.random.uniform(size=(21, 21))
|
||||
result = median_filter(img, radius, np.ones((21, 21), bool))
|
||||
sorted = img[octagon]
|
||||
sorted.sort()
|
||||
min_acceptable = sorted[len(sorted) / 2 - 1]
|
||||
max_acceptable = sorted[len(sorted) / 2 + 1]
|
||||
assert (result[10, 10] >= min_acceptable)
|
||||
assert (result[10, 10] <= max_acceptable)
|
||||
|
||||
|
||||
def test_04_01_half_masked():
|
||||
'''Make sure that the median filter can handle large masked areas.'''
|
||||
img = np.ones((20, 20))
|
||||
mask = np.ones((20, 20), bool)
|
||||
mask[10:, :] = False
|
||||
img[~ mask] = 2
|
||||
img[1, 1] = 0 # to prevent short circuit for uniform data.
|
||||
result = median_filter(img, 5, mask)
|
||||
# in partial coverage areas, the result should be only
|
||||
# from the masked pixels
|
||||
assert (np.all(result[:14, :] == 1))
|
||||
# in zero coverage areas, the result should be the lowest
|
||||
# value in the valid area
|
||||
assert (np.all(result[15:, :] == np.min(img[mask])))
|
||||
|
||||
|
||||
def test_default_values():
|
||||
img = (np.random.random((20, 20)) * 255).astype(np.uint8)
|
||||
mask = np.ones((20, 20), dtype=np.uint8)
|
||||
result1 = median_filter(img, radius=2, mask=mask, percent=50)
|
||||
result2 = median_filter(img)
|
||||
np.testing.assert_array_equal(result1, result2)
|
||||
|
||||
|
||||
@raises(ValueError)
|
||||
def test_insufficient_size():
|
||||
img = (np.random.random((20, 20)) * 255).astype(np.uint8)
|
||||
median_filter(img, radius=1)
|
||||
|
||||
|
||||
@raises(TypeError)
|
||||
def test_wrong_shape():
|
||||
img = np.empty((10, 10, 3))
|
||||
median_filter(img)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
np.testing.run_module_suite()
|
||||
Reference in New Issue
Block a user