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 inspect

import matplotlib.pyplot as plt
import numpy as np

import porespy as ps

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);
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
File ~/work/porespy/porespy/src/porespy/generators/_noise.py:94, in fractal_noise(shape, porosity, frequency, octaves, gain, mode, seed, cores, uniform)
     93 try:
---> 94     from pyfastnoisesimd import Noise, NoiseType, PerturbType
     95 except ModuleNotFoundError:

ModuleNotFoundError: No module named 'pyfastnoisesimd'

During handling of the above exception, another exception occurred:

ModuleNotFoundError                       Traceback (most recent call last)
Cell In[2], line 3
      1 np.random.seed(10)
      2 shape = [200, 200]
----> 3 im = ps.generators.fractal_noise(shape=shape)
      4 fig, ax = plt.subplots(1, 1, figsize=[4, 4])
      5 ax.imshow(im, origin='lower', interpolation='none')

File ~/work/porespy/porespy/src/porespy/generators/_noise.py:96, in fractal_noise(shape, porosity, frequency, octaves, gain, mode, seed, cores, uniform)
     94     from pyfastnoisesimd import Noise, NoiseType, PerturbType
     95 except ModuleNotFoundError:
---> 96     raise ModuleNotFoundError(
     97         "You need to install `pyfastnoisesimd` using" " `pip install pyfastnoisesimd`"
     98     )
     99 if cores is None:
    100     cores = psutil.cpu_count(logical=False)

ModuleNotFoundError: You need to install `pyfastnoisesimd` using `pip install pyfastnoisesimd`

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);
../../../_images/b7cdb688f597eb1be1001eaa62a845c428576997f4d3342dd87c7389115c64dc.png

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');
../../../_images/c12ed2f043fb16c4f2f2b870423ccf07f4bf967fc8e8744c432cbd14d1dcbf7e.png

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('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('frequency = 0.1');
../../../_images/5c089f9d3614da6a9ce9d649ba29125a52ca97d9e022c2ccb1bfba0f181132df.png

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}');
../../../_images/8793aeed8dd55e78c0a31cb5315d3fea7a4175c067e17c9f3b2b7b65ba1a0674.png

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}');
../../../_images/eff632255e7f428a1e19021a7fef6f2cf9ed59302545eafa86f9b01a92d785c3.png

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}');
../../../_images/503d20179403c63fed05d564db2b941fe937f45c326a54bc696417dcad9e6b62.png

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());
../../../_images/6729e3c05130fb35391f577bbce51266c97f61fc73dab63ad6044450aea4a784.png

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}');
../../../_images/3470b20f6ae97621098c18a6f25abebeee30b7e081afe39ec78db0552af1a1b9.png