gray.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. """
  2. Grayscale morphological operations
  3. """
  4. import numpy as np
  5. from scipy import ndimage as ndi
  6. from .footprints import _footprint_is_sequence, mirror_footprint, pad_footprint
  7. from .misc import default_footprint
  8. __all__ = ['erosion', 'dilation', 'opening', 'closing', 'white_tophat', 'black_tophat']
  9. def _iterate_gray_func(gray_func, image, footprints, out, mode, cval):
  10. """Helper to call `gray_func` for each footprint in a sequence.
  11. `gray_func` is a morphology function that accepts `footprint`, `output`,
  12. `mode` and `cval` keyword arguments (e.g. `scipy.ndimage.grey_erosion`).
  13. """
  14. fp, num_iter = footprints[0]
  15. gray_func(image, footprint=fp, output=out, mode=mode, cval=cval)
  16. for _ in range(1, num_iter):
  17. gray_func(out.copy(), footprint=fp, output=out, mode=mode, cval=cval)
  18. for fp, num_iter in footprints[1:]:
  19. # Note: out.copy() because the computation cannot be in-place!
  20. for _ in range(num_iter):
  21. gray_func(out.copy(), footprint=fp, output=out, mode=mode, cval=cval)
  22. return out
  23. def _min_max_to_constant_mode(dtype, mode, cval):
  24. """Replace 'max' and 'min' with appropriate 'cval' and 'constant' mode."""
  25. if mode == "max":
  26. mode = "constant"
  27. if np.issubdtype(dtype, bool):
  28. cval = True
  29. elif np.issubdtype(dtype, np.integer):
  30. cval = np.iinfo(dtype).max
  31. else:
  32. cval = np.inf
  33. elif mode == "min":
  34. mode = "constant"
  35. if np.issubdtype(dtype, bool):
  36. cval = False
  37. elif np.issubdtype(dtype, np.integer):
  38. cval = np.iinfo(dtype).min
  39. else:
  40. cval = -np.inf
  41. return mode, cval
  42. _SUPPORTED_MODES = {
  43. "reflect",
  44. "constant",
  45. "nearest",
  46. "mirror",
  47. "wrap",
  48. "max",
  49. "min",
  50. "ignore",
  51. }
  52. @default_footprint
  53. def erosion(
  54. image,
  55. footprint=None,
  56. out=None,
  57. *,
  58. mode="reflect",
  59. cval=0.0,
  60. ):
  61. """Return grayscale morphological erosion of an image.
  62. Morphological erosion sets a pixel at (i,j) to the minimum over all pixels
  63. in the neighborhood centered at (i,j). Erosion shrinks bright regions and
  64. enlarges dark regions.
  65. Parameters
  66. ----------
  67. image : ndarray
  68. Image array.
  69. footprint : ndarray or tuple, optional
  70. The neighborhood expressed as a 2-D array of 1's and 0's.
  71. If None, use a cross-shaped footprint (connectivity=1). The footprint
  72. can also be provided as a sequence of smaller footprints as described
  73. in the notes below.
  74. out : ndarrays, optional
  75. The array to store the result of the morphology. If None is
  76. passed, a new array will be allocated.
  77. mode : str, optional
  78. The `mode` parameter determines how the array borders are handled.
  79. Valid modes are: 'reflect', 'constant', 'nearest', 'mirror', 'wrap',
  80. 'max', 'min', or 'ignore'.
  81. If 'max' or 'ignore', pixels outside the image domain are assumed
  82. to be the maximum for the image's dtype, which causes them to not
  83. influence the result. Default is 'reflect'.
  84. cval : scalar, optional
  85. Value to fill past edges of input if `mode` is 'constant'. Default
  86. is 0.0.
  87. .. versionadded:: 0.23
  88. `mode` and `cval` were added in 0.23.
  89. Returns
  90. -------
  91. eroded : array, same shape as `image`
  92. The result of the morphological erosion.
  93. Notes
  94. -----
  95. For ``uint8`` (and ``uint16`` up to a certain bit-depth) data, the
  96. lower algorithm complexity makes the :func:`skimage.filters.rank.minimum`
  97. function more efficient for larger images and footprints.
  98. The footprint can also be a provided as a sequence of 2-tuples where the
  99. first element of each 2-tuple is a footprint ndarray and the second element
  100. is an integer describing the number of times it should be iterated. For
  101. example ``footprint=[(np.ones((9, 1)), 1), (np.ones((1, 9)), 1)]``
  102. would apply a 9x1 footprint followed by a 1x9 footprint resulting in a net
  103. effect that is the same as ``footprint=np.ones((9, 9))``, but with lower
  104. computational cost. Most of the builtin footprints such as
  105. :func:`skimage.morphology.disk` provide an option to automatically generate
  106. a footprint sequence of this type.
  107. For even-sized footprints, :func:`skimage.morphology.binary_erosion` and
  108. this function produce an output that differs: one is shifted by one pixel
  109. compared to the other. :func:`skimage.morphology.pad_footprint` is available
  110. to account for this.
  111. Examples
  112. --------
  113. >>> # Erosion shrinks bright regions
  114. >>> import numpy as np
  115. >>> from skimage.morphology import footprint_rectangle
  116. >>> bright_square = np.array([[0, 0, 0, 0, 0],
  117. ... [0, 1, 1, 1, 0],
  118. ... [0, 1, 1, 1, 0],
  119. ... [0, 1, 1, 1, 0],
  120. ... [0, 0, 0, 0, 0]], dtype=np.uint8)
  121. >>> erosion(bright_square, footprint_rectangle((3, 3)))
  122. array([[0, 0, 0, 0, 0],
  123. [0, 0, 0, 0, 0],
  124. [0, 0, 1, 0, 0],
  125. [0, 0, 0, 0, 0],
  126. [0, 0, 0, 0, 0]], dtype=uint8)
  127. """
  128. if out is None:
  129. out = np.empty_like(image)
  130. if mode not in _SUPPORTED_MODES:
  131. raise ValueError(f"unsupported mode, got {mode!r}")
  132. if mode == "ignore":
  133. mode = "max"
  134. mode, cval = _min_max_to_constant_mode(image.dtype, mode, cval)
  135. footprint = pad_footprint(footprint, pad_end=False)
  136. if not _footprint_is_sequence(footprint):
  137. footprint = [(footprint, 1)]
  138. out = _iterate_gray_func(
  139. gray_func=ndi.grey_erosion,
  140. image=image,
  141. footprints=footprint,
  142. out=out,
  143. mode=mode,
  144. cval=cval,
  145. )
  146. return out
  147. @default_footprint
  148. def dilation(
  149. image,
  150. footprint=None,
  151. out=None,
  152. *,
  153. mode="reflect",
  154. cval=0.0,
  155. ):
  156. """Return grayscale morphological dilation of an image.
  157. Morphological dilation sets the value of a pixel to the maximum over all
  158. pixel values within a local neighborhood centered about it. The values
  159. where the footprint is 1 define this neighborhood.
  160. Dilation enlarges bright regions and shrinks dark regions.
  161. Parameters
  162. ----------
  163. image : ndarray
  164. Image array.
  165. footprint : ndarray or tuple, optional
  166. The neighborhood expressed as a 2-D array of 1's and 0's.
  167. If None, use a cross-shaped footprint (connectivity=1). The footprint
  168. can also be provided as a sequence of smaller footprints as described
  169. in the notes below.
  170. out : ndarray, optional
  171. The array to store the result of the morphology. If None is
  172. passed, a new array will be allocated.
  173. mode : str, optional
  174. The `mode` parameter determines how the array borders are handled.
  175. Valid modes are: 'reflect', 'constant', 'nearest', 'mirror', 'wrap',
  176. 'max', 'min', or 'ignore'.
  177. If 'min' or 'ignore', pixels outside the image domain are assumed
  178. to be the maximum for the image's dtype, which causes them to not
  179. influence the result. Default is 'reflect'.
  180. cval : scalar, optional
  181. Value to fill past edges of input if `mode` is 'constant'. Default
  182. is 0.0.
  183. .. versionadded:: 0.23
  184. `mode` and `cval` were added in 0.23.
  185. Returns
  186. -------
  187. dilated : uint8 array, same shape and type as `image`
  188. The result of the morphological dilation.
  189. Notes
  190. -----
  191. For ``uint8`` (and ``uint16`` up to a certain bit-depth) data, the lower
  192. algorithm complexity makes the :func:`skimage.filters.rank.maximum`
  193. function more efficient for larger images and footprints.
  194. The footprint can also be a provided as a sequence of 2-tuples where the
  195. first element of each 2-tuple is a footprint ndarray and the second element
  196. is an integer describing the number of times it should be iterated. For
  197. example ``footprint=[(np.ones((9, 1)), 1), (np.ones((1, 9)), 1)]``
  198. would apply a 9x1 footprint followed by a 1x9 footprint resulting in a net
  199. effect that is the same as ``footprint=np.ones((9, 9))``, but with lower
  200. computational cost. Most of the builtin footprints such as
  201. :func:`skimage.morphology.disk` provide an option to automatically generate
  202. a footprint sequence of this type.
  203. For non-symmetric footprints, :func:`skimage.morphology.binary_dilation`
  204. and :func:`skimage.morphology.dilation` produce an output that differs:
  205. `binary_dilation` mirrors the footprint, whereas `dilation` does not.
  206. :func:`skimage.morphology.mirror_footprint` is available to correct for this.
  207. Examples
  208. --------
  209. >>> # Dilation enlarges bright regions
  210. >>> import numpy as np
  211. >>> from skimage.morphology import footprint_rectangle
  212. >>> bright_pixel = np.array([[0, 0, 0, 0, 0],
  213. ... [0, 0, 0, 0, 0],
  214. ... [0, 0, 1, 0, 0],
  215. ... [0, 0, 0, 0, 0],
  216. ... [0, 0, 0, 0, 0]], dtype=np.uint8)
  217. >>> dilation(bright_pixel, footprint_rectangle((3, 3)))
  218. array([[0, 0, 0, 0, 0],
  219. [0, 1, 1, 1, 0],
  220. [0, 1, 1, 1, 0],
  221. [0, 1, 1, 1, 0],
  222. [0, 0, 0, 0, 0]], dtype=uint8)
  223. """
  224. if out is None:
  225. out = np.empty_like(image)
  226. if mode not in _SUPPORTED_MODES:
  227. raise ValueError(f"unsupported mode, got {mode!r}")
  228. if mode == "ignore":
  229. mode = "min"
  230. mode, cval = _min_max_to_constant_mode(image.dtype, mode, cval)
  231. footprint = pad_footprint(footprint, pad_end=False)
  232. # Note that `ndi.grey_dilation` mirrors the footprint and this
  233. # additional inversion should be removed in skimage2, see gh-6676.
  234. footprint = mirror_footprint(footprint)
  235. if not _footprint_is_sequence(footprint):
  236. footprint = [(footprint, 1)]
  237. out = _iterate_gray_func(
  238. gray_func=ndi.grey_dilation,
  239. image=image,
  240. footprints=footprint,
  241. out=out,
  242. mode=mode,
  243. cval=cval,
  244. )
  245. return out
  246. @default_footprint
  247. def opening(image, footprint=None, out=None, *, mode="reflect", cval=0.0):
  248. """Return grayscale morphological opening of an image.
  249. The morphological opening of an image is defined as an erosion followed by
  250. a dilation. Opening can remove small bright spots (i.e. "salt") and connect
  251. small dark cracks. This tends to "open" up (dark) gaps between (bright)
  252. features.
  253. Parameters
  254. ----------
  255. image : ndarray
  256. Image array.
  257. footprint : ndarray or tuple, optional
  258. The neighborhood expressed as a 2-D array of 1's and 0's.
  259. If None, use a cross-shaped footprint (connectivity=1). The footprint
  260. can also be provided as a sequence of smaller footprints as described
  261. in the notes below.
  262. out : ndarray, optional
  263. The array to store the result of the morphology. If None
  264. is passed, a new array will be allocated.
  265. mode : str, optional
  266. The `mode` parameter determines how the array borders are handled.
  267. Valid modes are: 'reflect', 'constant', 'nearest', 'mirror', 'wrap',
  268. 'max', 'min', or 'ignore'.
  269. If 'ignore', pixels outside the image domain are assumed
  270. to be the maximum for the image's dtype in the erosion, and minimum
  271. in the dilation, which causes them to not influence the result.
  272. Default is 'reflect'.
  273. cval : scalar, optional
  274. Value to fill past edges of input if `mode` is 'constant'. Default
  275. is 0.0.
  276. .. versionadded:: 0.23
  277. `mode` and `cval` were added in 0.23.
  278. Returns
  279. -------
  280. opening : array, same shape and type as `image`
  281. The result of the morphological opening.
  282. Notes
  283. -----
  284. The footprint can also be a provided as a sequence of 2-tuples where the
  285. first element of each 2-tuple is a footprint ndarray and the second element
  286. is an integer describing the number of times it should be iterated. For
  287. example ``footprint=[(np.ones((9, 1)), 1), (np.ones((1, 9)), 1)]``
  288. would apply a 9x1 footprint followed by a 1x9 footprint resulting in a net
  289. effect that is the same as ``footprint=np.ones((9, 9))``, but with lower
  290. computational cost. Most of the builtin footprints such as
  291. :func:`skimage.morphology.disk` provide an option to automatically generate
  292. a footprint sequence of this type.
  293. Examples
  294. --------
  295. >>> # Open up gap between two bright regions (but also shrink regions)
  296. >>> import numpy as np
  297. >>> from skimage.morphology import footprint_rectangle
  298. >>> bad_connection = np.array([[1, 0, 0, 0, 1],
  299. ... [1, 1, 0, 1, 1],
  300. ... [1, 1, 1, 1, 1],
  301. ... [1, 1, 0, 1, 1],
  302. ... [1, 0, 0, 0, 1]], dtype=np.uint8)
  303. >>> opening(bad_connection, footprint_rectangle((3, 3)))
  304. array([[0, 0, 0, 0, 0],
  305. [1, 1, 0, 1, 1],
  306. [1, 1, 0, 1, 1],
  307. [1, 1, 0, 1, 1],
  308. [0, 0, 0, 0, 0]], dtype=uint8)
  309. """
  310. footprint = pad_footprint(footprint, pad_end=False)
  311. eroded = erosion(image, footprint, mode=mode, cval=cval)
  312. out = dilation(eroded, mirror_footprint(footprint), out=out, mode=mode, cval=cval)
  313. return out
  314. @default_footprint
  315. def closing(image, footprint=None, out=None, *, mode="reflect", cval=0.0):
  316. """Return grayscale morphological closing of an image.
  317. The morphological closing of an image is defined as a dilation followed by
  318. an erosion. Closing can remove small dark spots (i.e. "pepper") and connect
  319. small bright cracks. This tends to "close" up (dark) gaps between (bright)
  320. features.
  321. Parameters
  322. ----------
  323. image : ndarray
  324. Image array.
  325. footprint : ndarray or tuple, optional
  326. The neighborhood expressed as a 2-D array of 1's and 0's.
  327. If None, use a cross-shaped footprint (connectivity=1). The footprint
  328. can also be provided as a sequence of smaller footprints as described
  329. in the notes below.
  330. out : ndarray, optional
  331. The array to store the result of the morphology. If None,
  332. a new array will be allocated.
  333. mode : str, optional
  334. The `mode` parameter determines how the array borders are handled.
  335. Valid modes are: 'reflect', 'constant', 'nearest', 'mirror', 'wrap',
  336. 'max', 'min', or 'ignore'.
  337. If 'ignore', pixels outside the image domain are assumed
  338. to be the maximum for the image's dtype in the erosion, and minimum
  339. in the dilation, which causes them to not influence the result.
  340. Default is 'reflect'.
  341. cval : scalar, optional
  342. Value to fill past edges of input if `mode` is 'constant'. Default
  343. is 0.0.
  344. .. versionadded:: 0.23
  345. `mode` and `cval` were added in 0.23.
  346. Returns
  347. -------
  348. closing : array, same shape and type as `image`
  349. The result of the morphological closing.
  350. Notes
  351. -----
  352. The footprint can also be a provided as a sequence of 2-tuples where the
  353. first element of each 2-tuple is a footprint ndarray and the second element
  354. is an integer describing the number of times it should be iterated. For
  355. example ``footprint=[(np.ones((9, 1)), 1), (np.ones((1, 9)), 1)]``
  356. would apply a 9x1 footprint followed by a 1x9 footprint resulting in a net
  357. effect that is the same as ``footprint=np.ones((9, 9))``, but with lower
  358. computational cost. Most of the builtin footprints such as
  359. :func:`skimage.morphology.disk` provide an option to automatically generate
  360. a footprint sequence of this type.
  361. Examples
  362. --------
  363. >>> # Close a gap between two bright lines
  364. >>> import numpy as np
  365. >>> from skimage.morphology import footprint_rectangle
  366. >>> broken_line = np.array([[0, 0, 0, 0, 0],
  367. ... [0, 0, 0, 0, 0],
  368. ... [1, 1, 0, 1, 1],
  369. ... [0, 0, 0, 0, 0],
  370. ... [0, 0, 0, 0, 0]], dtype=np.uint8)
  371. >>> closing(broken_line, footprint_rectangle((3, 3)))
  372. array([[0, 0, 0, 0, 0],
  373. [0, 0, 0, 0, 0],
  374. [1, 1, 1, 1, 1],
  375. [0, 0, 0, 0, 0],
  376. [0, 0, 0, 0, 0]], dtype=uint8)
  377. """
  378. footprint = pad_footprint(footprint, pad_end=False)
  379. dilated = dilation(image, footprint, mode=mode, cval=cval)
  380. out = erosion(dilated, mirror_footprint(footprint), out=out, mode=mode, cval=cval)
  381. return out
  382. @default_footprint
  383. def white_tophat(image, footprint=None, out=None, *, mode="reflect", cval=0.0):
  384. """Return white top hat of an image.
  385. The white top hat of an image is defined as the image minus its
  386. morphological opening. This operation returns the bright spots of the image
  387. that are smaller than the footprint.
  388. Parameters
  389. ----------
  390. image : ndarray
  391. Image array.
  392. footprint : ndarray or tuple, optional
  393. The neighborhood expressed as a 2-D array of 1's and 0's.
  394. If None, use a cross-shaped footprint (connectivity=1). The footprint
  395. can also be provided as a sequence of smaller footprints as described
  396. in the notes below.
  397. out : ndarray, optional
  398. The array to store the result of the morphology. If None
  399. is passed, a new array will be allocated.
  400. mode : str, optional
  401. The `mode` parameter determines how the array borders are handled.
  402. Valid modes are: 'reflect', 'constant', 'nearest', 'mirror', 'wrap',
  403. 'max', 'min', or 'ignore'. See :func:`skimage.morphology.opening`.
  404. Default is 'reflect'.
  405. cval : scalar, optional
  406. Value to fill past edges of input if `mode` is 'constant'. Default
  407. is 0.0.
  408. .. versionadded:: 0.23
  409. `mode` and `cval` were added in 0.23.
  410. Returns
  411. -------
  412. out : array, same shape and type as `image`
  413. The result of the morphological white top hat.
  414. Notes
  415. -----
  416. The footprint can also be a provided as a sequence of 2-tuples where the
  417. first element of each 2-tuple is a footprint ndarray and the second element
  418. is an integer describing the number of times it should be iterated. For
  419. example ``footprint=[(np.ones((9, 1)), 1), (np.ones((1, 9)), 1)]``
  420. would apply a 9x1 footprint followed by a 1x9 footprint resulting in a net
  421. effect that is the same as ``footprint=np.ones((9, 9))``, but with lower
  422. computational cost. Most of the builtin footprints such as
  423. :func:`skimage.morphology.disk` provide an option to automatically generate
  424. a footprint sequence of this type.
  425. See Also
  426. --------
  427. black_tophat
  428. References
  429. ----------
  430. .. [1] https://en.wikipedia.org/wiki/Top-hat_transform
  431. Examples
  432. --------
  433. >>> # Subtract gray background from bright peak
  434. >>> import numpy as np
  435. >>> from skimage.morphology import footprint_rectangle
  436. >>> bright_on_gray = np.array([[2, 3, 3, 3, 2],
  437. ... [3, 4, 5, 4, 3],
  438. ... [3, 5, 9, 5, 3],
  439. ... [3, 4, 5, 4, 3],
  440. ... [2, 3, 3, 3, 2]], dtype=np.uint8)
  441. >>> white_tophat(bright_on_gray, footprint_rectangle((3, 3)))
  442. array([[0, 0, 0, 0, 0],
  443. [0, 0, 1, 0, 0],
  444. [0, 1, 5, 1, 0],
  445. [0, 0, 1, 0, 0],
  446. [0, 0, 0, 0, 0]], dtype=uint8)
  447. """
  448. if out is image:
  449. # We need a temporary image
  450. opened = opening(image, footprint, mode=mode, cval=cval)
  451. if np.issubdtype(opened.dtype, bool):
  452. np.logical_xor(out, opened, out=out)
  453. else:
  454. out -= opened
  455. return out
  456. # Else write intermediate result into output image
  457. out = opening(image, footprint, out=out, mode=mode, cval=cval)
  458. if np.issubdtype(out.dtype, bool):
  459. np.logical_xor(image, out, out=out)
  460. else:
  461. np.subtract(image, out, out=out)
  462. return out
  463. @default_footprint
  464. def black_tophat(image, footprint=None, out=None, *, mode="reflect", cval=0.0):
  465. """Return black top hat of an image.
  466. The black top hat of an image is defined as its morphological closing minus
  467. the original image. This operation returns the dark spots of the image that
  468. are smaller than the footprint. Note that dark spots in the
  469. original image are bright spots after the black top hat.
  470. Parameters
  471. ----------
  472. image : ndarray
  473. Image array.
  474. footprint : ndarray or tuple, optional
  475. The neighborhood expressed as a 2-D array of 1's and 0's.
  476. If None, use a cross-shaped footprint (connectivity=1). The footprint
  477. can also be provided as a sequence of smaller footprints as described
  478. in the notes below.
  479. out : ndarray, optional
  480. The array to store the result of the morphology. If None
  481. is passed, a new array will be allocated.
  482. mode : str, optional
  483. The `mode` parameter determines how the array borders are handled.
  484. Valid modes are: 'reflect', 'constant', 'nearest', 'mirror', 'wrap',
  485. 'max', 'min', or 'ignore'. See :func:`skimage.morphology.closing`.
  486. Default is 'reflect'.
  487. cval : scalar, optional
  488. Value to fill past edges of input if `mode` is 'constant'. Default
  489. is 0.0.
  490. .. versionadded:: 0.23
  491. `mode` and `cval` were added in 0.23.
  492. Returns
  493. -------
  494. out : array, same shape and type as `image`
  495. The result of the morphological black top hat.
  496. Notes
  497. -----
  498. The footprint can also be a provided as a sequence of 2-tuples where the
  499. first element of each 2-tuple is a footprint ndarray and the second element
  500. is an integer describing the number of times it should be iterated. For
  501. example ``footprint=[(np.ones((9, 1)), 1), (np.ones((1, 9)), 1)]``
  502. would apply a 9x1 footprint followed by a 1x9 footprint resulting in a net
  503. effect that is the same as ``footprint=np.ones((9, 9))``, but with lower
  504. computational cost. Most of the builtin footprints such as
  505. :func:`skimage.morphology.disk` provide an option to automatically generate
  506. a footprint sequence of this type.
  507. See Also
  508. --------
  509. white_tophat
  510. References
  511. ----------
  512. .. [1] https://en.wikipedia.org/wiki/Top-hat_transform
  513. Examples
  514. --------
  515. >>> # Change dark peak to bright peak and subtract background
  516. >>> import numpy as np
  517. >>> from skimage.morphology import footprint_rectangle
  518. >>> dark_on_gray = np.array([[7, 6, 6, 6, 7],
  519. ... [6, 5, 4, 5, 6],
  520. ... [6, 4, 0, 4, 6],
  521. ... [6, 5, 4, 5, 6],
  522. ... [7, 6, 6, 6, 7]], dtype=np.uint8)
  523. >>> black_tophat(dark_on_gray, footprint_rectangle((3, 3)))
  524. array([[0, 0, 0, 0, 0],
  525. [0, 0, 1, 0, 0],
  526. [0, 1, 5, 1, 0],
  527. [0, 0, 1, 0, 0],
  528. [0, 0, 0, 0, 0]], dtype=uint8)
  529. """
  530. if out is image:
  531. # We need a temporary image
  532. closed = closing(image, footprint, mode=mode, cval=cval)
  533. if np.issubdtype(closed.dtype, bool):
  534. np.logical_xor(closed, out, out=out)
  535. else:
  536. np.subtract(closed, out, out=out)
  537. return out
  538. out = closing(image, footprint, out=out, mode=mode, cval=cval)
  539. if np.issubdtype(out.dtype, np.bool_):
  540. np.logical_xor(out, image, out=out)
  541. else:
  542. out -= image
  543. return out