Merge pull request #977 from ahojnnes/todo-0.10

Fixes from TODO for 0.10
This commit is contained in:
Stefan van der Walt
2014-04-12 18:13:41 +02:00
18 changed files with 30 additions and 414 deletions
-10
View File
@@ -9,13 +9,3 @@ Version 0.11
`skimage.transform.PolynomialTransform._params`,
`skimage.transform.PiecewiseAffineTransform.affines_*` attributes
* Remove deprecated functions `skimage.filter.denoise_*`
Version 0.10
------------
* Remove backwards-compatability of `skimage.measure.regionprops`
* Remove deprecated logger function in `skimage/__init__.py`
* Remove deprecated function `filter.median_filter`
* Enable doctests of experimental `skimage.feature.brief`
* Remove deprecated `skimage.segmentation.visualize_boundaries`
* Remove deprecated `skimage.morphology.greyscale_*`
* Remove deprecated `skimage.exposure.equalize`
+7 -23
View File
@@ -60,11 +60,6 @@ Noise removal
Some noise is added to the image, 1% of pixels are randomly set to 255, 1% are
randomly set to 0. The **median** filter is applied to remove the noise.
.. note::
There are different implementations of median filter:
`skimage.filter.median_filter` and `skimage.filter.rank.median`
"""
from skimage.filter.rank import median
@@ -193,7 +188,7 @@ from skimage.filter import rank
noisy_image = img_as_ubyte(data.camera())
# equalize globally and locally
glob = exposure.equalize(noisy_image) * 255
glob = exposure.equalize_hist(noisy_image) * 255
loc = rank.equalize(noisy_image, disk(20))
# extract histogram for each image
@@ -554,7 +549,6 @@ from time import time
from scipy.ndimage.filters import percentile_filter
from skimage.morphology import dilation
from skimage.filter import median_filter
from skimage.filter.rank import median, maximum
@@ -584,11 +578,6 @@ def cm_dil(image, selem):
return dilation(image=image, selem=selem)
@exec_and_timeit
def ctmf_med(image, radius):
return median_filter(image=image, radius=radius)
@exec_and_timeit
def ndi_med(image, n):
return percentile_filter(image, 50, size=n * 2 - 1)
@@ -659,7 +648,6 @@ ax.legend(['filter.rank.maximum', 'morphology.dilate'])
Comparison between:
* `filter.rank.median`
* `filter.median_filter`
* `scipy.ndimage.percentile`
on increasing structuring element size:
@@ -673,17 +661,15 @@ e_range = range(2, 30, 4)
for r in e_range:
elem = disk(r + 1)
rc, ms_rc = cr_med(a, elem)
rctmf, ms_rctmf = ctmf_med(a, r)
rndi, ms_ndi = ndi_med(a, r)
rec.append((ms_rc, ms_rctmf, ms_ndi))
rec.append((ms_rc, ms_ndi))
rec = np.asarray(rec)
fig, ax = plt.subplots()
ax.set_title('Performance with respect to element size')
ax.plot(e_range, rec)
ax.legend(['filter.rank.median', 'filter.median_filter',
'scipy.ndimage.percentile'])
ax.legend(['filter.rank.median', 'scipy.ndimage.percentile'])
ax.set_ylabel('Time (ms)')
ax.set_xlabel('Element radius')
@@ -695,8 +681,8 @@ Comparison of outcome of the three methods:
"""
fig, ax = plt.subplots()
ax.imshow(np.hstack((rc, rctmf, rndi)))
ax.set_title('filter.rank.median vs filtermedian_filter vs scipy.ndimage.percentile')
ax.imshow(np.hstack((rc, rndi)))
ax.set_title('filter.rank.median vs. scipy.ndimage.percentile')
ax.axis('off')
"""
@@ -714,17 +700,15 @@ s_range = [100, 200, 500, 1000]
for s in s_range:
a = (np.random.random((s, s)) * 256).astype(np.uint8)
(rc, ms_rc) = cr_med(a, elem)
rctmf, ms_rctmf = ctmf_med(a, r)
rndi, ms_ndi = ndi_med(a, r)
rec.append((ms_rc, ms_rctmf, ms_ndi))
rec.append((ms_rc, ms_ndi))
rec = np.asarray(rec)
fig, ax = plt.subplots()
ax.set_title('Performance with respect to image size')
ax.plot(s_range, rec)
ax.legend(['filter.rank.median', 'filter.median_filter',
'scipy.ndimage.percentile'])
ax.legend(['filter.rank.median', 'scipy.ndimage.percentile'])
ax.set_ylabel('Time (ms)')
ax.set_xlabel('Image size')
+1 -1
View File
@@ -72,7 +72,7 @@ def gaussian_weights(window_ext, sigma=1):
def match_corner(coord, window_ext=5):
r, c = np.round(coord)
r, c = np.round(coord).astype(np.intp)
window_orig = img_orig[r-window_ext:r+window_ext+1,
c-window_ext:c+window_ext+1, :]
-26
View File
@@ -168,30 +168,4 @@ class _FakeLog(object):
pass
@_deprecated()
def get_log(name=None):
"""Return a console logger.
Output may be sent to the logger using the `debug`, `info`, `warning`,
`error` and `critical` methods.
Parameters
----------
name : str
Name of the log.
References
----------
.. [1] Logging facility for Python,
http://docs.python.org/library/logging.html
"""
if name is None:
name = 'skimage'
else:
name = 'skimage.' + name
return _FakeLog(name)
from .util.dtype import *
+1 -2
View File
@@ -1,4 +1,4 @@
from .exposure import histogram, equalize, equalize_hist, \
from .exposure import histogram, equalize_hist, \
rescale_intensity, cumulative_distribution, \
adjust_gamma, adjust_sigmoid, adjust_log
@@ -6,7 +6,6 @@ from ._adapthist import equalize_adapthist
__all__ = ['histogram',
'equalize',
'equalize_hist',
'equalize_adapthist',
'rescale_intensity',
+2 -1
View File
@@ -315,7 +315,8 @@ def interpolate(image, xslice, yslice,
np.arange(yslice.size))
x_inv_coef, y_inv_coef = x_coef[:, ::-1] + 1, y_coef[::-1] + 1
view = image[yslice[0]: yslice[-1] + 1, xslice[0]: xslice[-1] + 1]
view = image[int(yslice[0]):int(yslice[-1] + 1),
int(xslice[0]):int(xslice[-1] + 1)]
im_slice = aLUT[view]
new = ((y_inv_coef * (x_inv_coef * mapLU[im_slice]
+ x_coef * mapRU[im_slice])
-5
View File
@@ -105,11 +105,6 @@ def cumulative_distribution(image, nbins=256):
return img_cdf, bin_centers
@deprecated('equalize_hist')
def equalize(image, nbins=256):
return equalize_hist(image, nbins)
def equalize_hist(image, nbins=256):
"""Return image after histogram equalization.
-2
View File
@@ -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',
-109
View File
@@ -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
-127
View File
@@ -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()
+11 -40
View File
@@ -4,8 +4,6 @@ from math import sqrt, atan2, pi as PI
import numpy as np
from scipy import ndimage
from collections import MutableMapping
from skimage.morphology import convex_hull_image, label
from skimage.measure import _moments
@@ -107,16 +105,15 @@ class _cached_property(object):
return value
class _RegionProperties(MutableMapping):
class _RegionProperties(object):
def __init__(self, slice, label, label_image, intensity_image,
cache_active, properties=None):
cache_active):
self.label = label
self._slice = slice
self._label_image = label_image
self._intensity_image = intensity_image
self._cache_active = cache_active
self._properties = properties
@_cached_property
def area(self):
@@ -306,27 +303,14 @@ class _RegionProperties(MutableMapping):
def weighted_moments_normalized(self):
return _moments.moments_normalized(self.weighted_moments_central, 3)
# Preserve dictionary interface
def __delitem__(self, key):
pass
def __len__(self):
return len(self._properties or PROPS.values())
def __setitem__(self, key, value):
raise RuntimeError("Cannot assign region properties.")
def __iter__(self):
return iter(self._properties or PROPS.values())
return iter(PROPS.values())
def __getitem__(self, key):
value = getattr(self, key, None)
if value is not None:
return value
else: # backwards compatability
warnings.warn('Usage of deprecated property name.',
category=DeprecationWarning)
return getattr(self, PROPS[key])
def __eq__(self, other):
@@ -344,22 +328,15 @@ class _RegionProperties(MutableMapping):
return True
def regionprops(label_image, properties=None,
intensity_image=None, cache=True):
"""Measure properties of labelled image regions.
def regionprops(label_image, intensity_image=None, cache=True):
"""Measure properties of labeled image regions.
Parameters
----------
label_image : (N, M) ndarray
Labelled input image.
properties : {'all', list}
**Deprecated parameter**
This parameter is not needed any more since all properties are
determined dynamically.
Labeled input image.
intensity_image : (N, M) ndarray, optional
Intensity image with same size as labelled image. Default is None.
Intensity image with same size as labeled image. Default is None.
cache : bool, optional
Determine whether to cache calculated properties. The computation is
much faster for cached properties, whereas the memory consumption
@@ -500,9 +477,9 @@ def regionprops(label_image, properties=None,
>>> img = util.img_as_ubyte(data.coins()) > 110
>>> label_img = label(img)
>>> props = regionprops(label_img)
>>> props[0].centroid # centroid of first labelled object
>>> props[0].centroid # centroid of first labeled object
(22.729879860483141, 81.912285234465827)
>>> props[0]['centroid'] # centroid of first labelled object
>>> props[0]['centroid'] # centroid of first labeled object
(22.729879860483141, 81.912285234465827)
"""
@@ -512,12 +489,6 @@ def regionprops(label_image, properties=None,
if label_image.ndim != 2:
raise TypeError('Only 2-D images supported.')
if properties is not None:
warnings.warn('The ``properties`` argument is deprecated and is '
'not needed any more as properties are '
'determined dynamically.',
category=DeprecationWarning)
regions = []
objects = ndimage.find_objects(label_image)
@@ -527,8 +498,8 @@ def regionprops(label_image, properties=None,
label = i + 1
props = _RegionProperties(sl, label, label_image,
intensity_image, cache, properties=properties)
props = _RegionProperties(sl, label, label_image, intensity_image,
cache)
regions.append(props)
return regions
+2 -16
View File
@@ -23,9 +23,9 @@ INTENSITY_SAMPLE[1, 9:11] = 2
def test_all_props():
regions = regionprops(SAMPLE, 'all', INTENSITY_SAMPLE)[0]
region = regionprops(SAMPLE, INTENSITY_SAMPLE)[0]
for prop in PROPS:
regions[prop]
assert_equal(region[prop], getattr(region, PROPS[prop]))
def test_dtype():
@@ -336,20 +336,6 @@ def test_weighted_moments_normalized():
assert_array_almost_equal(wnu, ref)
def test_old_dict_interface():
feats = regionprops(SAMPLE,
['Area', 'Eccentricity', 'EulerNumber',
'Extent', 'MinIntensity', 'MeanIntensity',
'MaxIntensity', 'Solidity'],
intensity_image=INTENSITY_SAMPLE)
np.array([list(props.values()) for props in feats], np.float)
assert_equal(len(feats[0]), 8)
def assign():
feats[0]['Area'] = 0
assert_raises(RuntimeError, assign)
def test_label_sequence():
a = np.empty((2, 2), dtype=np.int)
a[:, :] = 2
+1 -9
View File
@@ -1,9 +1,7 @@
from .binary import (binary_erosion, binary_dilation, binary_opening,
binary_closing)
from .grey import (erosion, dilation, opening, closing, white_tophat,
black_tophat, greyscale_erode, greyscale_dilate,
greyscale_open, greyscale_close, greyscale_white_top_hat,
greyscale_black_top_hat)
black_tophat)
from .selem import (square, rectangle, diamond, disk, cube, octahedron, ball,
octagon, star)
from .ccomp import label
@@ -24,12 +22,6 @@ __all__ = ['binary_erosion',
'closing',
'white_tophat',
'black_tophat',
'greyscale_erode',
'greyscale_dilate',
'greyscale_open',
'greyscale_close',
'greyscale_white_top_hat',
'greyscale_black_top_hat',
'square',
'rectangle',
'diamond',
+1 -33
View File
@@ -5,9 +5,7 @@ from . import cmorph
__all__ = ['erosion', 'dilation', 'opening', 'closing', 'white_tophat',
'black_tophat', 'greyscale_erode', 'greyscale_dilate',
'greyscale_open', 'greyscale_close', 'greyscale_white_top_hat',
'greyscale_black_top_hat']
'black_tophat']
def erosion(image, selem, out=None, shift_x=False, shift_y=False):
@@ -313,33 +311,3 @@ def black_tophat(image, selem, out=None):
out = closing(image, selem, out=out)
out = out - image
return out
def greyscale_erode(*args, **kwargs):
warnings.warn("`greyscale_erode` renamed `erosion`.")
return erosion(*args, **kwargs)
def greyscale_dilate(*args, **kwargs):
warnings.warn("`greyscale_dilate` renamed `dilation`.")
return dilation(*args, **kwargs)
def greyscale_open(*args, **kwargs):
warnings.warn("`greyscale_open` renamed `opening`.")
return opening(*args, **kwargs)
def greyscale_close(*args, **kwargs):
warnings.warn("`greyscale_close` renamed `closing`.")
return closing(*args, **kwargs)
def greyscale_white_top_hat(*args, **kwargs):
warnings.warn("`greyscale_white_top_hat` renamed `white_tophat`.")
return white_tophat(*args, **kwargs)
def greyscale_black_top_hat(*args, **kwargs):
warnings.warn("`greyscale_black_top_hat` renamed `black_tophat`.")
return black_tophat(*args, **kwargs)
+1 -1
View File
@@ -226,7 +226,7 @@ class Picture(object):
Get the bottom-left pixel
>>> pic[0, 0]
Pixel(red=255, green=0, blue=0)
Pixel(red=255, green=0, blue=0, alpha=255)
Get the top row of the picture
>>> pic[:, pic.height-1]
+1 -2
View File
@@ -2,7 +2,7 @@ from .random_walker_segmentation import random_walker
from ._felzenszwalb import felzenszwalb
from .slic_superpixels import slic
from ._quickshift import quickshift
from .boundaries import find_boundaries, visualize_boundaries, mark_boundaries
from .boundaries import find_boundaries, mark_boundaries
from ._clear_border import clear_border
from ._join import join_segmentations, relabel_from_one, relabel_sequential
@@ -12,7 +12,6 @@ __all__ = ['random_walker',
'slic',
'quickshift',
'find_boundaries',
'visualize_boundaries',
'mark_boundaries',
'clear_border',
'join_segmentations',
-5
View File
@@ -38,8 +38,3 @@ def mark_boundaries(image, label_img, color=(1, 1, 0), outline_color=(0, 0, 0)):
image[outer_boundaries != 0, :] = np.array(outline_color)
image[boundaries, :] = np.array(color)
return image
@deprecated('mark_boundaries')
def visualize_boundaries(*args, **kwargs):
return mark_boundaries(*args, **kwargs)
+2 -2
View File
@@ -72,8 +72,8 @@ def hough_line_peaks(hspace, angles, dists, min_distance=9, min_angle=10,
hspace_t = hspace > threshold
label_hspace = morphology.label(hspace_t)
props = measure.regionprops(label_hspace, ['Centroid'])
coords = np.array([np.round(p['Centroid']) for p in props], dtype=int)
props = measure.regionprops(label_hspace)
coords = np.array([np.round(p.centroid) for p in props], dtype=int)
hspace_peaks = []
dist_peaks = []