_binary_blobs.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import warnings
  2. import numpy as np
  3. from .._shared.filters import gaussian
  4. def binary_blobs(
  5. length=512,
  6. blob_size_fraction=0.1,
  7. n_dim=2,
  8. volume_fraction=0.5,
  9. rng=None,
  10. *,
  11. boundary_mode='nearest',
  12. ):
  13. """
  14. Generate synthetic binary image with several rounded blob-like objects.
  15. Parameters
  16. ----------
  17. length : int, optional
  18. Linear size of output image.
  19. blob_size_fraction : float, optional
  20. Typical linear size of blob, as a fraction of ``length``, should be
  21. smaller than 1.
  22. n_dim : int, optional
  23. Number of dimensions of output image.
  24. volume_fraction : float, default 0.5
  25. Fraction of image pixels covered by the blobs (where the output is 1).
  26. Should be in [0, 1].
  27. rng : {`numpy.random.Generator`, int}, optional
  28. Pseudo-random number generator.
  29. By default, a PCG64 generator is used (see :func:`numpy.random.default_rng`).
  30. If `rng` is an int, it is used to seed the generator.
  31. boundary_mode : {'nearest', 'wrap'}, optional
  32. The blobs are created by smoothing and then thresholding an
  33. array consisting of ones at seed positions. This mode determines which values are
  34. filled in when the smoothing kernel overlaps the seed array's boundary.
  35. 'nearest' (`a a a a | a b c d | d d d d`)
  36. By default, when applying the Gaussian filter, the seed array is extended by replicating the last
  37. boundary value. This will increase the size of blobs whose seed or
  38. center lies exactly on the edge.
  39. 'wrap' (`a b c d | a b c d | a b c d`)
  40. The seed array is extended by wrapping around to the opposite edge.
  41. The resulting blob array can be tiled and blobs will be contiguous and
  42. have smooth edges across tile boundaries.
  43. boundary_mode : str, default "nearest"
  44. The `mode` parameter passed to the Gaussian filter.
  45. Use "wrap" for periodic boundary conditions.
  46. Returns
  47. -------
  48. blobs : ndarray of bools
  49. Output binary image
  50. Examples
  51. --------
  52. >>> from skimage import data
  53. >>> data.binary_blobs(length=5, blob_size_fraction=0.2) # doctest: +SKIP
  54. array([[ True, False, True, True, True],
  55. [ True, True, True, False, True],
  56. [False, True, False, True, True],
  57. [ True, False, False, True, True],
  58. [ True, False, False, False, True]])
  59. >>> blobs = data.binary_blobs(length=256, blob_size_fraction=0.1)
  60. >>> # Finer structures
  61. >>> blobs = data.binary_blobs(length=256, blob_size_fraction=0.05)
  62. >>> # Blobs cover a smaller volume fraction of the image
  63. >>> blobs = data.binary_blobs(length=256, volume_fraction=0.3)
  64. """
  65. if boundary_mode not in {"nearest", "wrap"}:
  66. raise ValueError(f"unsupported `boundary_mode`: {boundary_mode!r}")
  67. blob_size = blob_size_fraction * length
  68. if blob_size < 0.1:
  69. clamped_size_fraction = 0.1 / length
  70. clamped_blob_size = clamped_size_fraction * length
  71. warnings.warn(
  72. f"`{blob_size_fraction=}` together with `{length=}` would result in a blob "
  73. f"size of {blob_size} pixels. Small blob sizes likely lead to unexpected "
  74. f"results! "
  75. f"Clamping to `blob_size_fraction={clamped_size_fraction}` and a blob size "
  76. f"of {clamped_blob_size} pixels to avoid allocating excessive memory.",
  77. category=RuntimeWarning,
  78. stacklevel=2,
  79. )
  80. blob_size_fraction = clamped_size_fraction
  81. rs = np.random.default_rng(rng)
  82. shape = tuple([length] * n_dim)
  83. mask = np.zeros(shape)
  84. n_pts = max(int(1.0 / blob_size_fraction) ** n_dim, 1)
  85. points = (length * rs.random((n_dim, n_pts))).astype(int)
  86. mask[tuple(indices for indices in points)] = 1
  87. mask = gaussian(
  88. mask,
  89. sigma=0.25 * length * blob_size_fraction,
  90. preserve_range=False,
  91. mode=boundary_mode,
  92. )
  93. threshold = np.percentile(mask, 100 * (1 - volume_fraction))
  94. return np.logical_not(mask < threshold)