# Source code for porespy.metrics.__regionprops__

import scipy as sp
import scipy.ndimage as spim
from tqdm import tqdm
from porespy.tools import extract_subsection, bbox_to_slices
from skimage.measure import regionprops
from skimage.measure import mesh_surface_area, marching_cubes_lewiner
from skimage.morphology import skeletonize_3d, ball
from pandas import DataFrame

[docs]def props_to_DataFrame(regionprops):
r"""
Returns a Pandas DataFrame containing all the scalar metrics for each
region, such as volume, sphericity, and so on, calculated by
regionprops_3D.

Parameters
----------
regionprops : list
This is a list of properties for each region that is computed
by regionprops_3D.  Because regionprops_3D returns data in
the same list format as the regionprops function in **Skimage**
you can pass in either.

Returns
-------
DataFrame : Pandas DataFrame
A Pandas DataFrame with each region corresponding to a row and each
column corresponding to a key metric.  All the values for a given
property (e.g. 'sphericity') can be obtained as
val = df['sphericity'].  Conversely, all the key metrics for a
given region can be found with df.iloc[1].

--------
props_to_image
regionprops_3d
"""
# Parse the regionprops list and pull out all props with scalar values
metrics = []
reg = regionprops[0]
for item in reg.__dir__():
if not item.startswith('_'):
try:
if sp.shape(getattr(reg, item)) == ():
metrics.append(item)
except (TypeError, NotImplementedError, AttributeError):
pass
# Create a dictionary of all metrics that are simple scalar propertie
d = {}
for k in metrics:
try:
d[k] = sp.array([r[k] for r in regionprops])
except ValueError:
print('Error encountered evaluating ' + k + ' so skipping it')
# Create pandas data frame an return
df = DataFrame(d)
return df

[docs]def props_to_image(regionprops, shape, prop):
r"""
Creates an image with each region colored according the specified prop,
as obtained by regionprops_3d.

Parameters
----------
regionprops : list
This is a list of properties for each region that is computed
by PoreSpy's regionprops_3D or Skimage's regionsprops.

shape : array_like
The shape of the original image for which regionprops was obtained.

prop : string
The region property of interest.  Can be a scalar item such as 'volume'
in which case the the regions will be colored by their respective
volumes, or can be an image-type property such as 'border' or
'convex_image', which will return an image composed of the sub-images.

Returns
-------
image : ND-array
An ND-image the same size as the original image, with each region
represented by the values specified in prop.

--------
props_to_DataFrame
regionprops_3d

"""
im = sp.zeros(shape=shape)
for r in regionprops:
if prop == 'convex':
else:
s = bbox_to_slices(r.bbox)
im[s] += temp
return im

[docs]def regionprops_3D(im):
r"""
Calculates various metrics for each labeled region in a 3D image.

The regionsprops method in **skimage** is very thorough for 2D images,
but is a bit limited when it comes to 3D images, so this function aims
to fill this gap.

Parameters
----------
im : array_like
An imaging containing at least one labeled region.  If a boolean image
is received than the True voxels are treated as a single region
labeled 1.  Regions labeled 0 are ignored in all cases.

Returns
-------
props : list
An augmented version of the list returned by skimage's regionprops.
Information, such as volume, can be found for region A using the
following syntax: result[A-1].volume.

The returned list contains all the metrics normally returned by
**skimage.measure.regionprops** plus the following:

'slice': Slice indices into the image that can be used to extract the
region

'volume': Volume of the region in number of voxels.

'bbox_volume': Volume of the bounding box that contains the region.

'border': The edges of the region, found as the locations where
the distance transform is 1.

'inscribed_sphere': An image containing the largest sphere can can
fit entirely inside the region.

'surface_mesh_vertices': Obtained by applying the marching cubes
algorithm on the region, AFTER first blurring the voxel image.  This
allows marching cubes more freedom to fit the surface contours. See
also surface_mesh_simplices

'surface_mesh_simplices': This accompanies surface_mesh_vertices
and together they can be used to define the region as a mesh.

'surface_area': Calculated using the mesh obtained as described above,
using the porespy.metrics.mesh_surface_area method.

'sphericity': Defined as the ratio of the area of a sphere with the
same volume as the region to the actual surface area of the region.

'skeleton': The medial axis of the region obtained using the
skeletonize_3D method from **skimage**.

'convex_volume': Same as convex_area, but translated to a more
meaningful name.

--------
snow_partitioning

Notes
-----
This function may seem slow compared to the skimage version, but that is
because they defer calculation of certain properties until they are
accessed, while this one evalulates everything (inlcuding the deferred
properties from skimage's regionprops)

Regions can be identified using a watershed algorithm, which can be a bit
tricky to obtain desired results.  *PoreSpy* includes the SNOW algorithm,

"""
print('_'*60)
print('Calculating regionprops')

results = regionprops(im, coordinates='xy')
for i in tqdm(range(len(results))):
# ---------------------------------------------------------------------
# Slice indices
results[i].slice = results[i]._slice
# ---------------------------------------------------------------------
# Volume of regions in voxels
results[i].volume = results[i].area
# ---------------------------------------------------------------------
# Volume of bounding box, in voxels
# ---------------------------------------------------------------------
# Create an image of the border
results[i].border = dt == 1
# ---------------------------------------------------------------------
# Create an image of the maximal inscribed sphere
r = dt.max()
inv_dt = spim.distance_transform_edt(dt < r)
results[i].inscribed_sphere = inv_dt < r
# ---------------------------------------------------------------------
# Find surface area using marching cubes and analyze the mesh
tmp = spim.convolve(tmp, weights=ball(1))/5
verts, faces, norms, vals = marching_cubes_lewiner(volume=tmp, level=0)
results[i].surface_mesh_vertices = verts
results[i].surface_mesh_simplices = faces
area = mesh_surface_area(verts, faces)
results[i].surface_area = area
# ---------------------------------------------------------------------
# Find sphericity
vol = results[i].volume
r = (3/4/sp.pi*vol)**(1/3)
a_equiv = 4*sp.pi*(r)**2
a_region = results[i].surface_area
results[i].sphericity = a_equiv/a_region
# ---------------------------------------------------------------------
# Find skeleton of region