scikit-image
1752 строки · 54.6 Кб
1"""
2
3General Description
4-------------------
5
6These filters compute the local histogram at each pixel, using a sliding window
7similar to the method described in [1]_. A histogram is built using a moving
8window in order to limit redundant computation. The moving window follows a
9snake-like path:
10
11...------------------------↘
12↙--------------------------↙
13↘--------------------------...
14
15The local histogram is updated at each pixel as the footprint window
16moves by, i.e. only those pixels entering and leaving the footprint
17update the local histogram. The histogram size is 8-bit (256 bins) for 8-bit
18images and 2- to 16-bit for 16-bit images depending on the maximum value of the
19image.
20
21The filter is applied up to the image border, the neighborhood used is
22adjusted accordingly. The user may provide a mask image (same size as input
23image) where non zero values are the part of the image participating in the
24histogram computation. By default the entire image is filtered.
25
26This implementation outperforms :func:`skimage.morphology.dilation`
27for large footprints.
28
29Input images will be cast in unsigned 8-bit integer or unsigned 16-bit integer
30if necessary. The number of histogram bins is then determined from the maximum
31value present in the image. Eventually, the output image is cast in the input
32dtype, or the `output_dtype` if set.
33
34To do
35-----
36
37* add simple examples, adapt documentation on existing examples
38* add/check existing doc
39* adapting tests for each type of filter
40
41
42References
43----------
44
45.. [1] Huang, T. ,Yang, G. ; Tang, G.. "A fast two-dimensional
46median filtering algorithm", IEEE Transactions on Acoustics, Speech and
47Signal Processing, Feb 1979. Volume: 27 , Issue: 1, Page(s): 13 - 18.
48
49"""
50
51import numpy as np52from scipy import ndimage as ndi53
54from ..._shared.utils import check_nD, warn55from ...morphology.footprints import _footprint_is_sequence56from ...util import img_as_ubyte57from . import generic_cy58
59
60__all__ = [61'autolevel',62'equalize',63'gradient',64'maximum',65'mean',66'geometric_mean',67'subtract_mean',68'median',69'minimum',70'modal',71'enhance_contrast',72'pop',73'threshold',74'noise_filter',75'entropy',76'otsu',77]
78
79
80def _preprocess_input(81image,82footprint=None,83out=None,84mask=None,85out_dtype=None,86pixel_size=1,87shift_x=None,88shift_y=None,89):90"""Preprocess and verify input for filters.rank methods.91
92Parameters
93----------
94image : 2-D array (integer or float)
95Input image.
96footprint : 2-D array (integer or float), optional
97The neighborhood expressed as a 2-D array of 1's and 0's.
98out : 2-D array (integer or float), optional
99If None, a new array is allocated.
100mask : ndarray (integer or float), optional
101Mask array that defines (>0) area of the image included in the local
102neighborhood. If None, the complete image is used (default).
103out_dtype : data-type, optional
104Desired output data-type. Default is None, which means we cast output
105in input dtype.
106pixel_size : int, optional
107Dimension of each pixel. Default value is 1.
108shift_x, shift_y : int, optional
109Offset added to the footprint center point. Shift is bounded to the
110footprint size (center must be inside of the given footprint).
111
112Returns
113-------
114image : 2-D array (np.uint8 or np.uint16)
115footprint : 2-D array (np.uint8)
116The neighborhood expressed as a binary 2-D array.
117out : 3-D array (same dtype out_dtype or as input)
118Output array. The two first dimensions are the spatial ones, the third
119one is the pixel vector (length 1 by default).
120mask : 2-D array (np.uint8)
121Mask array that defines (>0) area of the image included in the local
122neighborhood.
123n_bins : int
124Number of histogram bins.
125
126"""
127check_nD(image, 2)128input_dtype = image.dtype129if input_dtype in (bool, bool) or out_dtype in (bool, bool):130raise ValueError('dtype cannot be bool.')131if input_dtype not in (np.uint8, np.uint16):132message = (133f'Possible precision loss converting image of type '134f'{input_dtype} to uint8 as required by rank filters. '135f'Convert manually using skimage.util.img_as_ubyte to '136f'silence this warning.'137)138warn(message, stacklevel=5)139image = img_as_ubyte(image)140
141if _footprint_is_sequence(footprint):142raise ValueError(143"footprint sequences are not currently supported by rank filters"144)145
146footprint = np.ascontiguousarray(img_as_ubyte(footprint > 0))147if footprint.ndim != image.ndim:148raise ValueError('Image dimensions and neighborhood dimensions' 'do not match')149
150image = np.ascontiguousarray(image)151
152if mask is not None:153mask = img_as_ubyte(mask)154mask = np.ascontiguousarray(mask)155
156if image is out:157raise NotImplementedError("Cannot perform rank operation in place.")158
159if out is None:160if out_dtype is None:161out_dtype = image.dtype162out = np.empty(image.shape + (pixel_size,), dtype=out_dtype)163else:164if len(out.shape) == 2:165out = out.reshape(out.shape + (pixel_size,))166
167if image.dtype in (np.uint8, np.int8):168n_bins = 256169else:170# Convert to a Python int to avoid the potential overflow when we add171# 1 to the maximum of the image.172n_bins = int(max(3, image.max())) + 1173
174if n_bins > 2**10:175warn(176f'Bad rank filter performance is expected due to a '177f'large number of bins ({n_bins}), equivalent to an approximate '178f'bitdepth of {np.log2(n_bins):.1f}.',179stacklevel=2,180)181
182for name, value in zip(("shift_x", "shift_y"), (shift_x, shift_y)):183if np.dtype(type(value)) == bool:184warn(185f"Paramter `{name}` is boolean and will be interpreted as int. "186"This is not officially supported, use int instead.",187category=UserWarning,188stacklevel=4,189)190
191return image, footprint, out, mask, n_bins192
193
194def _handle_input_3D(195image,196footprint=None,197out=None,198mask=None,199out_dtype=None,200pixel_size=1,201shift_x=None,202shift_y=None,203shift_z=None,204):205"""Preprocess and verify input for filters.rank methods.206
207Parameters
208----------
209image : 3-D array (integer or float)
210Input image.
211footprint : 3-D array (integer or float), optional
212The neighborhood expressed as a 3-D array of 1's and 0's.
213out : 3-D array (integer or float), optional
214If None, a new array is allocated.
215mask : ndarray (integer or float), optional
216Mask array that defines (>0) area of the image included in the local
217neighborhood. If None, the complete image is used (default).
218out_dtype : data-type, optional
219Desired output data-type. Default is None, which means we cast output
220in input dtype.
221pixel_size : int, optional
222Dimension of each pixel. Default value is 1.
223shift_x, shift_y, shift_z : int, optional
224Offset added to the footprint center point. Shift is bounded to the
225footprint size (center must be inside of the given footprint).
226
227Returns
228-------
229image : 3-D array (np.uint8 or np.uint16)
230footprint : 3-D array (np.uint8)
231The neighborhood expressed as a binary 3-D array.
232out : 3-D array (same dtype out_dtype or as input)
233Output array. The two first dimensions are the spatial ones, the third
234one is the pixel vector (length 1 by default).
235mask : 3-D array (np.uint8)
236Mask array that defines (>0) area of the image included in the local
237neighborhood.
238n_bins : int
239Number of histogram bins.
240
241"""
242check_nD(image, 3)243if image.dtype not in (np.uint8, np.uint16):244message = (245f'Possible precision loss converting image of type '246f'{image.dtype} to uint8 as required by rank filters. '247f'Convert manually using skimage.util.img_as_ubyte to '248f'silence this warning.'249)250warn(message, stacklevel=2)251image = img_as_ubyte(image)252
253footprint = np.ascontiguousarray(img_as_ubyte(footprint > 0))254if footprint.ndim != image.ndim:255raise ValueError('Image dimensions and neighborhood dimensions' 'do not match')256image = np.ascontiguousarray(image)257
258if mask is None:259mask = np.ones(image.shape, dtype=np.uint8)260else:261mask = img_as_ubyte(mask)262mask = np.ascontiguousarray(mask)263
264if image is out:265raise NotImplementedError("Cannot perform rank operation in place.")266
267if out is None:268if out_dtype is None:269out_dtype = image.dtype270out = np.empty(image.shape + (pixel_size,), dtype=out_dtype)271else:272out = out.reshape(out.shape + (pixel_size,))273
274is_8bit = image.dtype in (np.uint8, np.int8)275
276if is_8bit:277n_bins = 256278else:279# Convert to a Python int to avoid the potential overflow when we add280# 1 to the maximum of the image.281n_bins = int(max(3, image.max())) + 1282
283if n_bins > 2**10:284warn(285f'Bad rank filter performance is expected due to a '286f'large number of bins ({n_bins}), equivalent to an approximate '287f'bitdepth of {np.log2(n_bins):.1f}.',288stacklevel=2,289)290
291for name, value in zip(292("shift_x", "shift_y", "shift_z"), (shift_x, shift_y, shift_z)293):294if np.dtype(type(value)) == bool:295warn(296f"Parameter `{name}` is boolean and will be interpreted as int. "297"This is not officially supported, use int instead.",298category=UserWarning,299stacklevel=4,300)301
302return image, footprint, out, mask, n_bins303
304
305def _apply_scalar_per_pixel(306func, image, footprint, out, mask, shift_x, shift_y, out_dtype=None307):308"""Process the specific cython function to the image.309
310Parameters
311----------
312func : function
313Cython function to apply.
314image : 2-D array (integer or float)
315Input image.
316footprint : 2-D array (integer or float)
317The neighborhood expressed as a 2-D array of 1's and 0's.
318out : 2-D array (integer or float)
319If None, a new array is allocated.
320mask : ndarray (integer or float)
321Mask array that defines (>0) area of the image included in the local
322neighborhood. If None, the complete image is used (default).
323shift_x, shift_y : int
324Offset added to the footprint center point. Shift is bounded to the
325footprint sizes (center must be inside the given footprint).
326out_dtype : data-type, optional
327Desired output data-type. Default is None, which means we cast output
328in input dtype.
329
330"""
331# preprocess and verify the input332image, footprint, out, mask, n_bins = _preprocess_input(333image, footprint, out, mask, out_dtype, shift_x=shift_x, shift_y=shift_y334)335
336# apply cython function337func(338image,339footprint,340shift_x=shift_x,341shift_y=shift_y,342mask=mask,343out=out,344n_bins=n_bins,345)346
347return np.squeeze(out, axis=-1)348
349
350def _apply_scalar_per_pixel_3D(351func, image, footprint, out, mask, shift_x, shift_y, shift_z, out_dtype=None352):353image, footprint, out, mask, n_bins = _handle_input_3D(354image,355footprint,356out,357mask,358out_dtype,359shift_x=shift_x,360shift_y=shift_y,361shift_z=shift_z,362)363
364func(365image,366footprint,367shift_x=shift_x,368shift_y=shift_y,369shift_z=shift_z,370mask=mask,371out=out,372n_bins=n_bins,373)374
375return out.reshape(out.shape[:3])376
377
378def _apply_vector_per_pixel(379func, image, footprint, out, mask, shift_x, shift_y, out_dtype=None, pixel_size=1380):381"""382
383Parameters
384----------
385func : function
386Cython function to apply.
387image : 2-D array (integer or float)
388Input image.
389footprint : 2-D array (integer or float)
390The neighborhood expressed as a 2-D array of 1's and 0's.
391out : 2-D array (integer or float)
392If None, a new array is allocated.
393mask : ndarray (integer or float)
394Mask array that defines (>0) area of the image included in the local
395neighborhood. If None, the complete image is used (default).
396shift_x, shift_y : int
397Offset added to the footprint center point. Shift is bounded to the
398footprint sizes (center must be inside the given footprint).
399out_dtype : data-type, optional
400Desired output data-type. Default is None, which means we cast output
401in input dtype.
402pixel_size : int, optional
403Dimension of each pixel.
404
405Returns
406-------
407out : 3-D array with float dtype of dimensions (H,W,N), where (H,W) are
408the dimensions of the input image and N is n_bins or
409``image.max() + 1`` if no value is provided as a parameter.
410Effectively, each pixel is a N-D feature vector that is the histogram.
411The sum of the elements in the feature vector will be 1, unless no
412pixels in the window were covered by both footprint and mask, in which
413case all elements will be 0.
414
415"""
416# preprocess and verify the input417image, footprint, out, mask, n_bins = _preprocess_input(418image,419footprint,420out,421mask,422out_dtype,423pixel_size,424shift_x=shift_x,425shift_y=shift_y,426)427
428# apply cython function429func(430image,431footprint,432shift_x=shift_x,433shift_y=shift_y,434mask=mask,435out=out,436n_bins=n_bins,437)438
439return out440
441
442def autolevel(image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=0):443"""Auto-level image using local histogram.444
445This filter locally stretches the histogram of gray values to cover the
446entire range of values from "white" to "black".
447
448Parameters
449----------
450image : ([P,] M, N) ndarray (uint8, uint16)
451Input image.
452footprint : ndarray
453The neighborhood expressed as an ndarray of 1's and 0's.
454out : ([P,] M, N) array (same dtype as input)
455If None, a new array is allocated.
456mask : ndarray (integer or float), optional
457Mask array that defines (>0) area of the image included in the local
458neighborhood. If None, the complete image is used (default).
459shift_x, shift_y, shift_z : int
460Offset added to the footprint center point. Shift is bounded to the
461footprint sizes (center must be inside the given footprint).
462
463Returns
464-------
465out : ([P,] M, N) ndarray (same dtype as input image)
466Output image.
467
468Examples
469--------
470>>> from skimage import data
471>>> from skimage.morphology import disk, ball
472>>> from skimage.filters.rank import autolevel
473>>> import numpy as np
474>>> img = data.camera()
475>>> rng = np.random.default_rng()
476>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
477>>> auto = autolevel(img, disk(5))
478>>> auto_vol = autolevel(volume, ball(5))
479
480"""
481
482np_image = np.asanyarray(image)483if np_image.ndim == 2:484return _apply_scalar_per_pixel(485generic_cy._autolevel,486image,487footprint,488out=out,489mask=mask,490shift_x=shift_x,491shift_y=shift_y,492)493elif np_image.ndim == 3:494return _apply_scalar_per_pixel_3D(495generic_cy._autolevel_3D,496image,497footprint,498out=out,499mask=mask,500shift_x=shift_x,501shift_y=shift_y,502shift_z=shift_z,503)504raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')505
506
507def equalize(image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=0):508"""Equalize image using local histogram.509
510Parameters
511----------
512image : ([P,] M, N) ndarray (uint8, uint16)
513Input image.
514footprint : ndarray
515The neighborhood expressed as an ndarray of 1's and 0's.
516out : ([P,] M, N) array (same dtype as input)
517If None, a new array is allocated.
518mask : ndarray (integer or float), optional
519Mask array that defines (>0) area of the image included in the local
520neighborhood. If None, the complete image is used (default).
521shift_x, shift_y, shift_z : int
522Offset added to the footprint center point. Shift is bounded to the
523footprint sizes (center must be inside the given footprint).
524
525Returns
526-------
527out : ([P,] M, N) ndarray (same dtype as input image)
528Output image.
529
530Examples
531--------
532>>> from skimage import data
533>>> from skimage.morphology import disk, ball
534>>> from skimage.filters.rank import equalize
535>>> import numpy as np
536>>> img = data.camera()
537>>> rng = np.random.default_rng()
538>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
539>>> equ = equalize(img, disk(5))
540>>> equ_vol = equalize(volume, ball(5))
541
542"""
543
544np_image = np.asanyarray(image)545if np_image.ndim == 2:546return _apply_scalar_per_pixel(547generic_cy._equalize,548image,549footprint,550out=out,551mask=mask,552shift_x=shift_x,553shift_y=shift_y,554)555elif np_image.ndim == 3:556return _apply_scalar_per_pixel_3D(557generic_cy._equalize_3D,558image,559footprint,560out=out,561mask=mask,562shift_x=shift_x,563shift_y=shift_y,564shift_z=shift_z,565)566raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')567
568
569def gradient(image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=0):570"""Return local gradient of an image (i.e. local maximum - local minimum).571
572Parameters
573----------
574image : ([P,] M, N) ndarray (uint8, uint16)
575Input image.
576footprint : ndarray
577The neighborhood expressed as an ndarray of 1's and 0's.
578out : ([P,] M, N) array (same dtype as input)
579If None, a new array is allocated.
580mask : ndarray (integer or float), optional
581Mask array that defines (>0) area of the image included in the local
582neighborhood. If None, the complete image is used (default).
583shift_x, shift_y, shift_z : int
584Offset added to the footprint center point. Shift is bounded to the
585footprint sizes (center must be inside the given footprint).
586
587Returns
588-------
589out : ([P,] M, N) ndarray (same dtype as input image)
590Output image.
591
592Examples
593--------
594>>> from skimage import data
595>>> from skimage.morphology import disk, ball
596>>> from skimage.filters.rank import gradient
597>>> import numpy as np
598>>> img = data.camera()
599>>> rng = np.random.default_rng()
600>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
601>>> out = gradient(img, disk(5))
602>>> out_vol = gradient(volume, ball(5))
603
604"""
605
606np_image = np.asanyarray(image)607if np_image.ndim == 2:608return _apply_scalar_per_pixel(609generic_cy._gradient,610image,611footprint,612out=out,613mask=mask,614shift_x=shift_x,615shift_y=shift_y,616)617elif np_image.ndim == 3:618return _apply_scalar_per_pixel_3D(619generic_cy._gradient_3D,620image,621footprint,622out=out,623mask=mask,624shift_x=shift_x,625shift_y=shift_y,626shift_z=shift_z,627)628raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')629
630
631def maximum(image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=0):632"""Return local maximum of an image.633
634Parameters
635----------
636image : ([P,] M, N) ndarray (uint8, uint16)
637Input image.
638footprint : ndarray
639The neighborhood expressed as an ndarray of 1's and 0's.
640out : ([P,] M, N) array (same dtype as input)
641If None, a new array is allocated.
642mask : ndarray (integer or float), optional
643Mask array that defines (>0) area of the image included in the local
644neighborhood. If None, the complete image is used (default).
645shift_x, shift_y, shift_z : int
646Offset added to the footprint center point. Shift is bounded to the
647footprint sizes (center must be inside the given footprint).
648
649Returns
650-------
651out : ([P,] M, N) ndarray (same dtype as input image)
652Output image.
653
654See also
655--------
656skimage.morphology.dilation
657
658Notes
659-----
660The lower algorithm complexity makes `skimage.filters.rank.maximum`
661more efficient for larger images and footprints.
662
663Examples
664--------
665>>> from skimage import data
666>>> from skimage.morphology import disk, ball
667>>> from skimage.filters.rank import maximum
668>>> import numpy as np
669>>> img = data.camera()
670>>> rng = np.random.default_rng()
671>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
672>>> out = maximum(img, disk(5))
673>>> out_vol = maximum(volume, ball(5))
674
675"""
676
677np_image = np.asanyarray(image)678if np_image.ndim == 2:679return _apply_scalar_per_pixel(680generic_cy._maximum,681image,682footprint,683out=out,684mask=mask,685shift_x=shift_x,686shift_y=shift_y,687)688elif np_image.ndim == 3:689return _apply_scalar_per_pixel_3D(690generic_cy._maximum_3D,691image,692footprint,693out=out,694mask=mask,695shift_x=shift_x,696shift_y=shift_y,697shift_z=shift_z,698)699raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')700
701
702def mean(image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=0):703"""Return local mean of an image.704
705Parameters
706----------
707image : ([P,] M, N) ndarray (uint8, uint16)
708Input image.
709footprint : ndarray
710The neighborhood expressed as an ndarray of 1's and 0's.
711out : ([P,] M, N) array (same dtype as input)
712If None, a new array is allocated.
713mask : ndarray (integer or float), optional
714Mask array that defines (>0) area of the image included in the local
715neighborhood. If None, the complete image is used (default).
716shift_x, shift_y, shift_z : int
717Offset added to the footprint center point. Shift is bounded to the
718footprint sizes (center must be inside the given footprint).
719
720Returns
721-------
722out : ([P,] M, N) ndarray (same dtype as input image)
723Output image.
724
725Examples
726--------
727>>> from skimage import data
728>>> from skimage.morphology import disk, ball
729>>> from skimage.filters.rank import mean
730>>> import numpy as np
731>>> img = data.camera()
732>>> rng = np.random.default_rng()
733>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
734>>> avg = mean(img, disk(5))
735>>> avg_vol = mean(volume, ball(5))
736
737"""
738
739np_image = np.asanyarray(image)740if np_image.ndim == 2:741return _apply_scalar_per_pixel(742generic_cy._mean,743image,744footprint,745out=out,746mask=mask,747shift_x=shift_x,748shift_y=shift_y,749)750elif np_image.ndim == 3:751return _apply_scalar_per_pixel_3D(752generic_cy._mean_3D,753image,754footprint,755out=out,756mask=mask,757shift_x=shift_x,758shift_y=shift_y,759shift_z=shift_z,760)761raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')762
763
764def geometric_mean(765image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=0766):767"""Return local geometric mean of an image.768
769Parameters
770----------
771image : ([P,] M, N) ndarray (uint8, uint16)
772Input image.
773footprint : ndarray
774The neighborhood expressed as an ndarray of 1's and 0's.
775out : ([P,] M, N) array (same dtype as input)
776If None, a new array is allocated.
777mask : ndarray (integer or float), optional
778Mask array that defines (>0) area of the image included in the local
779neighborhood. If None, the complete image is used (default).
780shift_x, shift_y, shift_z : int
781Offset added to the footprint center point. Shift is bounded to the
782footprint sizes (center must be inside the given footprint).
783
784Returns
785-------
786out : ([P,] M, N) ndarray (same dtype as input image)
787Output image.
788
789Examples
790--------
791>>> from skimage import data
792>>> from skimage.morphology import disk, ball
793>>> from skimage.filters.rank import mean
794>>> import numpy as np
795>>> img = data.camera()
796>>> rng = np.random.default_rng()
797>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
798>>> avg = geometric_mean(img, disk(5))
799>>> avg_vol = geometric_mean(volume, ball(5))
800
801References
802----------
803.. [1] Gonzalez, R. C. and Woods, R. E. "Digital Image Processing
804(3rd Edition)." Prentice-Hall Inc, 2006.
805
806"""
807
808np_image = np.asanyarray(image)809if np_image.ndim == 2:810return _apply_scalar_per_pixel(811generic_cy._geometric_mean,812image,813footprint,814out=out,815mask=mask,816shift_x=shift_x,817shift_y=shift_y,818)819elif np_image.ndim == 3:820return _apply_scalar_per_pixel_3D(821generic_cy._geometric_mean_3D,822image,823footprint,824out=out,825mask=mask,826shift_x=shift_x,827shift_y=shift_y,828shift_z=shift_z,829)830raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')831
832
833def subtract_mean(834image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=0835):836"""Return image subtracted from its local mean.837
838Parameters
839----------
840image : ([P,] M, N) ndarray (uint8, uint16)
841Input image.
842footprint : ndarray
843The neighborhood expressed as an ndarray of 1's and 0's.
844out : ([P,] M, N) array (same dtype as input)
845If None, a new array is allocated.
846mask : ndarray (integer or float), optional
847Mask array that defines (>0) area of the image included in the local
848neighborhood. If None, the complete image is used (default).
849shift_x, shift_y, shift_z : int
850Offset added to the footprint center point. Shift is bounded to the
851footprint sizes (center must be inside the given footprint).
852
853Returns
854-------
855out : ([P,] M, N) ndarray (same dtype as input image)
856Output image.
857
858Notes
859-----
860Subtracting the mean value may introduce underflow. To compensate
861this potential underflow, the obtained difference is downscaled by
862a factor of 2 and shifted by `n_bins / 2 - 1`, the median value of
863the local histogram (`n_bins = max(3, image.max()) +1` for 16-bits
864images and 256 otherwise).
865
866Examples
867--------
868>>> from skimage import data
869>>> from skimage.morphology import disk, ball
870>>> from skimage.filters.rank import subtract_mean
871>>> import numpy as np
872>>> img = data.camera()
873>>> rng = np.random.default_rng()
874>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
875>>> out = subtract_mean(img, disk(5))
876>>> out_vol = subtract_mean(volume, ball(5))
877
878"""
879
880np_image = np.asanyarray(image)881if np_image.ndim == 2:882return _apply_scalar_per_pixel(883generic_cy._subtract_mean,884image,885footprint,886out=out,887mask=mask,888shift_x=shift_x,889shift_y=shift_y,890)891elif np_image.ndim == 3:892return _apply_scalar_per_pixel_3D(893generic_cy._subtract_mean_3D,894image,895footprint,896out=out,897mask=mask,898shift_x=shift_x,899shift_y=shift_y,900shift_z=shift_z,901)902raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')903
904
905def median(906image,907footprint=None,908out=None,909mask=None,910shift_x=0,911shift_y=0,912shift_z=0,913):914"""Return local median of an image.915
916Parameters
917----------
918image : ([P,] M, N) ndarray (uint8, uint16)
919Input image.
920footprint : ndarray
921The neighborhood expressed as an ndarray of 1's and 0's. If None, a
922full square of size 3 is used.
923out : ([P,] M, N) array (same dtype as input)
924If None, a new array is allocated.
925mask : ndarray (integer or float), optional
926Mask array that defines (>0) area of the image included in the local
927neighborhood. If None, the complete image is used (default).
928shift_x, shift_y, shift_z : int
929Offset added to the footprint center point. Shift is bounded to the
930footprint sizes (center must be inside the given footprint).
931
932Returns
933-------
934out : ([P,] M, N) ndarray (same dtype as input image)
935Output image.
936
937See also
938--------
939skimage.filters.median : Implementation of a median filtering which handles
940images with floating precision.
941
942Examples
943--------
944>>> from skimage import data
945>>> from skimage.morphology import disk, ball
946>>> from skimage.filters.rank import median
947>>> import numpy as np
948>>> img = data.camera()
949>>> rng = np.random.default_rng()
950>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
951>>> med = median(img, disk(5))
952>>> med_vol = median(volume, ball(5))
953
954"""
955
956np_image = np.asanyarray(image)957if footprint is None:958footprint = ndi.generate_binary_structure(image.ndim, image.ndim)959if np_image.ndim == 2:960return _apply_scalar_per_pixel(961generic_cy._median,962image,963footprint,964out=out,965mask=mask,966shift_x=shift_x,967shift_y=shift_y,968)969elif np_image.ndim == 3:970return _apply_scalar_per_pixel_3D(971generic_cy._median_3D,972image,973footprint,974out=out,975mask=mask,976shift_x=shift_x,977shift_y=shift_y,978shift_z=shift_z,979)980raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')981
982
983def minimum(image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=0):984"""Return local minimum of an image.985
986Parameters
987----------
988image : ([P,] M, N) ndarray (uint8, uint16)
989Input image.
990footprint : ndarray
991The neighborhood expressed as an ndarray of 1's and 0's.
992out : ([P,] M, N) array (same dtype as input)
993If None, a new array is allocated.
994mask : ndarray (integer or float), optional
995Mask array that defines (>0) area of the image included in the local
996neighborhood. If None, the complete image is used (default).
997shift_x, shift_y, shift_z : int
998Offset added to the footprint center point. Shift is bounded to the
999footprint sizes (center must be inside the given footprint).
1000
1001Returns
1002-------
1003out : ([P,] M, N) ndarray (same dtype as input image)
1004Output image.
1005
1006See also
1007--------
1008skimage.morphology.erosion
1009
1010Notes
1011-----
1012The lower algorithm complexity makes `skimage.filters.rank.minimum` more
1013efficient for larger images and footprints.
1014
1015Examples
1016--------
1017>>> from skimage import data
1018>>> from skimage.morphology import disk, ball
1019>>> from skimage.filters.rank import minimum
1020>>> import numpy as np
1021>>> img = data.camera()
1022>>> rng = np.random.default_rng()
1023>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
1024>>> out = minimum(img, disk(5))
1025>>> out_vol = minimum(volume, ball(5))
1026
1027"""
1028
1029np_image = np.asanyarray(image)1030if np_image.ndim == 2:1031return _apply_scalar_per_pixel(1032generic_cy._minimum,1033image,1034footprint,1035out=out,1036mask=mask,1037shift_x=shift_x,1038shift_y=shift_y,1039)1040elif np_image.ndim == 3:1041return _apply_scalar_per_pixel_3D(1042generic_cy._minimum_3D,1043image,1044footprint,1045out=out,1046mask=mask,1047shift_x=shift_x,1048shift_y=shift_y,1049shift_z=shift_z,1050)1051raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')1052
1053
1054def modal(image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=0):1055"""Return local mode of an image.1056
1057The mode is the value that appears most often in the local histogram.
1058
1059Parameters
1060----------
1061image : ([P,] M, N) ndarray (uint8, uint16)
1062Input image.
1063footprint : ndarray
1064The neighborhood expressed as an ndarray of 1's and 0's.
1065out : ([P,] M, N) array (same dtype as input)
1066If None, a new array is allocated.
1067mask : ndarray (integer or float), optional
1068Mask array that defines (>0) area of the image included in the local
1069neighborhood. If None, the complete image is used (default).
1070shift_x, shift_y, shift_z : int
1071Offset added to the footprint center point. Shift is bounded to the
1072footprint sizes (center must be inside the given footprint).
1073
1074Returns
1075-------
1076out : ([P,] M, N) ndarray (same dtype as input image)
1077Output image.
1078
1079Examples
1080--------
1081>>> from skimage import data
1082>>> from skimage.morphology import disk, ball
1083>>> from skimage.filters.rank import modal
1084>>> import numpy as np
1085>>> img = data.camera()
1086>>> rng = np.random.default_rng()
1087>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
1088>>> out = modal(img, disk(5))
1089>>> out_vol = modal(volume, ball(5))
1090
1091"""
1092
1093np_image = np.asanyarray(image)1094if np_image.ndim == 2:1095return _apply_scalar_per_pixel(1096generic_cy._modal,1097image,1098footprint,1099out=out,1100mask=mask,1101shift_x=shift_x,1102shift_y=shift_y,1103)1104elif np_image.ndim == 3:1105return _apply_scalar_per_pixel_3D(1106generic_cy._modal_3D,1107image,1108footprint,1109out=out,1110mask=mask,1111shift_x=shift_x,1112shift_y=shift_y,1113shift_z=shift_z,1114)1115raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')1116
1117
1118def enhance_contrast(1119image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=01120):1121"""Enhance contrast of an image.1122
1123This replaces each pixel by the local maximum if the pixel gray value is
1124closer to the local maximum than the local minimum. Otherwise it is
1125replaced by the local minimum.
1126
1127Parameters
1128----------
1129image : ([P,] M, N) ndarray (uint8, uint16)
1130Input image.
1131footprint : ndarray
1132The neighborhood expressed as an ndarray of 1's and 0's.
1133out : ([P,] M, N) array (same dtype as input)
1134If None, a new array is allocated.
1135mask : ndarray (integer or float), optional
1136Mask array that defines (>0) area of the image included in the local
1137neighborhood. If None, the complete image is used (default).
1138shift_x, shift_y, shift_z : int
1139Offset added to the footprint center point. Shift is bounded to the
1140footprint sizes (center must be inside the given footprint).
1141
1142Returns
1143-------
1144out : ([P,] M, N) ndarray (same dtype as input image)
1145Output image
1146
1147Examples
1148--------
1149>>> from skimage import data
1150>>> from skimage.morphology import disk, ball
1151>>> from skimage.filters.rank import enhance_contrast
1152>>> import numpy as np
1153>>> img = data.camera()
1154>>> rng = np.random.default_rng()
1155>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
1156>>> out = enhance_contrast(img, disk(5))
1157>>> out_vol = enhance_contrast(volume, ball(5))
1158
1159"""
1160
1161np_image = np.asanyarray(image)1162if np_image.ndim == 2:1163return _apply_scalar_per_pixel(1164generic_cy._enhance_contrast,1165image,1166footprint,1167out=out,1168mask=mask,1169shift_x=shift_x,1170shift_y=shift_y,1171)1172elif np_image.ndim == 3:1173return _apply_scalar_per_pixel_3D(1174generic_cy._enhance_contrast_3D,1175image,1176footprint,1177out=out,1178mask=mask,1179shift_x=shift_x,1180shift_y=shift_y,1181shift_z=shift_z,1182)1183raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')1184
1185
1186def pop(image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=0):1187"""Return the local number (population) of pixels.1188
1189The number of pixels is defined as the number of pixels which are included
1190in the footprint and the mask.
1191
1192Parameters
1193----------
1194image : ([P,] M, N) ndarray (uint8, uint16)
1195Input image.
1196footprint : ndarray
1197The neighborhood expressed as an ndarray of 1's and 0's.
1198out : ([P,] M, N) array (same dtype as input)
1199If None, a new array is allocated.
1200mask : ndarray (integer or float), optional
1201Mask array that defines (>0) area of the image included in the local
1202neighborhood. If None, the complete image is used (default).
1203shift_x, shift_y, shift_z : int
1204Offset added to the footprint center point. Shift is bounded to the
1205footprint sizes (center must be inside the given footprint).
1206
1207Returns
1208-------
1209out : ([P,] M, N) ndarray (same dtype as input image)
1210Output image.
1211
1212Examples
1213--------
1214>>> from skimage.morphology import square, cube # Need to add 3D example
1215>>> import skimage.filters.rank as rank
1216>>> img = 255 * np.array([[0, 0, 0, 0, 0],
1217... [0, 1, 1, 1, 0],
1218... [0, 1, 1, 1, 0],
1219... [0, 1, 1, 1, 0],
1220... [0, 0, 0, 0, 0]], dtype=np.uint8)
1221>>> rank.pop(img, square(3))
1222array([[4, 6, 6, 6, 4],
1223[6, 9, 9, 9, 6],
1224[6, 9, 9, 9, 6],
1225[6, 9, 9, 9, 6],
1226[4, 6, 6, 6, 4]], dtype=uint8)
1227
1228"""
1229
1230np_image = np.asanyarray(image)1231if np_image.ndim == 2:1232return _apply_scalar_per_pixel(1233generic_cy._pop,1234image,1235footprint,1236out=out,1237mask=mask,1238shift_x=shift_x,1239shift_y=shift_y,1240)1241elif np_image.ndim == 3:1242return _apply_scalar_per_pixel_3D(1243generic_cy._pop_3D,1244image,1245footprint,1246out=out,1247mask=mask,1248shift_x=shift_x,1249shift_y=shift_y,1250shift_z=shift_z,1251)1252raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')1253
1254
1255def sum(image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=0):1256"""Return the local sum of pixels.1257
1258Note that the sum may overflow depending on the data type of the input
1259array.
1260
1261Parameters
1262----------
1263image : ([P,] M, N) ndarray (uint8, uint16)
1264Input image.
1265footprint : ndarray
1266The neighborhood expressed as an ndarray of 1's and 0's.
1267out : ([P,] M, N) array (same dtype as input)
1268If None, a new array is allocated.
1269mask : ndarray (integer or float), optional
1270Mask array that defines (>0) area of the image included in the local
1271neighborhood. If None, the complete image is used (default).
1272shift_x, shift_y, shift_z : int
1273Offset added to the footprint center point. Shift is bounded to the
1274footprint sizes (center must be inside the given footprint).
1275
1276Returns
1277-------
1278out : ([P,] M, N) ndarray (same dtype as input image)
1279Output image.
1280
1281Examples
1282--------
1283>>> from skimage.morphology import square, cube # Need to add 3D example
1284>>> import skimage.filters.rank as rank # Cube seems to fail but
1285>>> img = np.array([[0, 0, 0, 0, 0], # Ball can pass
1286... [0, 1, 1, 1, 0],
1287... [0, 1, 1, 1, 0],
1288... [0, 1, 1, 1, 0],
1289... [0, 0, 0, 0, 0]], dtype=np.uint8)
1290>>> rank.sum(img, square(3))
1291array([[1, 2, 3, 2, 1],
1292[2, 4, 6, 4, 2],
1293[3, 6, 9, 6, 3],
1294[2, 4, 6, 4, 2],
1295[1, 2, 3, 2, 1]], dtype=uint8)
1296
1297"""
1298
1299np_image = np.asanyarray(image)1300if np_image.ndim == 2:1301return _apply_scalar_per_pixel(1302generic_cy._sum,1303image,1304footprint,1305out=out,1306mask=mask,1307shift_x=shift_x,1308shift_y=shift_y,1309)1310elif np_image.ndim == 3:1311return _apply_scalar_per_pixel_3D(1312generic_cy._sum_3D,1313image,1314footprint,1315out=out,1316mask=mask,1317shift_x=shift_x,1318shift_y=shift_y,1319shift_z=shift_z,1320)1321raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')1322
1323
1324def threshold(image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=0):1325"""Local threshold of an image.1326
1327The resulting binary mask is True if the gray value of the center pixel is
1328greater than the local mean.
1329
1330Parameters
1331----------
1332image : ([P,] M, N) ndarray (uint8, uint16)
1333Input image.
1334footprint : ndarray
1335The neighborhood expressed as an ndarray of 1's and 0's.
1336out : ([P,] M, N) array (same dtype as input)
1337If None, a new array is allocated.
1338mask : ndarray (integer or float), optional
1339Mask array that defines (>0) area of the image included in the local
1340neighborhood. If None, the complete image is used (default).
1341shift_x, shift_y, shift_z : int
1342Offset added to the footprint center point. Shift is bounded to the
1343footprint sizes (center must be inside the given footprint).
1344
1345Returns
1346-------
1347out : ([P,] M, N) ndarray (same dtype as input image)
1348Output image.
1349
1350Examples
1351--------
1352>>> from skimage.morphology import square, cube # Need to add 3D example
1353>>> from skimage.filters.rank import threshold
1354>>> img = 255 * np.array([[0, 0, 0, 0, 0],
1355... [0, 1, 1, 1, 0],
1356... [0, 1, 1, 1, 0],
1357... [0, 1, 1, 1, 0],
1358... [0, 0, 0, 0, 0]], dtype=np.uint8)
1359>>> threshold(img, square(3))
1360array([[0, 0, 0, 0, 0],
1361[0, 1, 1, 1, 0],
1362[0, 1, 0, 1, 0],
1363[0, 1, 1, 1, 0],
1364[0, 0, 0, 0, 0]], dtype=uint8)
1365
1366"""
1367
1368np_image = np.asanyarray(image)1369if np_image.ndim == 2:1370return _apply_scalar_per_pixel(1371generic_cy._threshold,1372image,1373footprint,1374out=out,1375mask=mask,1376shift_x=shift_x,1377shift_y=shift_y,1378)1379elif np_image.ndim == 3:1380return _apply_scalar_per_pixel_3D(1381generic_cy._threshold_3D,1382image,1383footprint,1384out=out,1385mask=mask,1386shift_x=shift_x,1387shift_y=shift_y,1388shift_z=shift_z,1389)1390raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')1391
1392
1393def noise_filter(1394image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=01395):1396"""Noise feature.1397
1398Parameters
1399----------
1400image : ([P,] M, N) ndarray (uint8, uint16)
1401Input image.
1402footprint : ndarray
1403The neighborhood expressed as an ndarray of 1's and 0's.
1404out : ([P,] M, N) array (same dtype as input)
1405If None, a new array is allocated.
1406mask : ndarray (integer or float), optional
1407Mask array that defines (>0) area of the image included in the local
1408neighborhood. If None, the complete image is used (default).
1409shift_x, shift_y, shift_z : int
1410Offset added to the footprint center point. Shift is bounded to the
1411footprint sizes (center must be inside the given footprint).
1412
1413References
1414----------
1415.. [1] N. Hashimoto et al. Referenceless image quality evaluation
1416for whole slide imaging. J Pathol Inform 2012;3:9.
1417
1418Returns
1419-------
1420out : ([P,] M, N) ndarray (same dtype as input image)
1421Output image.
1422
1423Examples
1424--------
1425>>> from skimage import data
1426>>> from skimage.morphology import disk, ball
1427>>> from skimage.filters.rank import noise_filter
1428>>> import numpy as np
1429>>> img = data.camera()
1430>>> rng = np.random.default_rng()
1431>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
1432>>> out = noise_filter(img, disk(5))
1433>>> out_vol = noise_filter(volume, ball(5))
1434
1435"""
1436
1437np_image = np.asanyarray(image)1438if _footprint_is_sequence(footprint):1439raise ValueError(1440"footprint sequences are not currently supported by rank filters"1441)1442if np_image.ndim == 2:1443# ensure that the central pixel in the footprint is empty1444centre_r = int(footprint.shape[0] / 2) + shift_y1445centre_c = int(footprint.shape[1] / 2) + shift_x1446# make a local copy1447footprint_cpy = footprint.copy()1448footprint_cpy[centre_r, centre_c] = 01449
1450return _apply_scalar_per_pixel(1451generic_cy._noise_filter,1452image,1453footprint_cpy,1454out=out,1455mask=mask,1456shift_x=shift_x,1457shift_y=shift_y,1458)1459elif np_image.ndim == 3:1460# ensure that the central pixel in the footprint is empty1461centre_r = int(footprint.shape[0] / 2) + shift_y1462centre_c = int(footprint.shape[1] / 2) + shift_x1463centre_z = int(footprint.shape[2] / 2) + shift_z1464# make a local copy1465footprint_cpy = footprint.copy()1466footprint_cpy[centre_r, centre_c, centre_z] = 01467
1468return _apply_scalar_per_pixel_3D(1469generic_cy._noise_filter_3D,1470image,1471footprint_cpy,1472out=out,1473mask=mask,1474shift_x=shift_x,1475shift_y=shift_y,1476shift_z=shift_z,1477)1478
1479raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')1480
1481
1482def entropy(image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=0):1483"""Local entropy.1484
1485The entropy is computed using base 2 logarithm i.e. the filter returns the
1486minimum number of bits needed to encode the local gray level
1487distribution.
1488
1489Parameters
1490----------
1491image : ([P,] M, N) ndarray (uint8, uint16)
1492Input image.
1493footprint : ndarray
1494The neighborhood expressed as an ndarray of 1's and 0's.
1495out : ([P,] M, N) array (same dtype as input)
1496If None, a new array is allocated.
1497mask : ndarray (integer or float), optional
1498Mask array that defines (>0) area of the image included in the local
1499neighborhood. If None, the complete image is used (default).
1500shift_x, shift_y, shift_z : int
1501Offset added to the footprint center point. Shift is bounded to the
1502footprint sizes (center must be inside the given footprint).
1503
1504Returns
1505-------
1506out : ([P,] M, N) ndarray (float)
1507Output image.
1508
1509References
1510----------
1511.. [1] `https://en.wikipedia.org/wiki/Entropy_(information_theory) <https://en.wikipedia.org/wiki/Entropy_(information_theory)>`_
1512
1513Examples
1514--------
1515>>> from skimage import data
1516>>> from skimage.filters.rank import entropy
1517>>> from skimage.morphology import disk, ball
1518>>> import numpy as np
1519>>> img = data.camera()
1520>>> rng = np.random.default_rng()
1521>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
1522>>> ent = entropy(img, disk(5))
1523>>> ent_vol = entropy(volume, ball(5))
1524
1525"""
1526
1527np_image = np.asanyarray(image)1528if np_image.ndim == 2:1529return _apply_scalar_per_pixel(1530generic_cy._entropy,1531image,1532footprint,1533out=out,1534mask=mask,1535shift_x=shift_x,1536shift_y=shift_y,1537out_dtype=np.float64,1538)1539elif np_image.ndim == 3:1540return _apply_scalar_per_pixel_3D(1541generic_cy._entropy_3D,1542image,1543footprint,1544out=out,1545mask=mask,1546shift_x=shift_x,1547shift_y=shift_y,1548shift_z=shift_z,1549out_dtype=np.float64,1550)1551raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')1552
1553
1554def otsu(image, footprint, out=None, mask=None, shift_x=0, shift_y=0, shift_z=0):1555"""Local Otsu's threshold value for each pixel.1556
1557Parameters
1558----------
1559image : ([P,] M, N) ndarray (uint8, uint16)
1560Input image.
1561footprint : ndarray
1562The neighborhood expressed as an ndarray of 1's and 0's.
1563out : ([P,] M, N) array (same dtype as input)
1564If None, a new array is allocated.
1565mask : ndarray (integer or float), optional
1566Mask array that defines (>0) area of the image included in the local
1567neighborhood. If None, the complete image is used (default).
1568shift_x, shift_y, shift_z : int
1569Offset added to the footprint center point. Shift is bounded to the
1570footprint sizes (center must be inside the given footprint).
1571
1572Returns
1573-------
1574out : ([P,] M, N) ndarray (same dtype as input image)
1575Output image.
1576
1577References
1578----------
1579.. [1] https://en.wikipedia.org/wiki/Otsu's_method
1580
1581Examples
1582--------
1583>>> from skimage import data
1584>>> from skimage.filters.rank import otsu
1585>>> from skimage.morphology import disk, ball
1586>>> import numpy as np
1587>>> img = data.camera()
1588>>> rng = np.random.default_rng()
1589>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
1590>>> local_otsu = otsu(img, disk(5))
1591>>> thresh_image = img >= local_otsu
1592>>> local_otsu_vol = otsu(volume, ball(5))
1593>>> thresh_image_vol = volume >= local_otsu_vol
1594
1595"""
1596
1597np_image = np.asanyarray(image)1598if np_image.ndim == 2:1599return _apply_scalar_per_pixel(1600generic_cy._otsu,1601image,1602footprint,1603out=out,1604mask=mask,1605shift_x=shift_x,1606shift_y=shift_y,1607)1608elif np_image.ndim == 3:1609return _apply_scalar_per_pixel_3D(1610generic_cy._otsu_3D,1611image,1612footprint,1613out=out,1614mask=mask,1615shift_x=shift_x,1616shift_y=shift_y,1617shift_z=shift_z,1618)1619raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')1620
1621
1622def windowed_histogram(1623image, footprint, out=None, mask=None, shift_x=0, shift_y=0, n_bins=None1624):1625"""Normalized sliding window histogram1626
1627Parameters
1628----------
1629image : 2-D array (integer or float)
1630Input image.
1631footprint : 2-D array (integer or float)
1632The neighborhood expressed as a 2-D array of 1's and 0's.
1633out : 2-D array (integer or float), optional
1634If None, a new array is allocated.
1635mask : ndarray (integer or float), optional
1636Mask array that defines (>0) area of the image included in the local
1637neighborhood. If None, the complete image is used (default).
1638shift_x, shift_y : int, optional
1639Offset added to the footprint center point. Shift is bounded to the
1640footprint sizes (center must be inside the given footprint).
1641n_bins : int or None
1642The number of histogram bins. Will default to ``image.max() + 1``
1643if None is passed.
1644
1645Returns
1646-------
1647out : 3-D array (float)
1648Array of dimensions (H,W,N), where (H,W) are the dimensions of the
1649input image and N is n_bins or ``image.max() + 1`` if no value is
1650provided as a parameter. Effectively, each pixel is a N-D feature
1651vector that is the histogram. The sum of the elements in the feature
1652vector will be 1, unless no pixels in the window were covered by both
1653footprint and mask, in which case all elements will be 0.
1654
1655Examples
1656--------
1657>>> from skimage import data
1658>>> from skimage.filters.rank import windowed_histogram
1659>>> from skimage.morphology import disk, ball
1660>>> import numpy as np
1661>>> img = data.camera()
1662>>> rng = np.random.default_rng()
1663>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
1664>>> hist_img = windowed_histogram(img, disk(5))
1665
1666"""
1667
1668if n_bins is None:1669n_bins = int(image.max()) + 11670
1671return _apply_vector_per_pixel(1672generic_cy._windowed_hist,1673image,1674footprint,1675out=out,1676mask=mask,1677shift_x=shift_x,1678shift_y=shift_y,1679out_dtype=np.float64,1680pixel_size=n_bins,1681)1682
1683
1684def majority(1685image,1686footprint,1687*,1688out=None,1689mask=None,1690shift_x=0,1691shift_y=0,1692shift_z=0,1693):1694"""Assign to each pixel the most common value within its neighborhood.1695
1696Parameters
1697----------
1698image : ndarray
1699Image array (uint8, uint16 array).
1700footprint : 2-D array (integer or float)
1701The neighborhood expressed as a 2-D array of 1's and 0's.
1702out : ndarray (integer or float), optional
1703If None, a new array will be allocated.
1704mask : ndarray (integer or float), optional
1705Mask array that defines (>0) area of the image included in the local
1706neighborhood. If None, the complete image is used (default).
1707shift_x, shift_y : int, optional
1708Offset added to the footprint center point. Shift is bounded to the
1709footprint sizes (center must be inside the given footprint).
1710
1711Returns
1712-------
1713out : 2-D array (same dtype as input image)
1714Output image.
1715
1716Examples
1717--------
1718>>> from skimage import data
1719>>> from skimage.filters.rank import majority
1720>>> from skimage.morphology import disk, ball
1721>>> import numpy as np
1722>>> img = data.camera()
1723>>> rng = np.random.default_rng()
1724>>> volume = rng.integers(0, 255, size=(10,10,10), dtype=np.uint8)
1725>>> maj_img = majority(img, disk(5))
1726>>> maj_img_vol = majority(volume, ball(5))
1727
1728"""
1729
1730np_image = np.asanyarray(image)1731if np_image.ndim == 2:1732return _apply_scalar_per_pixel(1733generic_cy._majority,1734image,1735footprint,1736out=out,1737mask=mask,1738shift_x=shift_x,1739shift_y=shift_y,1740)1741elif np_image.ndim == 3:1742return _apply_scalar_per_pixel_3D(1743generic_cy._majority_3D,1744image,1745footprint,1746out=out,1747mask=mask,1748shift_x=shift_x,1749shift_y=shift_y,1750shift_z=shift_z,1751)1752raise ValueError(f'`image` must have 2 or 3 dimensions, got {np_image.ndim}.')1753