def blobs(shape: List[int], porosity: float = 0.5, blobiness: int = 1, **kwargs): """ Generates an image containing amorphous blobs Parameters ---------- shape : list The size of the image to generate in [Nx, Ny, Nz] where N is the number of voxels porosity : float If specified, this will threshold the image to the specified value prior to returning. If ``None`` is specified, then the scalar noise field is converted to a uniform distribution and returned without thresholding. blobiness : int or list of ints(default = 1) Controls the morphology of the blobs. A higher number results in a larger number of small blobs. If a list is supplied then the blobs are anisotropic. Returns ------- image : ND-array A boolean array with ``True`` values denoting the pore space See Also -------- norm_to_uniform """ blobiness = np.array(blobiness) shape = np.array(shape) parallel = kwargs.pop('parallel', False) divs = kwargs.pop('divs', 2) cores = kwargs.pop('cores', None) if np.size(shape) == 1: shape = np.full((3, ), int(shape)) sigma = np.mean(shape)/(40*blobiness) im = np.random.random(shape) if parallel: # TODO: The determination of the overlap should be done rigorously im = ps.filters.chunked_func(func=spim.gaussian_filter, input=im, sigma=sigma, divs=divs, cores=cores, overlap=10) else: im = spim.gaussian_filter(im, sigma=sigma) im = norm_to_uniform(im, scale=[0, 1]) if porosity: im = im < porosity return im
def generate_phantom(shape, porosity, sigma, noise=0): """ noise=0 - бинарный исходный фантом noise>0 - есть зашумление (рекомендую noise = 0.1) """ shape = np.array(shape) if np.size(shape) == 1: shape = np.full((3, ), int(shape)) img = np.random.random(shape) img = gaussian_filter(img, sigma=sigma) img = norm_to_uniform(img, scale=[0, 1]) if porosity: img = img > porosity img = sp_noise(img, noise).astype(bool) return img
def blobs(shape: List[int], porosity: float = 0.5, blobiness: int = 1): """ Generates an image containing amorphous blobs Parameters ---------- shape : list The size of the image to generate in [Nx, Ny, Nz] where N is the number of voxels porosity : float If specified, this will threshold the image to the specified value prior to returning. If ``None`` is specified, then the scalar noise field is converted to a uniform distribution and returned without thresholding. blobiness : int or list of ints(default = 1) Controls the morphology of the blobs. A higher number results in a larger number of small blobs. If a list is supplied then the blobs are anisotropic. Returns ------- image : ND-array A boolean array with ``True`` values denoting the pore space See Also -------- norm_to_uniform """ blobiness = sp.array(blobiness) shape = sp.array(shape) if sp.size(shape) == 1: shape = sp.full((3, ), int(shape)) sigma = sp.mean(shape)/(40*blobiness) im = sp.random.random(shape) im = spim.gaussian_filter(im, sigma=sigma) im = norm_to_uniform(im, scale=[0, 1]) if porosity: im = im < porosity return im
def blobs(shape: List[int], porosity: float = 0.5, blobiness: int = 1): """ Generates an image containing amorphous blobs Parameters ---------- shape : list The size of the image to generate in [Nx, Ny, Nz] where N is the number of voxels porosity : float If specified, this will threshold the image to the specified value prior to returning. If no value is given (the default), then the scalar noise field is returned. blobiness : array_like (default = 1) Controls the morphology of the blobs. A higher number results in a larger number of small blobs. If a vector is supplied then the blobs are anisotropic. Returns ------- If porosity is given, then a boolean array with ``True`` values denoting the pore space is returned. If not, then normally distributed and spatially correlated randomly noise is returned. See Also -------- norm_to_uniform """ blobiness = sp.array(blobiness) shape = sp.array(shape) if sp.size(shape) == 1: shape = sp.full((3, ), int(shape)) sigma = sp.mean(shape) / (40 * blobiness) im = sp.random.random(shape) im = spim.gaussian_filter(im, sigma=sigma) if porosity: im = norm_to_uniform(im, scale=[0, 1]) im = im < porosity return im
def generate_noise(shape: List[int], porosity=None, octaves: int = 3, frequency: int = 32, mode: str = 'simplex'): r""" Generate a field of spatially correlated random noise using the Perlin noise algorithm, or the updated Simplex noise algorithm. Parameters ---------- shape : array_like The size of the image to generate in [Nx, Ny, Nz] where N is the number of voxels. porosity : float If specified, this will threshold the image to the specified value prior to returning. If no value is given (the default), then the scalar noise field is returned. octaves : int Controls the *texture* of the noise, with higher octaves giving more complex features over larger length scales. frequency : array_like Controls the relative sizes of the features, with higher frequencies giving larger features. A scalar value will apply the same frequency in all directions, given an isotropic field; a vector value will apply the specified values along each axis to create anisotropy. mode : string Which noise algorithm to use, either ``'simplex'`` (default) or ``'perlin'``. Returns ------- image : ND-array If porosity is given, then a boolean array with ``True`` values denoting the pore space is returned. If not, then normally distributed and spatially correlated randomly noise is returned. Notes ----- This method depends the a package called 'noise' which must be compiled. It is included in the Anaconda distribution, or a platform specific binary can be downloaded. See Also -------- porespy.tools.norm_to_uniform """ try: import noise except ModuleNotFoundError: raise Exception("The noise package must be installed") shape = sp.array(shape) if sp.size(shape) == 1: Lx, Ly, Lz = sp.full((3, ), int(shape)) elif len(shape) == 2: Lx, Ly = shape Lz = 1 elif len(shape) == 3: Lx, Ly, Lz = shape if mode == 'simplex': f = noise.snoise3 else: f = noise.pnoise3 frequency = sp.atleast_1d(frequency) if frequency.size == 1: freq = sp.full(shape=[3, ], fill_value=frequency[0]) elif frequency.size == 2: freq = sp.concatenate((frequency, [1])) else: freq = sp.array(frequency) im = sp.zeros(shape=[Lx, Ly, Lz], dtype=float) for x in range(Lx): for y in range(Ly): for z in range(Lz): im[x, y, z] = f(x=x/freq[0], y=y/freq[1], z=z/freq[2], octaves=octaves) im = im.squeeze() if porosity: im = norm_to_uniform(im, scale=[0, 1]) im = im < porosity return im
def perlin_noise(shape: List[int], porosity=None, octaves: int = 3, frequency: List[int] = 2, persistence: float = 0.5): r""" Generate a Perlin noise field Parameters ---------- shape : array_like The shape of the desired image frequncy : array_like Controls the frequency of the noise, with higher values leading to smaller features or more tightly spaced undulations in the brightness. porosity : float If specified, the returned image will be thresholded to the specified porosity. If not provided, the greyscale noise is returned (default). octaves : int Controls the texture of the noise, with higher values giving more comlex features of larger length scales. persistence : float Controls how prominent each successive octave is. Shoul be a number less than 1. Returns ------- An ND-array of the specified ``shape``. If ``porosity`` is not given then the array contains greyscale values distributed normally about 0. Use ``porespy.tools.norm_to_uniform`` to create an well-scale image for thresholding. If ``porosity`` is given then these steps are done internally and a boolean image is returned. Notes ----- The implementation used here is a bit fussy about the values of ``frequency`` and ``octaves``. (1) the image ``shape`` must an integer multiple of ``frequency`` in each direction, and (2) ``frequency`` to the power of ``octaves`` must be less than or equal the``shape`` in each direction. Exceptions are thrown if these conditions are not met. References ---------- This implementation is taken from Pierre Vigier's `Github repo <https://github.com/pvigier/perlin-numpy>`_ """ # Parse args shape = np.array(shape) if shape.size == 1: # Assume 3D shape = np.ones(3, dtype=int)*shape res = np.array(frequency) if res.size == 1: # Assume shape as shape res = np.ones(shape.size, dtype=int)*res # Check inputs for various sins if res.size != shape.size: raise Exception('shape and res must have same dimensions') if np.any(np.mod(shape, res) > 0): raise Exception('res must be a multiple of shape along each axis') if np.any(shape/res**octaves < 1): raise Exception('(res[i])**octaves must be <= shape[i]') check = shape/(res**octaves) if np.any(check % 1): raise Exception("Image size must be factor of res**octaves") # Generate noise noise = np.zeros(shape) frequency = 1 amplitude = 1 for _ in tqdm(range(octaves)): if noise.ndim == 2: noise += amplitude * _perlin_noise_2D(shape, frequency*res) elif noise.ndim == 3: noise += amplitude * _perlin_noise_3D(shape, frequency*res) frequency *= 2 amplitude *= persistence if porosity is not None: noise = norm_to_uniform(noise, scale=[0, 1]) noise = noise > porosity return noise