fractal_noise

This function wraps the pyfastnoisesimd package. The PoreSpy wrapper offers a slightly simpler experience which is more consistent with the function-based approach found in PoreSpy, scikit-image, and scipy.ndimage. For full control over it’s features you can just use it directly since it is installed with PoreSpy.

import matplotlib.pyplot as plt
import porespy as ps
import numpy as np
import inspect
inspect.signature(ps.generators.fractal_noise)
<Signature (shape, porosity: float = None, frequency: float = 0.05, octaves: int = 4, gain: float = 0.5, mode: Literal['simplex', 'perlin', 'value', 'cubic'] = 'simplex', seed: int = None, cores: int = 1, uniform: bool = True)>
np.random.seed(10)
shape = [200, 200]
im = ps.generators.fractal_noise(shape=shape)
fig, ax = plt.subplots(1, 1, figsize=[4, 4])
ax.imshow(im, origin='lower', interpolation='none')
ax.axis(False);

cores

The noise is generated by the pyfastnoisesimd package which supports multiple cores to really speed things up. By default we use all the available cores on the machine, however, occasionally this causes problems and kernel crashes. Setting this argument to a value less than the maximum usually avoids these issues. Here we’ll use 1.

cores = 1
im = ps.generators.fractal_noise(shape=shape, cores=cores)
fig, ax = plt.subplots(1, 1, figsize=[4, 4])
ax.imshow(im, origin='lower', interpolation='none')
ax.axis(False);

seed

In order to produce the same image twice it’s possible send a seed value to the function. Unfortunately the numpy.random.seed function is not recognized:

seed = 0
im1 = ps.generators.fractal_noise(shape=shape, seed=seed, cores=cores)
im2 = ps.generators.fractal_noise(shape=shape, seed=seed, cores=cores)
seed = 1
im3 = ps.generators.fractal_noise(shape=shape, seed=seed, cores=cores)
fig, ax = plt.subplots(1, 3, figsize=[9, 3])
ax[0].imshow(im1, origin='lower', interpolation='none')
ax[1].imshow(im2, origin='lower', interpolation='none')
ax[2].imshow(im3, origin='lower', interpolation='none')
ax[0].axis(False)
ax[1].axis(False)
ax[2].axis(False)
ax[0].set_title('seed = 0')
ax[1].set_title('seed = 0')
ax[2].set_title('seed = 1');

frequency

This controls the size of the blobs relative to the image size, with larger values giving smaller features:

fig, ax = plt.subplots(1, 2, figsize=[8, 4])

frequency = 0.1
im1 = ps.generators.fractal_noise(shape=shape, frequency=frequency, cores=cores, seed=seed)
ax[0].imshow(im)
ax[0].axis(False)
ax[0].set_title(f'frequency = 0.05')

frequency = 0.05
im2 = ps.generators.fractal_noise(shape=shape, frequency=frequency, cores=cores, seed=seed)
ax[1].imshow(im2)
ax[1].axis(False)
ax[1].set_title(f'frequency = 0.1');

octaves

The numbers of octaves controls the levels of noise that get overlaid to create the realistic texture

fig, ax = plt.subplots(1, 2, figsize=[8, 4])

octaves=2
im1 = ps.generators.fractal_noise(shape=shape, octaves=octaves, frequency=frequency, seed=seed, cores=cores)
ax[0].imshow(im1)
ax[0].axis(False)
ax[0].set_title(f'octaves = {octaves}')

octaves=10
im2 = ps.generators.fractal_noise(shape=shape, octaves=octaves, frequency=frequency, seed=seed, cores=cores)
ax[1].imshow(im2)
ax[1].axis(False)
ax[1].set_title(f'octaves = {octaves}');

gain

This controls the intensity of each added layer of noise:

fig, ax = plt.subplots(1, 2, figsize=[8, 4])

gain = 0.8
im1 = ps.generators.fractal_noise(shape=shape, gain=gain, seed=seed, cores=cores)
ax[0].imshow(im1)
ax[0].axis(False)
ax[0].set_title(f'gain = {gain}')

gain = 0.5
im2 = ps.generators.fractal_noise(shape=shape, gain=gain, seed=seed, cores=cores)
ax[1].imshow(im2)
ax[1].axis(False)
ax[1].set_title(f'gain = {gain}');

mode

The pyfastnoisesimd package provides 4 different methods for computing noise, which we expose via the mode argument:

fig, ax = plt.subplots(2, 2, figsize=[8, 8])

mode='perlin'
im1 = ps.generators.fractal_noise(shape=shape, gain=gain, seed=seed, cores=cores, mode=mode)
ax[0][0].imshow(im1)
ax[0][0].axis(False)
ax[0][0].set_title(f'mode = {mode}')

mode = 'simplex'
im2 = ps.generators.fractal_noise(shape=shape, gain=gain, seed=seed, cores=cores, mode=mode)
ax[0][1].imshow(im2)
ax[0][1].axis(False)
ax[0][1].set_title(f'mode = {mode}')

mode = 'cubic'
im2 = ps.generators.fractal_noise(shape=shape, gain=gain, seed=seed, cores=cores, mode=mode)
ax[1][0].imshow(im2)
ax[1][0].axis(False)
ax[1][0].set_title(f'mode = {mode}')

mode = 'value'
im2 = ps.generators.fractal_noise(shape=shape, gain=gain, seed=seed, cores=cores, mode=mode)
ax[1][1].imshow(im2)
ax[1][1].axis(False)
ax[1][1].set_title(f'mode = {mode}');

uniform and porosity

uniform Controls whether the returned noise values are scaled to a uniform distribution, which is useful for thresholding to create a specific porosity, or if the original distribution is returned.

fig, ax = plt.subplots(2, 2, figsize=[8, 8])

mode='perlin'
uniform=True
im1 = ps.generators.fractal_noise(shape=shape, gain=gain, seed=seed, cores=cores, mode=mode, uniform=uniform)
ax[0][0].imshow(im1)
ax[0][0].axis(False)
ax[0][0].set_title(f'uniform = {uniform}')

uniform=False
im2 = ps.generators.fractal_noise(shape=shape, gain=gain, seed=seed, cores=cores, mode=mode, uniform=uniform)
ax[0][1].imshow(im2)
ax[0][1].axis(False)
ax[0][1].set_title(f'uniform = {uniform}')

ax[1][0].hist(im1.flatten())
ax[1][1].hist(im2.flatten());

porosity can be specified for a specific porosity level. The image is thresholded after being converted into a uniform distribution. In this case, uniform does not need to be specified to True.

fig, ax = plt.subplots(1, 2, figsize=[8, 4])

porosity1=0.3
im1 = ps.generators.fractal_noise(shape=shape, porosity=porosity1, gain=gain, seed=seed, cores=cores, mode=mode)
ax[0].imshow(im1)
ax[0].axis(False)
ax[0].set_title(f'porosity = {porosity1}')

porosity2=0.6
im2 = ps.generators.fractal_noise(shape=shape, porosity=porosity2, gain=gain, seed=seed, cores=cores, mode=mode)
ax[1].imshow(im2)
ax[1].axis(False)
ax[1].set_title(f'porosity = {porosity2}');