MAGNET Network Extraction#

The MAGNET algorithm uses the traditional medial axis approach for extracting a network from an image. It makes use of the skeleton by locating pores as the junctions or endpoints of the skeleton.

import matplotlib.pyplot as plt
import numpy as np
import openpnm as op

import porespy as ps

ps.visualization.set_mpl_style()
np.random.seed(10)
[13:30:31] WARNING  PARDISO solver not installed on this platform. Simulations will be slow.       _workspace.py:56

Generate an Image#

im = ps.generators.blobs(shape=[400, 400], porosity=0.6, blobiness=2)
fig, ax = plt.subplots(figsize=(4, 4))
ax.imshow(im);

MAGNET uses a series of filters to extract a pore network but porespy has one function that does all this work for you!

magnet_output = ps.networks.magnet(im, voxel_size=1)
[13:30:34] WARNING  Dropped 1 skeleton fragment(s) without two adjacent junctions                    _magnet.py:671

The magnet function returns an object that has a network attribute. This is a dictionary that is suitable for loading into OpenPNM. The best way to get this into OpenPNM is to use the PoreSpy IO class. This splits the data into a network and a geometry:

pn = op.io.network_from_porespy(magnet_output.network)

As can be seen by printing the network it contains quite a lot of geometric information:

print(pn)
══════════════════════════════════════════════════════════════════════════════
net : <openpnm.network.Network at 0x7f4e1cf598b0>
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  #  Properties                                                   Valid Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  2  throat.conns                                                    326 / 326
  3  pore.coords                                                     301 / 301
  4  throat.actual_length                                            326 / 326
  5  throat.area                                                     326 / 326
  6  throat.max_diameter                                             326 / 326
  7  throat.min_diameter                                             326 / 326
  8  throat.avg_diameter                                             326 / 326
  9  throat.inscribed_diameter                                       326 / 326
 10  throat.integrated_diameter                                      326 / 326
 11  pore.inscribed_diameter                                         301 / 301
 12  pore.equivalent_diameter                                        301 / 301
 13  pore.index                                                      301 / 301
 14  param.voxel_size                                                 1 / None
 15  param.ndim                                                       1 / None
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  #  Labels                                                 Assigned Locations
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

You can also overlay the network on the image natively in porespy. Note that you need to transpose the image using im.T, since imshow uses matrix representation, e.g. a (10, 20)-shaped array is shown as 10 pixels in the y-axis, and 20 pixels in the x-axis.

fig, ax = plt.subplots(figsize=[5, 5])
ax.imshow(im.T, cmap=plt.cm.bone)
op.visualization.plot_coordinates(
    ax=fig,
    network=pn,
    size_by=pn["pore.inscribed_diameter"],
    color_by=pn["pore.inscribed_diameter"],
    markersize=200,
)
op.visualization.plot_connections(network=pn, ax=fig)
ax.axis("off");