scikit-image
1339 строк · 46.8 Кб
1import inspect
2import itertools
3import math
4from collections import OrderedDict
5from collections.abc import Iterable
6
7import numpy as np
8from scipy import ndimage as ndi
9
10from .._shared.filters import gaussian
11from .._shared.utils import _supported_float_type, warn
12from .._shared.version_requirements import require
13from ..exposure import histogram
14from ..filters._multiotsu import (
15_get_multiotsu_thresh_indices,
16_get_multiotsu_thresh_indices_lut,
17)
18from ..transform import integral_image
19from ..util import dtype_limits
20from ._sparse import _correlate_sparse, _validate_window_size
21
22__all__ = [
23'try_all_threshold',
24'threshold_otsu',
25'threshold_yen',
26'threshold_isodata',
27'threshold_li',
28'threshold_local',
29'threshold_minimum',
30'threshold_mean',
31'threshold_niblack',
32'threshold_sauvola',
33'threshold_triangle',
34'apply_hysteresis_threshold',
35'threshold_multiotsu',
36]
37
38
39def _try_all(image, methods=None, figsize=None, num_cols=2, verbose=True):
40"""Returns a figure comparing the outputs of different methods.
41
42Parameters
43----------
44image : (M, N) ndarray
45Input image.
46methods : dict, optional
47Names and associated functions.
48Functions must take and return an image.
49figsize : tuple, optional
50Figure size (in inches).
51num_cols : int, optional
52Number of columns.
53verbose : bool, optional
54Print function name for each method.
55
56Returns
57-------
58fig, ax : tuple
59Matplotlib figure and axes.
60"""
61from matplotlib import pyplot as plt
62
63# Compute the image histogram for better performances
64nbins = 256 # Default in threshold functions
65hist = histogram(image.reshape(-1), nbins, source_range='image')
66
67# Handle default value
68methods = methods or {}
69
70num_rows = math.ceil((len(methods) + 1.0) / num_cols)
71fig, ax = plt.subplots(
72num_rows, num_cols, figsize=figsize, sharex=True, sharey=True
73)
74ax = ax.reshape(-1)
75
76ax[0].imshow(image, cmap=plt.cm.gray)
77ax[0].set_title('Original')
78
79i = 1
80for name, func in methods.items():
81# Use precomputed histogram for supporting functions
82sig = inspect.signature(func)
83_kwargs = dict(hist=hist) if 'hist' in sig.parameters else {}
84
85ax[i].set_title(name)
86try:
87ax[i].imshow(func(image, **_kwargs), cmap=plt.cm.gray)
88except Exception as e:
89ax[i].text(
900.5,
910.5,
92f"{type(e).__name__}",
93ha="center",
94va="center",
95transform=ax[i].transAxes,
96)
97i += 1
98if verbose:
99print(func.__orifunc__)
100
101for a in ax:
102a.axis('off')
103
104fig.tight_layout()
105return fig, ax
106
107
108@require("matplotlib", ">=3.3")
109def try_all_threshold(image, figsize=(8, 5), verbose=True):
110"""Returns a figure comparing the outputs of different thresholding methods.
111
112Parameters
113----------
114image : (M, N) ndarray
115Input image.
116figsize : tuple, optional
117Figure size (in inches).
118verbose : bool, optional
119Print function name for each method.
120
121Returns
122-------
123fig, ax : tuple
124Matplotlib figure and axes.
125
126Notes
127-----
128The following algorithms are used:
129
130* isodata
131* li
132* mean
133* minimum
134* otsu
135* triangle
136* yen
137
138Examples
139--------
140.. testsetup::
141>>> import pytest; _ = pytest.importorskip('matplotlib')
142
143>>> from skimage.data import text
144>>> fig, ax = try_all_threshold(text(), figsize=(10, 6), verbose=False)
145"""
146
147def thresh(func):
148"""
149A wrapper function to return a thresholded image.
150"""
151
152def wrapper(im):
153return im > func(im)
154
155try:
156wrapper.__orifunc__ = func.__orifunc__
157except AttributeError:
158wrapper.__orifunc__ = func.__module__ + '.' + func.__name__
159return wrapper
160
161# Global algorithms.
162methods = OrderedDict(
163{
164'Isodata': thresh(threshold_isodata),
165'Li': thresh(threshold_li),
166'Mean': thresh(threshold_mean),
167'Minimum': thresh(threshold_minimum),
168'Otsu': thresh(threshold_otsu),
169'Triangle': thresh(threshold_triangle),
170'Yen': thresh(threshold_yen),
171}
172)
173
174return _try_all(image, figsize=figsize, methods=methods, verbose=verbose)
175
176
177def threshold_local(
178image, block_size=3, method='gaussian', offset=0, mode='reflect', param=None, cval=0
179):
180"""Compute a threshold mask image based on local pixel neighborhood.
181
182Also known as adaptive or dynamic thresholding. The threshold value is
183the weighted mean for the local neighborhood of a pixel subtracted by a
184constant. Alternatively the threshold can be determined dynamically by a
185given function, using the 'generic' method.
186
187Parameters
188----------
189image : (M, N[, ...]) ndarray
190Grayscale input image.
191block_size : int or sequence of int
192Odd size of pixel neighborhood which is used to calculate the
193threshold value (e.g. 3, 5, 7, ..., 21, ...).
194method : {'generic', 'gaussian', 'mean', 'median'}, optional
195Method used to determine adaptive threshold for local neighborhood in
196weighted mean image.
197
198* 'generic': use custom function (see ``param`` parameter)
199* 'gaussian': apply gaussian filter (see ``param`` parameter for custom\
200sigma value)
201* 'mean': apply arithmetic mean filter
202* 'median': apply median rank filter
203
204By default, the 'gaussian' method is used.
205offset : float, optional
206Constant subtracted from weighted mean of neighborhood to calculate
207the local threshold value. Default offset is 0.
208mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
209The mode parameter determines how the array borders are handled, where
210cval is the value when mode is equal to 'constant'.
211Default is 'reflect'.
212param : {int, function}, optional
213Either specify sigma for 'gaussian' method or function object for
214'generic' method. This functions takes the flat array of local
215neighborhood as a single argument and returns the calculated
216threshold for the centre pixel.
217cval : float, optional
218Value to fill past edges of input if mode is 'constant'.
219
220Returns
221-------
222threshold : (M, N[, ...]) ndarray
223Threshold image. All pixels in the input image higher than the
224corresponding pixel in the threshold image are considered foreground.
225
226References
227----------
228.. [1] Gonzalez, R. C. and Wood, R. E. "Digital Image Processing
229(2nd Edition)." Prentice-Hall Inc., 2002: 600--612.
230ISBN: 0-201-18075-8
231
232Examples
233--------
234>>> from skimage.data import camera
235>>> image = camera()[:50, :50]
236>>> binary_image1 = image > threshold_local(image, 15, 'mean')
237>>> func = lambda arr: arr.mean()
238>>> binary_image2 = image > threshold_local(image, 15, 'generic',
239... param=func)
240
241"""
242
243if np.isscalar(block_size):
244block_size = (block_size,) * image.ndim
245elif len(block_size) != image.ndim:
246raise ValueError("len(block_size) must equal image.ndim.")
247block_size = tuple(block_size)
248if any(b % 2 == 0 for b in block_size):
249raise ValueError(
250f'block_size must be odd! Given block_size '
251f'{block_size} contains even values.'
252)
253float_dtype = _supported_float_type(image.dtype)
254image = image.astype(float_dtype, copy=False)
255thresh_image = np.zeros(image.shape, dtype=float_dtype)
256if method == 'generic':
257ndi.generic_filter(
258image, param, block_size, output=thresh_image, mode=mode, cval=cval
259)
260elif method == 'gaussian':
261if param is None:
262# automatically determine sigma which covers > 99% of distribution
263sigma = tuple([(b - 1) / 6.0 for b in block_size])
264else:
265sigma = param
266gaussian(image, sigma=sigma, out=thresh_image, mode=mode, cval=cval)
267elif method == 'mean':
268ndi.uniform_filter(image, block_size, output=thresh_image, mode=mode, cval=cval)
269elif method == 'median':
270ndi.median_filter(image, block_size, output=thresh_image, mode=mode, cval=cval)
271else:
272raise ValueError(
273"Invalid method specified. Please use `generic`, "
274"`gaussian`, `mean`, or `median`."
275)
276
277return thresh_image - offset
278
279
280def _validate_image_histogram(image, hist, nbins=None, normalize=False):
281"""Ensure that either image or hist were given, return valid histogram.
282
283If hist is given, image is ignored.
284
285Parameters
286----------
287image : array or None
288Grayscale image.
289hist : array, 2-tuple of array, or None
290Histogram, either a 1D counts array, or an array of counts together
291with an array of bin centers.
292nbins : int, optional
293The number of bins with which to compute the histogram, if `hist` is
294None.
295normalize : bool
296If hist is not given, it will be computed by this function. This
297parameter determines whether the computed histogram is normalized
298(i.e. entries sum up to 1) or not.
299
300Returns
301-------
302counts : 1D array of float
303Each element is the number of pixels falling in each intensity bin.
304bin_centers : 1D array
305Each element is the value corresponding to the center of each intensity
306bin.
307
308Raises
309------
310ValueError : if image and hist are both None
311"""
312if image is None and hist is None:
313raise Exception("Either image or hist must be provided.")
314
315if hist is not None:
316if isinstance(hist, (tuple, list)):
317counts, bin_centers = hist
318else:
319counts = hist
320bin_centers = np.arange(counts.size)
321
322if counts[0] == 0 or counts[-1] == 0:
323# Trim histogram from both ends by removing starting and
324# ending zeroes as in histogram(..., source_range="image")
325cond = counts > 0
326start = np.argmax(cond)
327end = cond.size - np.argmax(cond[::-1])
328counts, bin_centers = counts[start:end], bin_centers[start:end]
329else:
330counts, bin_centers = histogram(
331image.reshape(-1), nbins, source_range='image', normalize=normalize
332)
333return counts.astype('float32', copy=False), bin_centers
334
335
336def threshold_otsu(image=None, nbins=256, *, hist=None):
337"""Return threshold value based on Otsu's method.
338
339Either image or hist must be provided. If hist is provided, the actual
340histogram of the image is ignored.
341
342Parameters
343----------
344image : (M, N[, ...]) ndarray, optional
345Grayscale input image.
346nbins : int, optional
347Number of bins used to calculate histogram. This value is ignored for
348integer arrays.
349hist : array, or 2-tuple of arrays, optional
350Histogram from which to determine the threshold, and optionally a
351corresponding array of bin center intensities. If no hist provided,
352this function will compute it from the image.
353
354
355Returns
356-------
357threshold : float
358Upper threshold value. All pixels with an intensity higher than
359this value are assumed to be foreground.
360
361References
362----------
363.. [1] Wikipedia, https://en.wikipedia.org/wiki/Otsu's_Method
364
365Examples
366--------
367>>> from skimage.data import camera
368>>> image = camera()
369>>> thresh = threshold_otsu(image)
370>>> binary = image <= thresh
371
372Notes
373-----
374The input image must be grayscale.
375"""
376if image is not None and image.ndim > 2 and image.shape[-1] in (3, 4):
377warn(
378f'threshold_otsu is expected to work correctly only for '
379f'grayscale images; image shape {image.shape} looks like '
380f'that of an RGB image.'
381)
382
383# Check if the image has more than one intensity value; if not, return that
384# value
385if image is not None:
386first_pixel = image.reshape(-1)[0]
387if np.all(image == first_pixel):
388return first_pixel
389
390counts, bin_centers = _validate_image_histogram(image, hist, nbins)
391
392# class probabilities for all possible thresholds
393weight1 = np.cumsum(counts)
394weight2 = np.cumsum(counts[::-1])[::-1]
395# class means for all possible thresholds
396mean1 = np.cumsum(counts * bin_centers) / weight1
397mean2 = (np.cumsum((counts * bin_centers)[::-1]) / weight2[::-1])[::-1]
398
399# Clip ends to align class 1 and class 2 variables:
400# The last value of ``weight1``/``mean1`` should pair with zero values in
401# ``weight2``/``mean2``, which do not exist.
402variance12 = weight1[:-1] * weight2[1:] * (mean1[:-1] - mean2[1:]) ** 2
403
404idx = np.argmax(variance12)
405threshold = bin_centers[idx]
406
407return threshold
408
409
410def threshold_yen(image=None, nbins=256, *, hist=None):
411"""Return threshold value based on Yen's method.
412Either image or hist must be provided. In case hist is given, the actual
413histogram of the image is ignored.
414
415Parameters
416----------
417image : (M, N[, ...]) ndarray
418Grayscale input image.
419nbins : int, optional
420Number of bins used to calculate histogram. This value is ignored for
421integer arrays.
422hist : array, or 2-tuple of arrays, optional
423Histogram from which to determine the threshold, and optionally a
424corresponding array of bin center intensities.
425An alternative use of this function is to pass it only hist.
426
427Returns
428-------
429threshold : float
430Upper threshold value. All pixels with an intensity higher than
431this value are assumed to be foreground.
432
433References
434----------
435.. [1] Yen J.C., Chang F.J., and Chang S. (1995) "A New Criterion
436for Automatic Multilevel Thresholding" IEEE Trans. on Image
437Processing, 4(3): 370-378. :DOI:`10.1109/83.366472`
438.. [2] Sezgin M. and Sankur B. (2004) "Survey over Image Thresholding
439Techniques and Quantitative Performance Evaluation" Journal of
440Electronic Imaging, 13(1): 146-165, :DOI:`10.1117/1.1631315`
441http://www.busim.ee.boun.edu.tr/~sankur/SankurFolder/Threshold_survey.pdf
442.. [3] ImageJ AutoThresholder code, http://fiji.sc/wiki/index.php/Auto_Threshold
443
444Examples
445--------
446>>> from skimage.data import camera
447>>> image = camera()
448>>> thresh = threshold_yen(image)
449>>> binary = image <= thresh
450"""
451counts, bin_centers = _validate_image_histogram(image, hist, nbins)
452
453# On blank images (e.g. filled with 0) with int dtype, `histogram()`
454# returns ``bin_centers`` containing only one value. Speed up with it.
455if bin_centers.size == 1:
456return bin_centers[0]
457
458# Calculate probability mass function
459pmf = counts.astype('float32', copy=False) / counts.sum()
460P1 = np.cumsum(pmf) # Cumulative normalized histogram
461P1_sq = np.cumsum(pmf**2)
462# Get cumsum calculated from end of squared array:
463P2_sq = np.cumsum(pmf[::-1] ** 2)[::-1]
464# P2_sq indexes is shifted +1. I assume, with P1[:-1] it's help avoid
465# '-inf' in crit. ImageJ Yen implementation replaces those values by zero.
466crit = np.log(((P1_sq[:-1] * P2_sq[1:]) ** -1) * (P1[:-1] * (1.0 - P1[:-1])) ** 2)
467return bin_centers[crit.argmax()]
468
469
470def threshold_isodata(image=None, nbins=256, return_all=False, *, hist=None):
471"""Return threshold value(s) based on ISODATA method.
472
473Histogram-based threshold, known as Ridler-Calvard method or inter-means.
474Threshold values returned satisfy the following equality::
475
476threshold = (image[image <= threshold].mean() +
477image[image > threshold].mean()) / 2.0
478
479That is, returned thresholds are intensities that separate the image into
480two groups of pixels, where the threshold intensity is midway between the
481mean intensities of these groups.
482
483For integer images, the above equality holds to within one; for floating-
484point images, the equality holds to within the histogram bin-width.
485
486Either image or hist must be provided. In case hist is given, the actual
487histogram of the image is ignored.
488
489Parameters
490----------
491image : (M, N[, ...]) ndarray
492Grayscale input image.
493nbins : int, optional
494Number of bins used to calculate histogram. This value is ignored for
495integer arrays.
496return_all : bool, optional
497If False (default), return only the lowest threshold that satisfies
498the above equality. If True, return all valid thresholds.
499hist : array, or 2-tuple of arrays, optional
500Histogram to determine the threshold from and a corresponding array
501of bin center intensities. Alternatively, only the histogram can be
502passed.
503
504Returns
505-------
506threshold : float or int or array
507Threshold value(s).
508
509References
510----------
511.. [1] Ridler, TW & Calvard, S (1978), "Picture thresholding using an
512iterative selection method"
513IEEE Transactions on Systems, Man and Cybernetics 8: 630-632,
514:DOI:`10.1109/TSMC.1978.4310039`
515.. [2] Sezgin M. and Sankur B. (2004) "Survey over Image Thresholding
516Techniques and Quantitative Performance Evaluation" Journal of
517Electronic Imaging, 13(1): 146-165,
518http://www.busim.ee.boun.edu.tr/~sankur/SankurFolder/Threshold_survey.pdf
519:DOI:`10.1117/1.1631315`
520.. [3] ImageJ AutoThresholder code,
521http://fiji.sc/wiki/index.php/Auto_Threshold
522
523Examples
524--------
525>>> from skimage.data import coins
526>>> image = coins()
527>>> thresh = threshold_isodata(image)
528>>> binary = image > thresh
529"""
530counts, bin_centers = _validate_image_histogram(image, hist, nbins)
531
532# image only contains one unique value
533if len(bin_centers) == 1:
534if return_all:
535return bin_centers
536else:
537return bin_centers[0]
538
539counts = counts.astype('float32', copy=False)
540
541# csuml and csumh contain the count of pixels in that bin or lower, and
542# in all bins strictly higher than that bin, respectively
543csuml = np.cumsum(counts)
544csumh = csuml[-1] - csuml
545
546# intensity_sum contains the total pixel intensity from each bin
547intensity_sum = counts * bin_centers
548
549# l and h contain average value of all pixels in that bin or lower, and
550# in all bins strictly higher than that bin, respectively.
551# Note that since exp.histogram does not include empty bins at the low or
552# high end of the range, csuml and csumh are strictly > 0, except in the
553# last bin of csumh, which is zero by construction.
554# So no worries about division by zero in the following lines, except
555# for the last bin, but we can ignore that because no valid threshold
556# can be in the top bin.
557# To avoid the division by zero, we simply skip over the last element in
558# all future computation.
559csum_intensity = np.cumsum(intensity_sum)
560lower = csum_intensity[:-1] / csuml[:-1]
561higher = (csum_intensity[-1] - csum_intensity[:-1]) / csumh[:-1]
562
563# isodata finds threshold values that meet the criterion t = (l + m)/2
564# where l is the mean of all pixels <= t and h is the mean of all pixels
565# > t, as calculated above. So we are looking for places where
566# (l + m) / 2 equals the intensity value for which those l and m figures
567# were calculated -- which is, of course, the histogram bin centers.
568# We only require this equality to be within the precision of the bin
569# width, of course.
570all_mean = (lower + higher) / 2.0
571bin_width = bin_centers[1] - bin_centers[0]
572
573# Look only at thresholds that are below the actual all_mean value,
574# for consistency with the threshold being included in the lower pixel
575# group. Otherwise, can get thresholds that are not actually fixed-points
576# of the isodata algorithm. For float images, this matters less, since
577# there really can't be any guarantees anymore anyway.
578distances = all_mean - bin_centers[:-1]
579thresholds = bin_centers[:-1][(distances >= 0) & (distances < bin_width)]
580
581if return_all:
582return thresholds
583else:
584return thresholds[0]
585
586
587# Computing a histogram using np.histogram on a uint8 image with bins=256
588# doesn't work and results in aliasing problems. We use a fully specified set
589# of bins to ensure that each uint8 value false into its own bin.
590_DEFAULT_ENTROPY_BINS = tuple(np.arange(-0.5, 255.51, 1))
591
592
593def _cross_entropy(image, threshold, bins=_DEFAULT_ENTROPY_BINS):
594"""Compute cross-entropy between distributions above and below a threshold.
595
596Parameters
597----------
598image : array
599The input array of values.
600threshold : float
601The value dividing the foreground and background in ``image``.
602bins : int or array of float, optional
603The number of bins or the bin edges. (Any valid value to the ``bins``
604argument of ``np.histogram`` will work here.) For an exact calculation,
605each unique value should have its own bin. The default value for bins
606ensures exact handling of uint8 images: ``bins=256`` results in
607aliasing problems due to bin width not being equal to 1.
608
609Returns
610-------
611nu : float
612The cross-entropy target value as defined in [1]_.
613
614Notes
615-----
616See Li and Lee, 1993 [1]_; this is the objective function ``threshold_li``
617minimizes. This function can be improved but this implementation most
618closely matches equation 8 in [1]_ and equations 1-3 in [2]_.
619
620References
621----------
622.. [1] Li C.H. and Lee C.K. (1993) "Minimum Cross Entropy Thresholding"
623Pattern Recognition, 26(4): 617-625
624:DOI:`10.1016/0031-3203(93)90115-D`
625.. [2] Li C.H. and Tam P.K.S. (1998) "An Iterative Algorithm for Minimum
626Cross Entropy Thresholding" Pattern Recognition Letters, 18(8): 771-776
627:DOI:`10.1016/S0167-8655(98)00057-9`
628"""
629histogram, bin_edges = np.histogram(image, bins=bins, density=True)
630bin_centers = np.convolve(bin_edges, [0.5, 0.5], mode='valid')
631t = np.flatnonzero(bin_centers > threshold)[0]
632m0a = np.sum(histogram[:t]) # 0th moment, background
633m0b = np.sum(histogram[t:])
634m1a = np.sum(histogram[:t] * bin_centers[:t]) # 1st moment, background
635m1b = np.sum(histogram[t:] * bin_centers[t:])
636mua = m1a / m0a # mean value, background
637mub = m1b / m0b
638nu = -m1a * np.log(mua) - m1b * np.log(mub)
639return nu
640
641
642def threshold_li(image, *, tolerance=None, initial_guess=None, iter_callback=None):
643"""Compute threshold value by Li's iterative Minimum Cross Entropy method.
644
645Parameters
646----------
647image : (M, N[, ...]) ndarray
648Grayscale input image.
649tolerance : float, optional
650Finish the computation when the change in the threshold in an iteration
651is less than this value. By default, this is half the smallest
652difference between intensity values in ``image``.
653initial_guess : float or Callable[[array[float]], float], optional
654Li's iterative method uses gradient descent to find the optimal
655threshold. If the image intensity histogram contains more than two
656modes (peaks), the gradient descent could get stuck in a local optimum.
657An initial guess for the iteration can help the algorithm find the
658globally-optimal threshold. A float value defines a specific start
659point, while a callable should take in an array of image intensities
660and return a float value. Example valid callables include
661``numpy.mean`` (default), ``lambda arr: numpy.quantile(arr, 0.95)``,
662or even :func:`skimage.filters.threshold_otsu`.
663iter_callback : Callable[[float], Any], optional
664A function that will be called on the threshold at every iteration of
665the algorithm.
666
667Returns
668-------
669threshold : float
670Upper threshold value. All pixels with an intensity higher than
671this value are assumed to be foreground.
672
673References
674----------
675.. [1] Li C.H. and Lee C.K. (1993) "Minimum Cross Entropy Thresholding"
676Pattern Recognition, 26(4): 617-625
677:DOI:`10.1016/0031-3203(93)90115-D`
678.. [2] Li C.H. and Tam P.K.S. (1998) "An Iterative Algorithm for Minimum
679Cross Entropy Thresholding" Pattern Recognition Letters, 18(8): 771-776
680:DOI:`10.1016/S0167-8655(98)00057-9`
681.. [3] Sezgin M. and Sankur B. (2004) "Survey over Image Thresholding
682Techniques and Quantitative Performance Evaluation" Journal of
683Electronic Imaging, 13(1): 146-165
684:DOI:`10.1117/1.1631315`
685.. [4] ImageJ AutoThresholder code, http://fiji.sc/wiki/index.php/Auto_Threshold
686
687Examples
688--------
689>>> from skimage.data import camera
690>>> image = camera()
691>>> thresh = threshold_li(image)
692>>> binary = image > thresh
693"""
694# Remove nan:
695image = image[~np.isnan(image)]
696if image.size == 0:
697return np.nan
698
699# Make sure image has more than one value; otherwise, return that value
700# This works even for np.inf
701if np.all(image == image.flat[0]):
702return image.flat[0]
703
704# At this point, the image only contains np.inf, -np.inf, or valid numbers
705image = image[np.isfinite(image)]
706# if there are no finite values in the image, return 0. This is because
707# at this point we *know* that there are *both* inf and -inf values,
708# because inf == inf evaluates to True. We might as well separate them.
709if image.size == 0:
710return 0.0
711
712# Li's algorithm requires positive image (because of log(mean))
713image_min = np.min(image)
714image -= image_min
715if image.dtype.kind in 'iu':
716tolerance = tolerance or 0.5
717else:
718tolerance = tolerance or np.min(np.diff(np.unique(image))) / 2
719
720# Initial estimate for iteration. See "initial_guess" in the parameter list
721if initial_guess is None:
722t_next = np.mean(image)
723elif callable(initial_guess):
724t_next = initial_guess(image)
725elif np.isscalar(initial_guess): # convert to new, positive image range
726t_next = initial_guess - float(image_min)
727image_max = np.max(image) + image_min
728if not 0 < t_next < np.max(image):
729msg = (
730f'The initial guess for threshold_li must be within the '
731f'range of the image. Got {initial_guess} for image min '
732f'{image_min} and max {image_max}.'
733)
734raise ValueError(msg)
735t_next = image.dtype.type(t_next)
736else:
737raise TypeError(
738'Incorrect type for `initial_guess`; should be '
739'a floating point value, or a function mapping an '
740'array to a floating point value.'
741)
742
743# initial value for t_curr must be different from t_next by at
744# least the tolerance. Since the image is positive, we ensure this
745# by setting to a large-enough negative number
746t_curr = -2 * tolerance
747
748# Callback on initial iterations
749if iter_callback is not None:
750iter_callback(t_next + image_min)
751
752# Stop the iterations when the difference between the
753# new and old threshold values is less than the tolerance
754# or if the background mode has only one value left,
755# since log(0) is not defined.
756
757if image.dtype.kind in 'iu':
758hist, bin_centers = histogram(image.reshape(-1), source_range='image')
759hist = hist.astype('float32', copy=False)
760while abs(t_next - t_curr) > tolerance:
761t_curr = t_next
762foreground = bin_centers > t_curr
763background = ~foreground
764
765mean_fore = np.average(bin_centers[foreground], weights=hist[foreground])
766mean_back = np.average(bin_centers[background], weights=hist[background])
767
768if mean_back == 0:
769break
770
771t_next = (mean_back - mean_fore) / (np.log(mean_back) - np.log(mean_fore))
772
773if iter_callback is not None:
774iter_callback(t_next + image_min)
775
776else:
777while abs(t_next - t_curr) > tolerance:
778t_curr = t_next
779foreground = image > t_curr
780mean_fore = np.mean(image[foreground])
781mean_back = np.mean(image[~foreground])
782
783if mean_back == 0.0:
784break
785
786t_next = (mean_back - mean_fore) / (np.log(mean_back) - np.log(mean_fore))
787
788if iter_callback is not None:
789iter_callback(t_next + image_min)
790
791threshold = t_next + image_min
792return threshold
793
794
795def threshold_minimum(image=None, nbins=256, max_num_iter=10000, *, hist=None):
796"""Return threshold value based on minimum method.
797
798The histogram of the input ``image`` is computed if not provided and
799smoothed until there are only two maxima. Then the minimum in between is
800the threshold value.
801
802Either image or hist must be provided. In case hist is given, the actual
803histogram of the image is ignored.
804
805Parameters
806----------
807image : (M, N[, ...]) ndarray, optional
808Grayscale input image.
809nbins : int, optional
810Number of bins used to calculate histogram. This value is ignored for
811integer arrays.
812max_num_iter : int, optional
813Maximum number of iterations to smooth the histogram.
814hist : array, or 2-tuple of arrays, optional
815Histogram to determine the threshold from and a corresponding array
816of bin center intensities. Alternatively, only the histogram can be
817passed.
818
819Returns
820-------
821threshold : float
822Upper threshold value. All pixels with an intensity higher than
823this value are assumed to be foreground.
824
825Raises
826------
827RuntimeError
828If unable to find two local maxima in the histogram or if the
829smoothing takes more than 1e4 iterations.
830
831References
832----------
833.. [1] C. A. Glasbey, "An analysis of histogram-based thresholding
834algorithms," CVGIP: Graphical Models and Image Processing,
835vol. 55, pp. 532-537, 1993.
836.. [2] Prewitt, JMS & Mendelsohn, ML (1966), "The analysis of cell
837images", Annals of the New York Academy of Sciences 128: 1035-1053
838:DOI:`10.1111/j.1749-6632.1965.tb11715.x`
839
840Examples
841--------
842>>> from skimage.data import camera
843>>> image = camera()
844>>> thresh = threshold_minimum(image)
845>>> binary = image > thresh
846"""
847
848def find_local_maxima_idx(hist):
849# We can't use scipy.signal.argrelmax
850# as it fails on plateaus
851maximum_idxs = list()
852direction = 1
853
854for i in range(hist.shape[0] - 1):
855if direction > 0:
856if hist[i + 1] < hist[i]:
857direction = -1
858maximum_idxs.append(i)
859else:
860if hist[i + 1] > hist[i]:
861direction = 1
862
863return maximum_idxs
864
865counts, bin_centers = _validate_image_histogram(image, hist, nbins)
866
867smooth_hist = counts.astype('float32', copy=False)
868
869for counter in range(max_num_iter):
870smooth_hist = ndi.uniform_filter1d(smooth_hist, 3)
871maximum_idxs = find_local_maxima_idx(smooth_hist)
872if len(maximum_idxs) < 3:
873break
874
875if len(maximum_idxs) != 2:
876raise RuntimeError('Unable to find two maxima in histogram')
877elif counter == max_num_iter - 1:
878raise RuntimeError('Maximum iteration reached for histogram' 'smoothing')
879
880# Find the lowest point between the maxima
881threshold_idx = np.argmin(smooth_hist[maximum_idxs[0] : maximum_idxs[1] + 1])
882
883return bin_centers[maximum_idxs[0] + threshold_idx]
884
885
886def threshold_mean(image):
887"""Return threshold value based on the mean of grayscale values.
888
889Parameters
890----------
891image : (M, N[, ...]) ndarray
892Grayscale input image.
893
894Returns
895-------
896threshold : float
897Upper threshold value. All pixels with an intensity higher than
898this value are assumed to be foreground.
899
900References
901----------
902.. [1] C. A. Glasbey, "An analysis of histogram-based thresholding
903algorithms," CVGIP: Graphical Models and Image Processing,
904vol. 55, pp. 532-537, 1993.
905:DOI:`10.1006/cgip.1993.1040`
906
907Examples
908--------
909>>> from skimage.data import camera
910>>> image = camera()
911>>> thresh = threshold_mean(image)
912>>> binary = image > thresh
913"""
914return np.mean(image)
915
916
917def threshold_triangle(image, nbins=256):
918"""Return threshold value based on the triangle algorithm.
919
920Parameters
921----------
922image : (M, N[, ...]) ndarray
923Grayscale input image.
924nbins : int, optional
925Number of bins used to calculate histogram. This value is ignored for
926integer arrays.
927
928Returns
929-------
930threshold : float
931Upper threshold value. All pixels with an intensity higher than
932this value are assumed to be foreground.
933
934References
935----------
936.. [1] Zack, G. W., Rogers, W. E. and Latt, S. A., 1977,
937Automatic Measurement of Sister Chromatid Exchange Frequency,
938Journal of Histochemistry and Cytochemistry 25 (7), pp. 741-753
939:DOI:`10.1177/25.7.70454`
940.. [2] ImageJ AutoThresholder code,
941http://fiji.sc/wiki/index.php/Auto_Threshold
942
943Examples
944--------
945>>> from skimage.data import camera
946>>> image = camera()
947>>> thresh = threshold_triangle(image)
948>>> binary = image > thresh
949"""
950# nbins is ignored for integer arrays
951# so, we recalculate the effective nbins.
952hist, bin_centers = histogram(image.reshape(-1), nbins, source_range='image')
953nbins = len(hist)
954
955# Find peak, lowest and highest gray levels.
956arg_peak_height = np.argmax(hist)
957peak_height = hist[arg_peak_height]
958arg_low_level, arg_high_level = np.flatnonzero(hist)[[0, -1]]
959
960if arg_low_level == arg_high_level:
961# Image has constant intensity.
962return image.ravel()[0]
963
964# Flip is True if left tail is shorter.
965flip = arg_peak_height - arg_low_level < arg_high_level - arg_peak_height
966if flip:
967hist = hist[::-1]
968arg_low_level = nbins - arg_high_level - 1
969arg_peak_height = nbins - arg_peak_height - 1
970
971# If flip == True, arg_high_level becomes incorrect
972# but we don't need it anymore.
973del arg_high_level
974
975# Set up the coordinate system.
976width = arg_peak_height - arg_low_level
977x1 = np.arange(width)
978y1 = hist[x1 + arg_low_level]
979
980# Normalize.
981norm = np.sqrt(peak_height**2 + width**2)
982peak_height /= norm
983width /= norm
984
985# Maximize the length.
986# The ImageJ implementation includes an additional constant when calculating
987# the length, but here we omit it as it does not affect the location of the
988# minimum.
989length = peak_height * x1 - width * y1
990arg_level = np.argmax(length) + arg_low_level
991
992if flip:
993arg_level = nbins - arg_level - 1
994
995return bin_centers[arg_level]
996
997
998def _mean_std(image, w):
999"""Return local mean and standard deviation of each pixel using a
1000neighborhood defined by a rectangular window size ``w``.
1001The algorithm uses integral images to speedup computation. This is
1002used by :func:`threshold_niblack` and :func:`threshold_sauvola`.
1003
1004Parameters
1005----------
1006image : (M, N[, ...]) ndarray
1007Grayscale input image.
1008w : int, or iterable of int
1009Window size specified as a single odd integer (3, 5, 7, …),
1010or an iterable of length ``image.ndim`` containing only odd
1011integers (e.g. ``(1, 5, 5)``).
1012
1013Returns
1014-------
1015m : ndarray of float, same shape as ``image``
1016Local mean of the image.
1017s : ndarray of float, same shape as ``image``
1018Local standard deviation of the image.
1019
1020References
1021----------
1022.. [1] F. Shafait, D. Keysers, and T. M. Breuel, "Efficient
1023implementation of local adaptive thresholding techniques
1024using integral images." in Document Recognition and
1025Retrieval XV, (San Jose, USA), Jan. 2008.
1026:DOI:`10.1117/12.767755`
1027"""
1028
1029if not isinstance(w, Iterable):
1030w = (w,) * image.ndim
1031_validate_window_size(w)
1032
1033float_dtype = _supported_float_type(image.dtype)
1034pad_width = tuple((k // 2 + 1, k // 2) for k in w)
1035padded = np.pad(image.astype(float_dtype, copy=False), pad_width, mode='reflect')
1036
1037# Note: keep float64 integral images for accuracy. Outputs of
1038# _correlate_sparse can later be safely cast to float_dtype
1039integral = integral_image(padded, dtype=np.float64)
1040padded *= padded
1041integral_sq = integral_image(padded, dtype=np.float64)
1042
1043# Create lists of non-zero kernel indices and values
1044kernel_indices = list(itertools.product(*tuple([(0, _w) for _w in w])))
1045kernel_values = [
1046(-1) ** (image.ndim % 2 != np.sum(indices) % 2) for indices in kernel_indices
1047]
1048
1049total_window_size = math.prod(w)
1050kernel_shape = tuple(_w + 1 for _w in w)
1051m = _correlate_sparse(integral, kernel_shape, kernel_indices, kernel_values)
1052m = m.astype(float_dtype, copy=False)
1053m /= total_window_size
1054g2 = _correlate_sparse(integral_sq, kernel_shape, kernel_indices, kernel_values)
1055g2 = g2.astype(float_dtype, copy=False)
1056g2 /= total_window_size
1057# Note: we use np.clip because g2 is not guaranteed to be greater than
1058# m*m when floating point error is considered
1059s = np.sqrt(np.clip(g2 - m * m, 0, None))
1060return m, s
1061
1062
1063def threshold_niblack(image, window_size=15, k=0.2):
1064"""Applies Niblack local threshold to an array.
1065
1066A threshold T is calculated for every pixel in the image using the
1067following formula::
1068
1069T = m(x,y) - k * s(x,y)
1070
1071where m(x,y) and s(x,y) are the mean and standard deviation of
1072pixel (x,y) neighborhood defined by a rectangular window with size w
1073times w centered around the pixel. k is a configurable parameter
1074that weights the effect of standard deviation.
1075
1076Parameters
1077----------
1078image : (M, N[, ...]) ndarray
1079Grayscale input image.
1080window_size : int, or iterable of int, optional
1081Window size specified as a single odd integer (3, 5, 7, …),
1082or an iterable of length ``image.ndim`` containing only odd
1083integers (e.g. ``(1, 5, 5)``).
1084k : float, optional
1085Value of parameter k in threshold formula.
1086
1087Returns
1088-------
1089threshold : (M, N[, ...]) ndarray
1090Threshold mask. All pixels with an intensity higher than
1091this value are assumed to be foreground.
1092
1093Notes
1094-----
1095This algorithm is originally designed for text recognition.
1096
1097The Bradley threshold is a particular case of the Niblack
1098one, being equivalent to
1099
1100>>> from skimage import data
1101>>> image = data.page()
1102>>> q = 1
1103>>> threshold_image = threshold_niblack(image, k=0) * q
1104
1105for some value ``q``. By default, Bradley and Roth use ``q=1``.
1106
1107
1108References
1109----------
1110.. [1] W. Niblack, An introduction to Digital Image Processing,
1111Prentice-Hall, 1986.
1112.. [2] D. Bradley and G. Roth, "Adaptive thresholding using Integral
1113Image", Journal of Graphics Tools 12(2), pp. 13-21, 2007.
1114:DOI:`10.1080/2151237X.2007.10129236`
1115
1116Examples
1117--------
1118>>> from skimage import data
1119>>> image = data.page()
1120>>> threshold_image = threshold_niblack(image, window_size=7, k=0.1)
1121"""
1122m, s = _mean_std(image, window_size)
1123return m - k * s
1124
1125
1126def threshold_sauvola(image, window_size=15, k=0.2, r=None):
1127"""Applies Sauvola local threshold to an array. Sauvola is a
1128modification of Niblack technique.
1129
1130In the original method a threshold T is calculated for every pixel
1131in the image using the following formula::
1132
1133T = m(x,y) * (1 + k * ((s(x,y) / R) - 1))
1134
1135where m(x,y) and s(x,y) are the mean and standard deviation of
1136pixel (x,y) neighborhood defined by a rectangular window with size w
1137times w centered around the pixel. k is a configurable parameter
1138that weights the effect of standard deviation.
1139R is the maximum standard deviation of a grayscale image.
1140
1141Parameters
1142----------
1143image : (M, N[, ...]) ndarray
1144Grayscale input image.
1145window_size : int, or iterable of int, optional
1146Window size specified as a single odd integer (3, 5, 7, …),
1147or an iterable of length ``image.ndim`` containing only odd
1148integers (e.g. ``(1, 5, 5)``).
1149k : float, optional
1150Value of the positive parameter k.
1151r : float, optional
1152Value of R, the dynamic range of standard deviation.
1153If None, set to the half of the image dtype range.
1154
1155Returns
1156-------
1157threshold : (M, N[, ...]) ndarray
1158Threshold mask. All pixels with an intensity higher than
1159this value are assumed to be foreground.
1160
1161Notes
1162-----
1163This algorithm is originally designed for text recognition.
1164
1165References
1166----------
1167.. [1] J. Sauvola and M. Pietikainen, "Adaptive document image
1168binarization," Pattern Recognition 33(2),
1169pp. 225-236, 2000.
1170:DOI:`10.1016/S0031-3203(99)00055-2`
1171
1172Examples
1173--------
1174>>> from skimage import data
1175>>> image = data.page()
1176>>> t_sauvola = threshold_sauvola(image, window_size=15, k=0.2)
1177>>> binary_image = image > t_sauvola
1178"""
1179if r is None:
1180imin, imax = dtype_limits(image, clip_negative=False)
1181r = 0.5 * (imax - imin)
1182m, s = _mean_std(image, window_size)
1183return m * (1 + k * ((s / r) - 1))
1184
1185
1186def apply_hysteresis_threshold(image, low, high):
1187"""Apply hysteresis thresholding to ``image``.
1188
1189This algorithm finds regions where ``image`` is greater than ``high``
1190OR ``image`` is greater than ``low`` *and* that region is connected to
1191a region greater than ``high``.
1192
1193Parameters
1194----------
1195image : (M[, ...]) ndarray
1196Grayscale input image.
1197low : float, or array of same shape as ``image``
1198Lower threshold.
1199high : float, or array of same shape as ``image``
1200Higher threshold.
1201
1202Returns
1203-------
1204thresholded : (M[, ...]) array of bool
1205Array in which ``True`` indicates the locations where ``image``
1206was above the hysteresis threshold.
1207
1208Examples
1209--------
1210>>> image = np.array([1, 2, 3, 2, 1, 2, 1, 3, 2])
1211>>> apply_hysteresis_threshold(image, 1.5, 2.5).astype(int)
1212array([0, 1, 1, 1, 0, 0, 0, 1, 1])
1213
1214References
1215----------
1216.. [1] J. Canny. A computational approach to edge detection.
1217IEEE Transactions on Pattern Analysis and Machine Intelligence.
12181986; vol. 8, pp.679-698.
1219:DOI:`10.1109/TPAMI.1986.4767851`
1220"""
1221low = np.clip(low, a_min=None, a_max=high) # ensure low always below high
1222mask_low = image > low
1223mask_high = image > high
1224# Connected components of mask_low
1225labels_low, num_labels = ndi.label(mask_low)
1226# Check which connected components contain pixels from mask_high
1227sums = ndi.sum(mask_high, labels_low, np.arange(num_labels + 1))
1228connected_to_high = sums > 0
1229thresholded = connected_to_high[labels_low]
1230return thresholded
1231
1232
1233def threshold_multiotsu(image=None, classes=3, nbins=256, *, hist=None):
1234r"""Generate `classes`-1 threshold values to divide gray levels in `image`,
1235following Otsu's method for multiple classes.
1236
1237The threshold values are chosen to maximize the total sum of pairwise
1238variances between the thresholded graylevel classes. See Notes and [1]_
1239for more details.
1240
1241Either image or hist must be provided. If hist is provided, the actual
1242histogram of the image is ignored.
1243
1244Parameters
1245----------
1246image : (M, N[, ...]) ndarray, optional
1247Grayscale input image.
1248classes : int, optional
1249Number of classes to be thresholded, i.e. the number of resulting
1250regions.
1251nbins : int, optional
1252Number of bins used to calculate the histogram. This value is ignored
1253for integer arrays.
1254hist : array, or 2-tuple of arrays, optional
1255Histogram from which to determine the threshold, and optionally a
1256corresponding array of bin center intensities. If no hist provided,
1257this function will compute it from the image (see notes).
1258
1259Returns
1260-------
1261thresh : array
1262Array containing the threshold values for the desired classes.
1263
1264Raises
1265------
1266ValueError
1267If ``image`` contains less grayscale value then the desired
1268number of classes.
1269
1270Notes
1271-----
1272This implementation relies on a Cython function whose complexity
1273is :math:`O\left(\frac{Ch^{C-1}}{(C-1)!}\right)`, where :math:`h`
1274is the number of histogram bins and :math:`C` is the number of
1275classes desired.
1276
1277If no hist is given, this function will make use of
1278`skimage.exposure.histogram`, which behaves differently than
1279`np.histogram`. While both allowed, use the former for consistent
1280behaviour.
1281
1282The input image must be grayscale.
1283
1284References
1285----------
1286.. [1] Liao, P-S., Chen, T-S. and Chung, P-C., "A fast algorithm for
1287multilevel thresholding", Journal of Information Science and
1288Engineering 17 (5): 713-727, 2001. Available at:
1289<https://ftp.iis.sinica.edu.tw/JISE/2001/200109_01.pdf>
1290:DOI:`10.6688/JISE.2001.17.5.1`
1291.. [2] Tosa, Y., "Multi-Otsu Threshold", a java plugin for ImageJ.
1292Available at:
1293<http://imagej.net/plugins/download/Multi_OtsuThreshold.java>
1294
1295Examples
1296--------
1297>>> from skimage.color import label2rgb
1298>>> from skimage import data
1299>>> image = data.camera()
1300>>> thresholds = threshold_multiotsu(image)
1301>>> regions = np.digitize(image, bins=thresholds)
1302>>> regions_colorized = label2rgb(regions)
1303"""
1304if image is not None and image.ndim > 2 and image.shape[-1] in (3, 4):
1305warn(
1306f'threshold_multiotsu is expected to work correctly only for '
1307f'grayscale images; image shape {image.shape} looks like '
1308f'that of an RGB image.'
1309)
1310
1311# calculating the histogram and the probability of each gray level.
1312prob, bin_centers = _validate_image_histogram(image, hist, nbins, normalize=True)
1313prob = prob.astype('float32', copy=False)
1314
1315nvalues = np.count_nonzero(prob)
1316if nvalues < classes:
1317msg = (
1318f'After discretization into bins, the input image has '
1319f'only {nvalues} different values. It cannot be thresholded '
1320f'in {classes} classes. If there are more unique values '
1321f'before discretization, try increasing the number of bins '
1322f'(`nbins`).'
1323)
1324raise ValueError(msg)
1325elif nvalues == classes:
1326thresh_idx = np.flatnonzero(prob)[:-1]
1327else:
1328# Get threshold indices
1329try:
1330thresh_idx = _get_multiotsu_thresh_indices_lut(prob, classes - 1)
1331except MemoryError:
1332# Don't use LUT if the number of bins is too large (if the
1333# image is uint16 for example): in this case, the
1334# allocated memory is too large.
1335thresh_idx = _get_multiotsu_thresh_indices(prob, classes - 1)
1336
1337thresh = bin_centers[thresh_idx]
1338
1339return thresh
1340