Skip to content

Commit

Permalink
fixing my temporary insanity, packing now adds to background
Browse files Browse the repository at this point in the history
  • Loading branch information
jgostick committed Mar 27, 2024
1 parent b2b97a1 commit ce4155c
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 72 deletions.
16 changes: 8 additions & 8 deletions src/porespy/generators/_imgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ def random_spheres(
edges: Literal['contained', 'extended'] = 'contained',
smooth: bool = True,
seed: int = None,
value: int = False,
value: int = True,
):
r"""
Generates a sphere or disk packing using random sequential addition as
Expand All @@ -242,9 +242,9 @@ def random_spheres(
----------
shape : list
The shape of the image to create. This is equivalent to passing an array
of `True` values of the desired size to `im`.
of `False` values of the desired size to `im`.
im : ndarray
Image with values > 0 indicating the voxels where spheres should be
Image with `False` indicating the voxels where spheres should be
inserted. This can be used to insert spheres into an image that already
has some features (e.g. half filled with larger spheres, or a cylindrical
plug).
Expand Down Expand Up @@ -291,14 +291,14 @@ def random_spheres(
function so it can be initialized the way ``numba`` requires. The default
is ``None``, which means each call will produce a new realization.
value : scalar
The value to set the inserted spheres to. Using `value < 0` is a handy
The value to set the inserted spheres to. Using `value > 1` is a handy
way to repeatedly insert different sphere sizes into the same image while
making them easy to identify.
Returns
-------
image : ndarray
An image with spheres of specified radius *added* to the foreground.
An image with spheres of specified radius *added* to the background.
See Also
--------
Expand All @@ -310,7 +310,7 @@ def random_spheres(
This algorithm ensures that spheres do not overlap but does not guarantee they
are tightly packed.
This function adds spheres to the foreground of the received ``im``, which
This function adds spheres to the background of the received ``im``, which
allows iteratively adding spheres of different radii to the unfilled space
by repeatedly passing in the result of previous calls to the function.
Expand All @@ -333,10 +333,10 @@ def random_spheres(
if (im is None) and (shape is not None): # If shape was given, generate empty im
if np.ndim(shape) > 1:
raise Exception('shape must be a list like [Nx, Ny] or [Nx, Ny, Nz]')
im = np.ones(shape, dtype=type(value))
im = np.zeros(shape, dtype=type(value))
options_im = np.ones_like(im, dtype=bool)
elif (shape is None) and (im is not None): # Otherwise use im
options_im = edt(im == 1) >= (r - protrusion)
options_im = edt(im == 0) >= (r - protrusion)
im = np.copy(im).astype(type(value))
else:
raise Exception('Must specify either im or shape')
Expand Down
46 changes: 23 additions & 23 deletions src/porespy/generators/_pseudo_packings.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def _random_spheres2(
phi: float = 1.0,
seed: float = None,
smooth: bool = True,
value: int = 0,
value: int = 1,
) -> np.ndarray:
r"""
This is an alternative implementation of random_spheres that uses the same
Expand All @@ -54,14 +54,14 @@ def _random_spheres2(
np.random.seed(seed) # Also initialize numpys rng

if im is None: # If shape is given instead of im
im = np.ones(shape, dtype=bool)
im = np.zeros(shape, dtype=bool)

im = np.swapaxes(im, 0, axis) # Move "axis" to x position for processing

if smooth: # Smooth balls are 1 pixel smaller, so increase r
r = r + 1

mask = im == 1 # Find mask of valid points
mask = im == 0 # Find mask of valid points
if protrusion > 0: # Dilate mask
dt = edt(~mask)
mask = dt <= protrusion
Expand Down Expand Up @@ -111,7 +111,7 @@ def pseudo_gravity_packing(
phi: float = 1.0,
seed: int = None,
smooth: bool = True,
value: int = 0,
value: int = 1,
) -> np.ndarray:
r"""
Iteratively inserts spheres at the lowest accessible point in an image,
Expand All @@ -121,9 +121,9 @@ def pseudo_gravity_packing(
----------
shape : list
The shape of the image to create. This is equivalent to passing an array
of `True` values of the desired size to `im`.
of `False` values of the desired size to `im`.
im : ndarray
Image with values > 0 indicating the voxels where spheres should be
Image with `False` indicating the voxels where spheres should be
inserted. This can be used to insert spheres into an image that already
has some features (e.g. half filled with larger spheres, or a cylindrical
plug).
Expand Down Expand Up @@ -169,10 +169,10 @@ def pseudo_gravity_packing(
is ``None``, which means each call will produce a new realization.
smooth : bool, default = `True`
Controls whether or not the spheres have the small pip each face.
value : int, default = 0
The value of insert for the spheres. The default is 0, which puts solid
inclusions into to the foreground. Values other than 1 (Foreground) make
it easy to add spheres repeated and identify which were added on each step.
value : int, default = 1
The value of insert for the spheres. The default is 1, which puts holes into
the background. Values other than 1 make it easy to add spheres repeatedly
and identify which were added on each step.
Returns
-------
Expand All @@ -195,15 +195,15 @@ def pseudo_gravity_packing(
np.random.seed(seed)

if im is None: # If shape was given, generate empty im
im = np.ones(shape, dtype=bool)
im = np.zeros(shape, dtype=bool)

im = np.swapaxes(im, 0, axis) # Move specified axis to 0 position

if smooth: # If smooth spheres are used, increase r to compensate
r = r + 1

# Shrink or grow mask to allow for clearance
mask = im == 1 # Find mask of valid points
mask = im == 0 # Find mask of valid points
if protrusion > 0: # Dilate mask
dt = edt(~mask)
mask = dt <= protrusion
Expand Down Expand Up @@ -265,7 +265,7 @@ def pseudo_electrostatic_packing(
seed: int = None,
smooth: bool = True,
compactness: float = 1.0,
value: int = 0,
value: int = 1,
):
r"""
Iterativley inserts spheres as close to the given sites as possible.
Expand All @@ -274,9 +274,9 @@ def pseudo_electrostatic_packing(
----------
shape : list
The shape of the image to create. This is equivalent to passing an array
of `True` values of the desired size to `im`.
of `False` values of the desired size to `im`.
im : ndarray
Image with values > 0 indicating the voxels where spheres should be
Image with `False` indicating the voxels where spheres should be
inserted. This can be used to insert spheres into an image that already
has some features (e.g. half filled with larger spheres, or a cylindrical
plug).
Expand Down Expand Up @@ -325,9 +325,9 @@ def pseudo_electrostatic_packing(
Controls how tightly the spheres are grouped together. A value of 1.0
(default) results in the tighest possible grouping while values < 1.0
give more loosely or imperfectly packed spheres.
value : int, default = 0
The value of insert for the spheres. The default is 0, which puts solid
inclusions into to the foreground. Values other than 1 (Foreground) make
value : int, default = 1
The value of insert for the spheres. The default is 1, which puts holes
into to the background. Values other than 1 (Foreground) make
it easy to add spheres repeated and identify which were added on each step.
Returns
Expand All @@ -347,12 +347,12 @@ def pseudo_electrostatic_packing(
np.random.seed(seed)

if im is None: # If shape was given, generate empty im
im = np.ones(shape, dtype=bool)
im = np.zeros(shape, dtype=bool)

if smooth: # If smooth spheres are used, increase r to compensate
r = r + 1

mask = im > 0 # Find mask of valid points
mask = im == 0 # Find mask of valid points
if protrusion > 0: # Dilate mask
dt = edt(~mask)
mask = dt <= protrusion
Expand Down Expand Up @@ -473,7 +473,7 @@ def _do_packing(im, mask, q, r, value, clearance, smooth, maxiter):
if 1:
blobs = ps.generators.blobs([400, 400], porosity=0.75, seed=0)
im = ps.generators.pseudo_electrostatic_packing(
im=blobs,
im=~blobs,
r=10,
clearance=0,
protrusion=0,
Expand All @@ -484,7 +484,7 @@ def _do_packing(im, mask, q, r, value, clearance, smooth, maxiter):
value=2,
)
im2 = ps.generators.pseudo_electrostatic_packing(
im=im == 1,
im=im,
sites=im == 2,
r=5,
clearance=1,
Expand Down Expand Up @@ -564,6 +564,6 @@ def _do_packing(im, mask, q, r, value, clearance, smooth, maxiter):
edges='contained',
seed=0,
smooth=True,
value=4
value=1
)
ax[1][1].imshow(im, origin='lower')
80 changes: 39 additions & 41 deletions test/unit/test_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,59 +265,59 @@ def test_blobs_w_divs(self):
assert np.all(im1 == im2)

def test_random_spheres_2d_contained(self):
im = np.ones([100, 100], dtype=int)
im = np.zeros([100, 100], dtype=int)
im = ps.generators.random_spheres(
im=im, r=10, phi=0.5, edges='contained')
im = np.pad(im, pad_width=1, mode='constant', constant_values=True)
lt = ps.filters.local_thickness(~im, sizes=range(1, 20))
im = np.pad(im, pad_width=1, mode='constant', constant_values=False)
lt = ps.filters.local_thickness(im > 0, sizes=range(1, 20))
assert len(np.unique(lt)) == 2

def test_random_spheres_2d_extended(self):
im = np.ones([100, 100], dtype=int)
im = np.zeros([100, 100], dtype=int)
im = ps.generators.random_spheres(
im=im, r=10, phi=0.5, edges='extended')
im = np.pad(im, pad_width=1, mode='constant', constant_values=True)
lt = ps.filters.local_thickness(~im, sizes=range(1, 20))
im = np.pad(im, pad_width=1, mode='constant', constant_values=False)
lt = ps.filters.local_thickness(im > 0, sizes=range(1, 20))
assert len(np.unique(lt)) > 2

def test_random_spheres_2d_extended_with_clearance(self):
im = np.ones([100, 100], dtype=int)
im = np.zeros([100, 100], dtype=int)
im = ps.generators.random_spheres(
im=im, r=10, phi=0.5, clearance=2, edges='extended')
im = np.pad(im, pad_width=1, mode='constant', constant_values=True)
lt = ps.filters.local_thickness(~im, sizes=range(1, 20))
im = np.pad(im, pad_width=1, mode='constant', constant_values=False)
lt = ps.filters.local_thickness(im > 0, sizes=range(1, 20))
assert len(np.unique(lt)) > 2

def test_random_spheres_3d_contained(self):
im = np.ones([100, 100, 100], dtype=int)
im = np.zeros([100, 100, 100], dtype=int)
im = ps.generators.random_spheres(
im=im, r=10, phi=0.5, edges='contained', smooth=False)
im = np.pad(im, pad_width=1, mode='constant', constant_values=True)
lt = ps.filters.local_thickness(~im, sizes=[10, 9, 8, 7, 6, 5])
im = np.pad(im, pad_width=1, mode='constant', constant_values=False)
lt = ps.filters.local_thickness(im > 0, sizes=[10, 9, 8, 7, 6, 5])
assert len(np.unique(lt)) == 2

def test_random_spheres_3d_extended(self):
im = np.ones([100, 100, 100], dtype=int)
im = np.zeros([100, 100, 100], dtype=int)
im = ps.generators.random_spheres(
im=im, r=10, phi=0.5, edges='extended', smooth=False)
im = np.pad(im, pad_width=1, mode='constant', constant_values=True)
lt = ps.filters.local_thickness(~im, sizes=[10, 9, 8, 7, 6, 5])
im = np.pad(im, pad_width=1, mode='constant', constant_values=False)
lt = ps.filters.local_thickness(im > 0, sizes=[10, 9, 8, 7, 6, 5])
assert len(np.unique(lt)) > 2

def test_random_spheres_2d_seqential_additions(self):
im = np.ones([100, 100], dtype=int)
im = np.zeros([100, 100], dtype=int)
im = ps.generators.random_spheres(im=im, r=10)
phi1 = ps.metrics.porosity(im)
im = ps.generators.random_spheres(im=im, r=5)
phi2 = ps.metrics.porosity(im)
assert phi2 < phi1
assert phi2 > phi1

def test_random_spheres_preexisting_structure(self):
im = ps.generators.blobs(shape=[200, 200, 200])
phi1 = im.sum()/im.size
im = ps.generators.random_spheres(im=im, r=8, maxiter=200, edges='contained')
phi2 = im.sum()/im.size
assert phi2 < phi1
assert phi2 > phi1
# Ensure that 3 passes through random_spheres fills up image
im = ps.generators.random_spheres(im=im, r=8, maxiter=200, edges='contained')
im = ps.generators.random_spheres(im=im, r=8, maxiter=200, edges='contained')
Expand All @@ -336,19 +336,17 @@ def test_random_spheres_clearance_large_spheres(self):
shape=[200, 200], r=9, clearance=0, seed=0)
random_spheres2p = ps.generators.random_spheres(
shape=[200, 200], r=9, clearance=3, seed=0)
assert random_spheres0.sum() < random_spheres2p.sum()
assert random_spheres0.sum() > random_spheres2p.sum()
random_spheres1n = ps.generators.random_spheres(
shape=[200, 200], r=9, clearance=-3, seed=0)
assert random_spheres0.sum() > random_spheres1n.sum()
assert random_spheres0.sum() < random_spheres1n.sum()

def test_random_spheres_clearance_small_spheres(self):
np.random.seed(0)
random_spheres0 = ps.generators.random_spheres(
shape=[200, 200], r=1, clearance=0)
np.random.seed(0)
random_spheres2p = ps.generators.random_spheres(
shape=[200, 200], r=1, clearance=2)
assert random_spheres0.sum() < random_spheres2p.sum()
assert random_spheres0.sum() > random_spheres2p.sum()

def test_random_spheres_w_seed(self):
im1 = ps.generators.random_spheres(shape=[50, 50], r=5, seed=0)
Expand All @@ -372,65 +370,65 @@ def test_line_segment(self):
assert np.all(L3 == [5, 6, 7, 8, 9, 10, 11, 12, 13])

def test_pseudo_gravity_packing_monodisperse(self):
im = np.ones([400, 400], dtype=bool)
im = np.zeros([400, 400], dtype=bool)
im = ps.generators.pseudo_gravity_packing(im=im, r=20, clearance=0, seed=0)
e1 = im.sum()/im.size
im = np.ones([400, 400], dtype=bool)
im = np.zeros([400, 400], dtype=bool)
im = ps.generators.pseudo_gravity_packing(im=im, r=20, clearance=5, seed=0)
e2 = im.sum()/im.size
assert e2 > e1
im = np.ones([400, 400], dtype=bool)
assert e2 < e1
im = np.zeros([400, 400], dtype=bool)
im = ps.generators.pseudo_gravity_packing(im=im, r=20, maxiter=10, seed=0)
e3 = im.sum()/im.size
im = np.ones([400, 400], dtype=bool)
im = np.zeros([400, 400], dtype=bool)
im = ps.generators.pseudo_gravity_packing(im=im, r=50, maxiter=10, seed=0)
e4 = im.sum()/im.size
assert e3 > e4
assert e3 < e4

def test_pseudo_gravity_packing_2D(self):
im = np.ones([100, 100], dtype=bool)
im = np.zeros([100, 100], dtype=bool)
im = ps.generators.pseudo_gravity_packing(
im=im, r=8, clearance=1, seed=0)
assert im.sum() == 4024
assert im.sum() == 5976

def test_pseudo_gravity_packing_3D(self):
im = np.ones([100, 100, 100], dtype=bool)
im = np.zeros([100, 100, 100], dtype=bool)
im = ps.generators.pseudo_gravity_packing(
im=im, r=8, clearance=1, seed=0)
assert im.sum() == 605123 # Used to be 279240
assert im.sum() == 394877

def test_pseudo_gravity_packing_w_seed(self):
im1 = ps.generators.pseudo_gravity_packing(
im=np.ones([50, 50], dtype=bool), r=5, seed=0)
shape=[50, 50], r=5, seed=0)
im2 = ps.generators.pseudo_gravity_packing(
im=np.ones([50, 50], dtype=bool), r=5, seed=0)
shape=[50, 50], r=5, seed=0)
im3 = ps.generators.pseudo_gravity_packing(
im=np.ones([50, 50], dtype=bool), r=5, seed=1)
shape=[50, 50], r=5, seed=1)
assert np.all(im1 == im2)
assert not np.all(im1 == im3)

def test_pseudo_electrostatic_packing(self):
im1 = ps.generators.blobs(shape=[100, 100])
im2 = ps.generators.pseudo_electrostatic_packing(
im=im1, r=3, clearance=1, protrusion=1)
assert (im1.sum() > im2.sum())
assert (im2.sum() > im1.sum())
assert im2.sum() > 0

def test_pseudo_electrostatic_packing_2D(self):
im = np.ones([100, 100], dtype=bool)
im = np.zeros([100, 100], dtype=bool)
sites = np.zeros_like(im)
sites[50, 50] = True
im = ps.generators.pseudo_electrostatic_packing(
im=im, r=8, sites=sites, maxiter=10, seed=0)
assert im.sum() == 7510
assert im.sum() == 2490

def test_pseudo_electrostatic_packing_3D(self):
im = np.ones([100, 100, 100], dtype=bool)
im = np.zeros([100, 100, 100], dtype=bool)
sites = np.zeros_like(im)
sites[50, 50, 50] = True
im = ps.generators.pseudo_electrostatic_packing(
im=im, r=8, sites=sites, maxiter=10, seed=0)
assert im.sum() == 970310 # Used to be 21030
assert im.sum() == 29690

def test_pseudo_electrostatic_packing_w_seed(self):
im1 = ps.generators.pseudo_electrostatic_packing(
Expand Down

0 comments on commit ce4155c

Please sign in to comment.