import numpy as np
from scipy import spatial, ndimage as ndi
from skimage import filters, restoration
from skimage.filters import threshold_sauvola, threshold_niblack
SMOOTH_METHODS = {
'Gaussian': filters.gaussian,
'TV': restoration.denoise_tv_bregman,
}
[docs]
def hyperball(ndim, radius):
"""Return a binary morphological filter containing pixels within `radius`.
Parameters
----------
ndim : int
The number of dimensions of the filter.
radius : int
The radius of the filter.
Returns
-------
ball : array of bool, shape [2 * radius + 1,] * ndim
The required structural element
"""
size = 2*radius + 1
center = [(radius,) * ndim]
coords = np.mgrid[[
slice(None, size),
] * ndim].reshape(ndim, -1).T
distances = np.ravel(spatial.distance_matrix(coords, center))
selector = distances <= radius
ball = np.zeros((size,) * ndim, dtype=bool)
ball.ravel()[selector] = True
return ball
[docs]
def threshold(
image,
*,
sigma=0.,
radius=0,
offset=0.,
method='sauvola',
smooth_method='Gaussian'
):
"""Use scikit-image filters to "intelligently" threshold an image.
Parameters
----------
image : array, shape (M, N, ...[, 3])
Input image, conformant with scikit-image data type
specification [1]_.
sigma : float, optional
If positive, use Gaussian filtering to smooth the image before
thresholding.
radius : int, optional
If given, use local median thresholding instead of global.
offset : float, optional
If given, reduce the threshold by this amount. Higher values
result in fewer pixels above the threshold.
method: {'sauvola', 'niblack', 'median'}
Which method to use for thresholding. Sauvola is 100x faster, but
median might be more accurate.
smooth_method: {'Gaussian', 'TV'}
Which method to use for smoothing. Choose from Gaussian smoothing
and total variation denoising.
Returns
-------
thresholded : image of bool, same shape as `image`
The thresholded image.
References
----------
.. [1] http://scikit-image.org/docs/dev/user_guide/data_types.html
"""
if sigma > 0:
if smooth_method.lower() == 'gaussian':
image = filters.gaussian(image, sigma=sigma)
elif smooth_method.lower() == 'tv':
image = restoration.denoise_tv_bregman(image, weight=sigma)
if radius == 0:
t = filters.threshold_otsu(image) + offset
else:
if method == 'median':
footprint = hyperball(image.ndim, radius=radius)
t = ndi.median_filter(image, footprint=footprint) + offset
elif method == 'sauvola':
w = 2*radius + 1
t = threshold_sauvola(image, window_size=w, k=offset)
elif method == 'niblack':
w = 2*radius + 1
t = threshold_niblack(image, window_size=w, k=offset)
else:
raise ValueError(
'Unknown method %s. Valid methods are median,'
'niblack, and sauvola.' % method
)
thresholded = image > t
return thresholded