binary.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. """
  2. Binary morphological operations
  3. """
  4. import warnings
  5. import numpy as np
  6. from scipy import ndimage as ndi
  7. from .footprints import _footprint_is_sequence, pad_footprint
  8. from .misc import default_footprint
  9. from .._shared.utils import deprecate_func
  10. def _iterate_binary_func(binary_func, image, footprint, out, border_value):
  11. """Helper to call `binary_func` for each footprint in a sequence.
  12. binary_func is a binary morphology function that accepts "structure",
  13. "output" and "iterations" keyword arguments
  14. (e.g. `scipy.ndimage.binary_erosion`).
  15. """
  16. fp, num_iter = footprint[0]
  17. binary_func(
  18. image, structure=fp, output=out, iterations=num_iter, border_value=border_value
  19. )
  20. for fp, num_iter in footprint[1:]:
  21. # Note: out.copy() because the computation cannot be in-place!
  22. # SciPy <= 1.7 did not automatically make a copy if needed.
  23. binary_func(
  24. out.copy(),
  25. structure=fp,
  26. output=out,
  27. iterations=num_iter,
  28. border_value=border_value,
  29. )
  30. return out
  31. # The default_footprint decorator provides a diamond footprint as
  32. # default with the same dimension as the input image and size 3 along each
  33. # axis.
  34. @default_footprint
  35. @deprecate_func(
  36. deprecated_version="0.26",
  37. removed_version="0.28",
  38. hint="Use `skimage.morphology.erosion` instead. "
  39. "Note the pixel shift by 1 for even-sized footprints (see docstring notes).",
  40. )
  41. def binary_erosion(image, footprint=None, out=None, *, mode='ignore'):
  42. """Return fast binary morphological erosion of an image.
  43. This function returns the same result as grayscale erosion but performs
  44. faster for binary images.
  45. Morphological erosion sets a pixel at ``(i,j)`` to the minimum over all
  46. pixels in the neighborhood centered at ``(i,j)``. Erosion shrinks bright
  47. regions and enlarges dark regions.
  48. Parameters
  49. ----------
  50. image : ndarray
  51. Binary input image.
  52. footprint : ndarray or tuple, optional
  53. The neighborhood expressed as a 2-D array of 1's and 0's.
  54. If None, use a cross-shaped footprint (connectivity=1). The footprint
  55. can also be provided as a sequence of smaller footprints as described
  56. in the notes below.
  57. out : ndarray of bool, optional
  58. The array to store the result of the morphology. If None is
  59. passed, a new array will be allocated.
  60. mode : str, optional
  61. The `mode` parameter determines how the array borders are handled.
  62. Valid modes are: 'max', 'min', 'ignore'.
  63. If 'max' or 'ignore', pixels outside the image domain are assumed
  64. to be `True`, which causes them to not influence the result.
  65. Default is 'ignore'.
  66. .. versionadded:: 0.23
  67. `mode` was added in 0.23.
  68. Returns
  69. -------
  70. eroded : ndarray of bool or uint
  71. The result of the morphological erosion taking values in
  72. ``[False, True]``.
  73. Notes
  74. -----
  75. The footprint can also be a provided as a sequence of 2-tuples where the
  76. first element of each 2-tuple is a footprint ndarray and the second element
  77. is an integer describing the number of times it should be iterated. For
  78. example ``footprint=[(np.ones((9, 1)), 1), (np.ones((1, 9)), 1)]``
  79. would apply a 9x1 footprint followed by a 1x9 footprint resulting in a net
  80. effect that is the same as ``footprint=np.ones((9, 9))``, but with lower
  81. computational cost. Most of the builtin footprints such as
  82. :func:`skimage.morphology.disk` provide an option to automatically generate a
  83. footprint sequence of this type.
  84. For even-sized footprints, :func:`skimage.morphology.erosion` and
  85. this function produce an output that differs: one is shifted by one pixel
  86. compared to the other. :func:`skimage.morphology.pad_footprint´ is available
  87. to account for this.
  88. See also
  89. --------
  90. skimage.morphology.isotropic_erosion
  91. """
  92. if out is None:
  93. out = np.empty(image.shape, dtype=bool)
  94. if mode not in {"max", "min", "ignore"}:
  95. raise ValueError(f"unsupported mode, got {mode!r}")
  96. border_value = False if mode == 'min' else True
  97. footprint = pad_footprint(footprint, pad_end=True)
  98. if not _footprint_is_sequence(footprint):
  99. footprint = [(footprint, 1)]
  100. out = _iterate_binary_func(
  101. binary_func=ndi.binary_erosion,
  102. image=image,
  103. footprint=footprint,
  104. out=out,
  105. border_value=border_value,
  106. )
  107. return out
  108. @default_footprint
  109. @deprecate_func(
  110. deprecated_version="0.26",
  111. removed_version="0.28",
  112. hint="Use `skimage.morphology.dilation` instead. "
  113. "Note the lack of mirroring for non-symmetric footprints (see docstring notes).",
  114. )
  115. def binary_dilation(image, footprint=None, out=None, *, mode='ignore'):
  116. """Return fast binary morphological dilation of an image.
  117. This function returns the same result as grayscale dilation but performs
  118. faster for binary images.
  119. Morphological dilation sets a pixel at ``(i,j)`` to the maximum over all
  120. pixels in the neighborhood centered at ``(i,j)``. Dilation enlarges bright
  121. regions and shrinks dark regions.
  122. Parameters
  123. ----------
  124. image : ndarray
  125. Binary input image.
  126. footprint : ndarray or tuple, optional
  127. The neighborhood expressed as a 2-D array of 1's and 0's.
  128. If None, use a cross-shaped footprint (connectivity=1). The footprint
  129. can also be provided as a sequence of smaller footprints as described
  130. in the notes below.
  131. out : ndarray of bool, optional
  132. The array to store the result of the morphology. If None is
  133. passed, a new array will be allocated.
  134. mode : str, optional
  135. The `mode` parameter determines how the array borders are handled.
  136. Valid modes are: 'max', 'min', 'ignore'.
  137. If 'min' or 'ignore', pixels outside the image domain are assumed
  138. to be `False`, which causes them to not influence the result.
  139. Default is 'ignore'.
  140. .. versionadded:: 0.23
  141. `mode` was added in 0.23.
  142. Returns
  143. -------
  144. dilated : ndarray of bool or uint
  145. The result of the morphological dilation with values in
  146. ``[False, True]``.
  147. Notes
  148. -----
  149. The footprint can also be a provided as a sequence of 2-tuples where the
  150. first element of each 2-tuple is a footprint ndarray and the second element
  151. is an integer describing the number of times it should be iterated. For
  152. example ``footprint=[(np.ones((9, 1)), 1), (np.ones((1, 9)), 1)]``
  153. would apply a 9x1 footprint followed by a 1x9 footprint resulting in a net
  154. effect that is the same as ``footprint=np.ones((9, 9))``, but with lower
  155. computational cost. Most of the builtin footprints such as
  156. :func:`skimage.morphology.disk` provide an option to automatically generate a
  157. footprint sequence of this type.
  158. For non-symmetric footprints, :func:`skimage.morphology.binary_dilation`
  159. and :func:`skimage.morphology.dilation` produce an output that differs:
  160. `binary_dilation` mirrors the footprint, whereas `dilation` does not.
  161. :func:`skimage.morphology.mirror_footprint` is available to correct for this.
  162. See also
  163. --------
  164. skimage.morphology.isotropic_dilation
  165. """
  166. if out is None:
  167. out = np.empty(image.shape, dtype=bool)
  168. if mode not in {"max", "min", "ignore"}:
  169. raise ValueError(f"unsupported mode, got {mode!r}")
  170. border_value = True if mode == 'max' else False
  171. footprint = pad_footprint(footprint, pad_end=True)
  172. if not _footprint_is_sequence(footprint):
  173. footprint = [(footprint, 1)]
  174. out = _iterate_binary_func(
  175. binary_func=ndi.binary_dilation,
  176. image=image,
  177. footprint=footprint,
  178. out=out,
  179. border_value=border_value,
  180. )
  181. return out
  182. @default_footprint
  183. @deprecate_func(
  184. deprecated_version="0.26",
  185. removed_version="0.28",
  186. hint="Use `skimage.morphology.opening` instead.",
  187. )
  188. def binary_opening(image, footprint=None, out=None, *, mode='ignore'):
  189. """Return fast binary morphological opening of an image.
  190. This function returns the same result as grayscale opening but performs
  191. faster for binary images.
  192. The morphological opening on an image is defined as an erosion followed by
  193. a dilation. Opening can remove small bright spots (i.e. "salt") and connect
  194. small dark cracks. This tends to "open" up (dark) gaps between (bright)
  195. features.
  196. Parameters
  197. ----------
  198. image : ndarray
  199. Binary input image.
  200. footprint : ndarray or tuple, optional
  201. The neighborhood expressed as a 2-D array of 1's and 0's.
  202. If None, use a cross-shaped footprint (connectivity=1). The footprint
  203. can also be provided as a sequence of smaller footprints as described
  204. in the notes below.
  205. out : ndarray of bool, optional
  206. The array to store the result of the morphology. If None
  207. is passed, a new array will be allocated.
  208. mode : str, optional
  209. The `mode` parameter determines how the array borders are handled.
  210. Valid modes are: 'max', 'min', 'ignore'.
  211. If 'ignore', pixels outside the image domain are assumed to be `True`
  212. for the erosion and `False` for the dilation, which causes them to not
  213. influence the result. Default is 'ignore'.
  214. .. versionadded:: 0.23
  215. `mode` was added in 0.23.
  216. Returns
  217. -------
  218. opening : ndarray of bool
  219. The result of the morphological opening.
  220. Notes
  221. -----
  222. The footprint can also be a provided as a sequence of 2-tuples where the
  223. first element of each 2-tuple is a footprint ndarray and the second element
  224. is an integer describing the number of times it should be iterated. For
  225. example ``footprint=[(np.ones((9, 1)), 1), (np.ones((1, 9)), 1)]``
  226. would apply a 9x1 footprint followed by a 1x9 footprint resulting in a net
  227. effect that is the same as ``footprint=np.ones((9, 9))``, but with lower
  228. computational cost. Most of the builtin footprints such as
  229. :func:`skimage.morphology.disk` provide an option to automatically generate a
  230. footprint sequence of this type.
  231. See also
  232. --------
  233. skimage.morphology.isotropic_opening
  234. """
  235. with warnings.catch_warnings():
  236. warnings.filterwarnings(
  237. action="ignore",
  238. message="`binary_(dilation|erosion)` is deprecated",
  239. category=FutureWarning,
  240. module="skimage",
  241. )
  242. tmp = binary_erosion(image, footprint, mode=mode)
  243. out = binary_dilation(tmp, footprint, out=out, mode=mode)
  244. return out
  245. @default_footprint
  246. @deprecate_func(
  247. deprecated_version="0.26",
  248. removed_version="0.28",
  249. hint="Use `skimage.morphology.closing` instead.",
  250. )
  251. def binary_closing(image, footprint=None, out=None, *, mode='ignore'):
  252. """Return fast binary morphological closing of an image.
  253. This function returns the same result as grayscale closing but performs
  254. faster for binary images.
  255. The morphological closing on an image is defined as a dilation followed by
  256. an erosion. Closing can remove small dark spots (i.e. "pepper") and connect
  257. small bright cracks. This tends to "close" up (dark) gaps between (bright)
  258. features.
  259. Parameters
  260. ----------
  261. image : ndarray
  262. Binary input image.
  263. footprint : ndarray or tuple, optional
  264. The neighborhood expressed as a 2-D array of 1's and 0's.
  265. If None, use a cross-shaped footprint (connectivity=1). The footprint
  266. can also be provided as a sequence of smaller footprints as described
  267. in the notes below.
  268. out : ndarray of bool, optional
  269. The array to store the result of the morphology. If None,
  270. is passed, a new array will be allocated.
  271. mode : str, optional
  272. The `mode` parameter determines how the array borders are handled.
  273. Valid modes are: 'max', 'min', 'ignore'.
  274. If 'ignore', pixels outside the image domain are assumed to be `True`
  275. for the erosion and `False` for the dilation, which causes them to not
  276. influence the result. Default is 'ignore'.
  277. .. versionadded:: 0.23
  278. `mode` was added in 0.23.
  279. Returns
  280. -------
  281. closing : ndarray of bool
  282. The result of the morphological closing.
  283. Notes
  284. -----
  285. The footprint can also be a provided as a sequence of 2-tuples where the
  286. first element of each 2-tuple is a footprint ndarray and the second element
  287. is an integer describing the number of times it should be iterated. For
  288. example ``footprint=[(np.ones((9, 1)), 1), (np.ones((1, 9)), 1)]``
  289. would apply a 9x1 footprint followed by a 1x9 footprint resulting in a net
  290. effect that is the same as ``footprint=np.ones((9, 9))``, but with lower
  291. computational cost. Most of the builtin footprints such as
  292. :func:`skimage.morphology.disk` provide an option to automatically generate a
  293. footprint sequence of this type.
  294. See also
  295. --------
  296. skimage.morphology.isotropic_closing
  297. """
  298. with warnings.catch_warnings():
  299. warnings.filterwarnings(
  300. action="ignore",
  301. message="`binary_(dilation|erosion)` is deprecated",
  302. category=FutureWarning,
  303. module="skimage",
  304. )
  305. tmp = binary_dilation(image, footprint, mode=mode)
  306. out = binary_erosion(tmp, footprint, out=out, mode=mode)
  307. return out