_clear_border.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import numpy as np
  2. from ..measure import label
  3. def clear_border(labels, buffer_size=0, bgval=0, mask=None, *, out=None):
  4. """Clear objects connected to the label image border.
  5. Parameters
  6. ----------
  7. labels : (M[, N[, ..., P]]) array of int or bool
  8. Imaging data labels.
  9. buffer_size : int, optional
  10. The width of the border examined. By default, only objects
  11. that touch the outside of the image are removed.
  12. bgval : float or int, optional
  13. Cleared objects are set to this value.
  14. mask : ndarray of bool, same shape as `image`, optional.
  15. Image data mask. Objects in labels image overlapping with
  16. False pixels of mask will be removed. If defined, the
  17. argument buffer_size will be ignored.
  18. out : ndarray
  19. Array of the same shape as `labels`, into which the
  20. output is placed. By default, a new array is created.
  21. Returns
  22. -------
  23. out : (M[, N[, ..., P]]) array
  24. Imaging data labels with cleared borders
  25. Examples
  26. --------
  27. >>> import numpy as np
  28. >>> from skimage.segmentation import clear_border
  29. >>> labels = np.array([[0, 0, 0, 0, 0, 0, 0, 1, 0],
  30. ... [1, 1, 0, 0, 1, 0, 0, 1, 0],
  31. ... [1, 1, 0, 1, 0, 1, 0, 0, 0],
  32. ... [0, 0, 0, 1, 1, 1, 1, 0, 0],
  33. ... [0, 1, 1, 1, 1, 1, 1, 1, 0],
  34. ... [0, 0, 0, 0, 0, 0, 0, 0, 0]])
  35. >>> clear_border(labels)
  36. array([[0, 0, 0, 0, 0, 0, 0, 0, 0],
  37. [0, 0, 0, 0, 1, 0, 0, 0, 0],
  38. [0, 0, 0, 1, 0, 1, 0, 0, 0],
  39. [0, 0, 0, 1, 1, 1, 1, 0, 0],
  40. [0, 1, 1, 1, 1, 1, 1, 1, 0],
  41. [0, 0, 0, 0, 0, 0, 0, 0, 0]])
  42. >>> mask = np.array([[0, 0, 1, 1, 1, 1, 1, 1, 1],
  43. ... [0, 0, 1, 1, 1, 1, 1, 1, 1],
  44. ... [1, 1, 1, 1, 1, 1, 1, 1, 1],
  45. ... [1, 1, 1, 1, 1, 1, 1, 1, 1],
  46. ... [1, 1, 1, 1, 1, 1, 1, 1, 1],
  47. ... [1, 1, 1, 1, 1, 1, 1, 1, 1]]).astype(bool)
  48. >>> clear_border(labels, mask=mask)
  49. array([[0, 0, 0, 0, 0, 0, 0, 1, 0],
  50. [0, 0, 0, 0, 1, 0, 0, 1, 0],
  51. [0, 0, 0, 1, 0, 1, 0, 0, 0],
  52. [0, 0, 0, 1, 1, 1, 1, 0, 0],
  53. [0, 1, 1, 1, 1, 1, 1, 1, 0],
  54. [0, 0, 0, 0, 0, 0, 0, 0, 0]])
  55. """
  56. if any(buffer_size >= s for s in labels.shape) and mask is None:
  57. # ignore buffer_size if mask
  58. raise ValueError("buffer size may not be greater than labels size")
  59. if out is None:
  60. out = labels.copy()
  61. if mask is not None:
  62. err_msg = (
  63. f'labels and mask should have the same shape but '
  64. f'are {out.shape} and {mask.shape}'
  65. )
  66. if out.shape != mask.shape:
  67. raise (ValueError, err_msg)
  68. if mask.dtype != bool:
  69. raise TypeError("mask should be of type bool.")
  70. borders = ~mask
  71. else:
  72. # create borders with buffer_size
  73. borders = np.zeros_like(out, dtype=bool)
  74. ext = buffer_size + 1
  75. slstart = slice(ext)
  76. slend = slice(-ext, None)
  77. slices = [slice(None) for _ in out.shape]
  78. for d in range(out.ndim):
  79. slices[d] = slstart
  80. borders[tuple(slices)] = True
  81. slices[d] = slend
  82. borders[tuple(slices)] = True
  83. slices[d] = slice(None)
  84. # Re-label, in case we are dealing with a binary out
  85. # and to get consistent labeling
  86. labels, number = label(out, background=0, return_num=True)
  87. # determine all objects that are connected to borders
  88. borders_indices = np.unique(labels[borders])
  89. indices = np.arange(number + 1)
  90. # mask all label indices that are connected to borders
  91. label_mask = np.isin(indices, borders_indices)
  92. # create mask for pixels to clear
  93. mask = label_mask[labels.reshape(-1)].reshape(labels.shape)
  94. # clear border pixels
  95. out[mask] = bgval
  96. return out