tools

Helper Functions

This module contains a variety of functions for manipulating images in ways that do NOT return a modified version of the original image.

porespy.tools.align_image_with_openpnm(im) Rotates an image to agree with the coordinates used in OpenPNM.
porespy.tools.bbox_to_slices(bbox) Given a tuple containing bounding box coordinates, return a tuple of slice objects.
porespy.tools.extend_slice(s, shape[, pad]) Adjust slice indices to include additional voxles around the slice.
porespy.tools.extract_subsection(im, shape) Extracts the middle section of a image
porespy.tools.extract_regions(regions, labels) Combine given regions into a single boolean mask
porespy.tools.extract_cylinder(im[, r, axis]) Returns a cylindrical section of the image of specified radius.
porespy.tools.extract_subsection(im, shape) Extracts the middle section of a image
porespy.tools.fftmorphology(im, strel[, mode]) Perform morphological operations on binary images using fft approach for improved performance
porespy.tools.find_outer_region(im[, r]) Finds regions of the image that are outside of the solid matrix.
porespy.tools.get_border(shape[, thickness, …]) Creates an array of specified size with corners, edges or faces labelled as True.
porespy.tools.get_planes(im[, squeeze]) Extracts three planar images from the volumetric image, one for each principle axis.
porespy.tools.insert_cylinder(im, xyz0, xyz1, r) Inserts a cylinder of given radius onto a given image
porespy.tools.insert_sphere(im, c, r) Inserts a sphere of a specified radius into a given image
porespy.tools.in_hull(points, hull) Test if a list of coordinates are inside a given convex hull
porespy.tools.make_contiguous(im[, keep_zeros]) Take an image with arbitrary greyscale values and adjust them to ensure all values fall in a contiguous range starting at 0.
porespy.tools.mesh_region(region[, strel]) Creates a tri-mesh of the provided region using the marching cubes algorithm
porespy.tools.norm_to_uniform(im[, scale]) Take an image with normally distributed greyscale values and convert it to a uniform (i.e.
porespy.tools.overlay(im1, im2, c) Overlays im2 onto im1, given voxel coords of center of im2 in im1.
porespy.tools.randomize_colors(im[, keep_vals]) Takes a greyscale image and randomly shuffles the greyscale values, so that all voxels labeled X will be labelled Y, and all voxels labeled Y will be labeled Z, where X, Y, Z and so on are randomly selected from the values in the input image.
porespy.tools.subdivide(im[, divs]) Returns slices into an image describing the specified number of sub-arrays.
porespy.tools.ps_disk(radius) Creates circular disk structuring element for morphological operations
porespy.tools.ps_ball(radius) Creates spherical ball structuring element for morphological operations
porespy.tools.pad_faces(im, faces) Pads the input image at specified faces.
porespy.tools.align_image_with_openpnm(im)[source]

Rotates an image to agree with the coordinates used in OpenPNM. It is unclear why they are not in agreement to start with. This is necessary for overlaying the image and the network in Paraview.

Parameters:im (ND-array) – The image to be rotated. Can be the Boolean image of the pore space or any other image of interest.
Returns:image – Returns a copy of im rotated accordingly.
Return type:ND-array
porespy.tools.bbox_to_slices(bbox)[source]

Given a tuple containing bounding box coordinates, return a tuple of slice objects.

A bounding box in the form of a straight list is returned by several functions in skimage, but these cannot be used to direct index into an image. This function returns a tuples of slices can be, such as: im[bbox_to_slices([xmin, ymin, xmax, ymax])].

Parameters:bbox (tuple of ints) – The bounding box indices in the form (xmin, ymin, zmin, xmax, ymax, zmax). For a 2D image, simply omit the zmin and zmax entries.
Returns:slices – A tuple of slice objects that can be used to directly index into a larger image.
Return type:tuple
porespy.tools.extend_slice(s, shape, pad=1)[source]

Adjust slice indices to include additional voxles around the slice.

This function does bounds checking to ensure the indices don’t extend outside the image.

Parameters:
  • s (list of slice objects) – A list (or tuple) of N slice objects, where N is the number of dimensions in the image.
  • shape (array_like) – The shape of the image into which the slice objects apply. This is used to check the bounds to prevent indexing beyond the image.
  • pad (int) – The number of voxels to expand in each direction.
Returns:

slices – A list slice of objects with the start and stop attributes respectively incremented and decremented by 1, without extending beyond the image boundaries.

Return type:

list of slice objects

Examples

>>> from scipy.ndimage import label, find_objects
>>> from porespy.tools import extend_slice
>>> im = sp.array([[1, 0, 0], [1, 0, 0], [0, 0, 1]])
>>> labels = label(im)[0]
>>> s = find_objects(labels)

Using the slices returned by find_objects, set the first label to 3

>>> labels[s[0]] = 3
>>> print(labels)
[[3 0 0]
 [3 0 0]
 [0 0 2]]

Next extend the slice, and use it to set the values to 4

>>> s_ext = extend_slice(s[0], shape=im.shape, pad=1)
>>> labels[s_ext] = 4
>>> print(labels)
[[4 4 0]
 [4 4 0]
 [4 4 2]]

As can be seen by the location of the 4s, the slice was extended by 1, and also handled the extension beyond the boundary correctly.

porespy.tools.extract_cylinder(im, r=None, axis=0)[source]

Returns a cylindrical section of the image of specified radius.

This is useful for making square images look like cylindrical cores such as those obtained from X-ray tomography.

Parameters:
  • im (ND-array) – The image of the porous material. Can be any data type.
  • r (scalr) – The radius of the cylinder to extract. If None is given then the default is the largest cylinder that can fit inside the specified plane.
  • axis (scalar) – The axis along with the cylinder will be oriented.
Returns:

image – A copy of im with values outside the cylindrical area set to 0 or False.

Return type:

ND-array

porespy.tools.extract_regions(regions, labels: list, trim=True)[source]

Combine given regions into a single boolean mask

Parameters:
  • regions (ND-array) – An image containing an arbitrary number of labeled regions
  • labels (array_like or scalar) – A list of labels indicating which region or regions to extract
  • trim (bool) – If True then image shape will trimmed to a bounding box around the given regions.
Returns:

im – A boolean mask with True values indicating where the given labels exist

Return type:

ND-array

porespy.tools.extract_subsection(im, shape)[source]

Extracts the middle section of a image

Parameters:
  • im (ND-array) – Image from which to extract the subsection
  • shape (array_like) – Can either specify the size of the extracted section or the fractional size of the image to extact.
Returns:

image – An ND-array of size given by the shape argument, taken from the center of the image.

Return type:

ND-array

Examples

>>> import scipy as sp
>>> from porespy.tools import extract_subsection
>>> im = sp.array([[1, 1, 1, 1], [1, 2, 2, 2], [1, 2, 3, 3], [1, 2, 3, 4]])
>>> print(im)
[[1 1 1 1]
 [1 2 2 2]
 [1 2 3 3]
 [1 2 3 4]]
>>> im = extract_subsection(im=im, shape=[2, 2])
>>> print(im)
[[2 2]
 [2 3]]
porespy.tools.fftmorphology(im, strel, mode='opening')[source]

Perform morphological operations on binary images using fft approach for improved performance

Parameters:
  • im (nd-array) – The binary image on which to perform the morphological operation
  • strel (nd-array) – The structuring element to use. Must have the same dims as im.
  • mode (string) – The type of operation to perform. Options are ‘dilation’, ‘erosion’, ‘opening’ and ‘closing’.
Returns:

image – A copy of the image with the specified moropholgical operation applied using the fft-based methods available in scipy.fftconvolve.

Return type:

ND-array

Notes

This function uses scipy.signal.fftconvolve which can be more than 10x faster than the standard binary morphology operation in scipy.ndimage. This speed up may not always be realized, depending on the scipy distribution used.

Examples

>>> import porespy as ps
>>> from numpy import array_equal
>>> import scipy.ndimage as spim
>>> from skimage.morphology import disk
>>> im = ps.generators.blobs(shape=[100, 100], porosity=0.8)

Check that erosion, dilation, opening, and closing are all the same as the scipy.ndimage functions:

>>> result = ps.filters.fftmorphology(im, strel=disk(5), mode='erosion')
>>> temp = spim.binary_erosion(im, structure=disk(5))
>>> array_equal(result, temp)
True
>>> result = ps.filters.fftmorphology(im, strel=disk(5), mode='dilation')
>>> temp = spim.binary_dilation(im, structure=disk(5))
>>> array_equal(result, temp)
True
>>> result = ps.filters.fftmorphology(im, strel=disk(5), mode='opening')
>>> temp = spim.binary_opening(im, structure=disk(5))
>>> array_equal(result, temp)
True
>>> result = ps.filters.fftmorphology(im, strel=disk(5), mode='closing')
>>> temp = spim.binary_closing(im, structure=disk(5))
>>> array_equal(result, temp)
True
porespy.tools.find_outer_region(im, r=0)[source]

Finds regions of the image that are outside of the solid matrix.

This function uses the rolling ball method to define where the outer region ends and the void space begins.

This function is particularly useful for samples that do not fill the entire rectangular image, such as cylindrical cores or samples with non- parallel faces.

Parameters:
  • im (ND-array) – Image of the porous material with 1’s for void and 0’s for solid
  • r (scalar) – The radius of the rolling ball to use. If not specified then a value is calculated as twice maximum of the distance transform. The image size is padded by this amount in all directions, so the image can become quite large and unwieldy if too large a value is given.
Returns:

image – A boolean mask the same shape as im, containing True in all voxels identified as outside the sample.

Return type:

ND-array

porespy.tools.get_border(shape, thickness=1, mode='edges', return_indices=False)[source]

Creates an array of specified size with corners, edges or faces labelled as True. This can be used as mask to manipulate values laying on the perimeter of an image.

Parameters:
  • shape (array_like) – The shape of the array to return. Can be either 2D or 3D.
  • thickness (scalar (default is 1)) – The number of pixels/voxels to place along perimeter.
  • mode (string) – The type of border to create. Options are ‘faces’, ‘edges’ (default) and ‘corners’. In 2D ‘faces’ and ‘edges’ give the same result.
  • return_indices (boolean) – If False (default) an image is returned with the border voxels set to True. If True, then a tuple with the x, y, z (if im is 3D) indices is returned. This tuple can be used directly to index into the image, such as im[tup] = 2.
  • asmask (Boolean) – If True (default) then an image of the specified shape is returned, otherwise indices of the border voxels are returned.
Returns:

image – An ND-array of specified shape with True values at the perimeter and False elsewhere

Return type:

ND-array

Notes

TODO: This function uses brute force to create an image then fill the edges using location-based logic, and if the user requests return_indices it finds them using np.where. Since these arrays are cubic it should be possible to use more elegant and efficient index-based logic to find the indices, then use them to fill an empty image with True using these indices.

Examples

>>> import porespy as ps
>>> import scipy as sp
>>> mask = ps.tools.get_border(shape=[3, 3], mode='corners')
>>> print(mask)
[[ True False  True]
 [False False False]
 [ True False  True]]
>>> mask = ps.tools.get_border(shape=[3, 3], mode='edges')
>>> print(mask)
[[ True  True  True]
 [ True False  True]
 [ True  True  True]]
porespy.tools.get_planes(im, squeeze=True)[source]

Extracts three planar images from the volumetric image, one for each principle axis. The planes are taken from the middle of the domain.

Parameters:
  • im (ND-array) – The volumetric image from which the 3 planar images are to be obtained
  • squeeze (boolean, optional) – If True (default) the returned images are 2D (i.e. squeezed). If False, the images are 1 element deep along the axis where the slice was obtained.
Returns:

planes – A list of 2D-images

Return type:

list

porespy.tools.insert_cylinder(im, xyz0, xyz1, r)[source]

Inserts a cylinder of given radius onto a given image

Parameters:
  • im (array_like) – Original voxelated image
  • xyz1 (xyz0,) – Voxel coordinates of the two end points of the cylinder
  • r (int) – Radius of the cylinder
Returns:

im – Original voxelated image overlayed with the cylinder

Return type:

ND-array

Notes

This function is only implemented for 3D images

porespy.tools.insert_sphere(im, c, r)[source]

Inserts a sphere of a specified radius into a given image

Parameters:
  • im (array_like) – Image into which the sphere should be inserted
  • c (array_like) – The [x, y, z] coordinate indicating the center of the sphere
  • r (int) – The radius of sphere to insert
Returns:

image – The original image with a sphere inerted at the specified location

Return type:

ND-array

porespy.tools.in_hull(points, hull)[source]

Test if a list of coordinates are inside a given convex hull

Parameters:
  • points (array_like (N x ndims)) – The spatial coordinates of the points to check
  • hull (scipy.spatial.ConvexHull object OR array_like) – Can be either a convex hull object as returned by scipy.spatial.ConvexHull or simply the coordinates of the points that define the convex hull.
Returns:

result – A 1D-array Boolean array of length N indicating whether or not the given points in points lies within the provided hull.

Return type:

1D-array

porespy.tools.make_contiguous(im, keep_zeros=True)[source]

Take an image with arbitrary greyscale values and adjust them to ensure all values fall in a contiguous range starting at 0.

This function will handle negative numbers such that most negative number will become 0, unless keep_zeros is True in which case it will become 1, and all 0’s in the original image remain 0.

Parameters:
  • im (array_like) – An ND array containing greyscale values
  • keep_zeros (Boolean) – If True (default) then 0 values remain 0, regardless of how the other numbers are adjusted. This is mostly relevant when the array contains negative numbers, and means that -1 will become +1, while 0 values remain 0.
Returns:

image – An ND-array the same size as im but with all values in contiguous orders.

Return type:

ND-array

Example

>>> import porespy as ps
>>> import scipy as sp
>>> im = sp.array([[0, 2, 9], [6, 8, 3]])
>>> im = ps.tools.make_contiguous(im)
>>> print(im)
[[0 1 5]
 [3 4 2]]
porespy.tools.mesh_region(region: bool, strel=None)[source]

Creates a tri-mesh of the provided region using the marching cubes algorithm

Parameters:
  • im (ND-array) – A boolean image with True values indicating the region of interest
  • strel (ND-array) – The structuring element to use when blurring the region. The blur is perfomed using a simple convolution filter. The point is to create a greyscale region to allow the marching cubes algorithm some freedom to conform the mesh to the surface. As the size of strel increases the region will become increasingly blurred and inaccurate. The default is a spherical element with a radius of 1.
Returns:

mesh – A named-tuple containing faces, verts, norm, and val as returned by scikit-image.measure.marching_cubes function.

Return type:

tuple

porespy.tools.norm_to_uniform(im, scale=None)[source]

Take an image with normally distributed greyscale values and convert it to a uniform (i.e. flat) distribution.

Parameters:
  • im (ND-image) – The image containing the normally distributed scalar field
  • scale ([low, high]) – A list or array indicating the lower and upper bounds for the new randomly distributed data. The default is None, which uses the max and min of the original image as the the lower and upper bounds, but another common option might be [0, 1].
Returns:

image – A copy of im with uniformly distributed greyscale values spanning the specified range, if given.

Return type:

ND-array

porespy.tools.overlay(im1, im2, c)[source]

Overlays im2 onto im1, given voxel coords of center of im2 in im1.

Parameters:
  • im1 (ND-array) – Original voxelated image
  • im2 (ND-array) – Template voxelated image
  • c (array_like) – [x, y, z] coordinates in im1 where im2 will be centered
Returns:

image – A modified version of im1, with im2 overlaid at the specified location

Return type:

ND-array

porespy.tools.ps_disk(radius)[source]

Creates circular disk structuring element for morphological operations

Parameters:radius (float or int) – The desired radius of the structuring element
Returns:strel – A 2D numpy bool array of the structring element
Return type:2D-array
porespy.tools.ps_ball(radius)[source]

Creates spherical ball structuring element for morphological operations

Parameters:radius (float or int) – The desired radius of the structuring element
Returns:strel – A 3D numpy array of the structuring element
Return type:3D-array
porespy.tools.pad_faces(im, faces)[source]

Pads the input image at specified faces. This shape of image is same as the output image of add_boundary_regions function.

Parameters:
  • im (ND_array) – The image that needs to be padded
  • faces (list of strings) –

    Labels indicating where image needs to be padded. Given a 3D image of shape [x, y, z] = [i, j, k], the following conventions are used to indicate along which axis the padding should be applied:

    • ’left’ -> x = 0
    • ’right’ -> x = i
    • ’front’ -> y = 0
    • ’back’ -> y = j
    • ’bottom’ -> z = 0
    • ’top’ -> z = k
Returns:

Return type:

A image padded at specified face(s)

See also

add_boundary_regions()

porespy.tools.randomize_colors(im, keep_vals=[0])[source]

Takes a greyscale image and randomly shuffles the greyscale values, so that all voxels labeled X will be labelled Y, and all voxels labeled Y will be labeled Z, where X, Y, Z and so on are randomly selected from the values in the input image.

This function is useful for improving the visibility of images with neighboring regions that are only incrementally different from each other, such as that returned by scipy.ndimage.label.

Parameters:
  • im (array_like) – An ND image of greyscale values.
  • keep_vals (array_like) – Indicate which voxel values should NOT be altered. The default is [0] which is useful for leaving the background of the image untouched.
Returns:

image – An image the same size and type as im but with the greyscale values reassigned. The unique values in both the input and output images will be identical.

Return type:

ND-array

Notes

If the greyscale values in the input image are not contiguous then the neither will they be in the output.

Examples

>>> import porespy as ps
>>> import scipy as sp
>>> sp.random.seed(0)
>>> im = sp.random.randint(low=0, high=5, size=[4, 4])
>>> print(im)
[[4 0 3 3]
 [3 1 3 2]
 [4 0 0 4]
 [2 1 0 1]]
>>> im_rand = ps.tools.randomize_colors(im)
>>> print(im_rand)
[[2 0 4 4]
 [4 1 4 3]
 [2 0 0 2]
 [3 1 0 1]]

As can be seen, the 2’s have become 3, 3’s have become 4, and 4’s have become 2. 1’s remained 1 by random accident. 0’s remain zeros by default, but this can be controlled using the keep_vals argument.

porespy.tools.subdivide(im, divs=2)[source]

Returns slices into an image describing the specified number of sub-arrays.

This function is useful for performing operations on smaller images for memory or speed. Note that for most typical operations this will NOT work, since the image borders would cause artifacts (e.g. distance_transform)

Parameters:
  • im (ND-array) – The image of the porous media
  • divs (scalar or array_like) – The number of sub-divisions to create in each axis of the image. If a scalar is given it is assumed this value applies in all dimensions.
Returns:

slices – A 1-D array containing slice objects for indexing into im that extract the sub-divided arrays.

Return type:

1D-array

Notes

This method uses the array_split package which offers the same functionality as the split method of Numpy’s ND-array, but supports the splitting multidimensional arrays in all dimensions.

Examples

>>> import porespy as ps
>>> import matplotlib.pyplot as plt
>>> im = ps.generators.blobs(shape=[200, 200])
>>> s = ps.tools.subdivide(im, divs=[2, 2])

s contains an array with the shape given by divs. To access the first and last quadrants of im use: >>> print(im[tuple(s[0, 0])].shape) (100, 100) >>> print(im[tuple(s[1, 1])].shape) (100, 100)

It can be easier to index the array with the slices by applying flatten first: >>> s_flat = s.flatten() >>> for i in s_flat: … print(im[i].shape) (100, 100) (100, 100) (100, 100) (100, 100)