Source code for pumapy.segmentation.ccl

"""
Functions to relying on Connected Component Labeling (CCL)
"""
import numpy as np
from skimage import measure
from pumapy.utilities.generic_checks import check_ws_cutoff


[docs]def identify_porespace(workspace, solid_cutoff, connectivity=None): """ Identify the porespace :param workspace: domain :type workspace: pumapy.Workspace :param solid_cutoff: specify the solid range to discard from pores identification :type solid_cutoff: (int, int) :param connectivity: Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor (None automatically gives full connectivity of ``input.ndim``, which for 3D domain is 3, for 2D is 2) :type connectivity: int or None :return: porespace marked as: 0 solid, 1 largest pore (likely open porosity), >1 other pores :rtype: np.ndarray :Example: >>> import pumapy as puma >>> ws = puma.generate_sphere((100, 100, 100), (50, 50, 50), 40, segmented=False) Generated in... >>> ws.binarize_range((1, 253)) >>> # puma.render_volume(ws[:ws.matrix.shape[0] // 2], cutoff=(0, 255)) # to visualize workspace >>> pores = puma.identify_porespace(ws, (1, 1)) >>> # puma.render_volume(pores[:pores.shape[0] // 2], cutoff=(0, pores.max())) # to visualize pores """ # error check check_ws_cutoff(workspace, solid_cutoff) ws = workspace.copy() # logging workspace.log.log_section("Identifying Porespace") workspace.log.log_value("Solid Cutoff: ", solid_cutoff) workspace.log.write_log() ws.binarize_range(solid_cutoff) # padding with solid to detect pores along the borders array = np.ones((ws.matrix.shape[0] + 2, ws.matrix.shape[1] + 2, ws.matrix.shape[2] + 2)) array[1:-1, 1:-1, 1:-1] = ws.matrix pore_labels = measure.label(array, background=1, connectivity=connectivity) pore_labels = pore_labels[1:-1, 1:-1, 1:-1] unique_pore_ids, unique_id_counts = np.unique(pore_labels[pore_labels != 0], return_counts=True) sorted_unique_pore_ids = unique_pore_ids[np.argsort(unique_id_counts)[::-1]] keyarray = np.arange(np.max(pore_labels) + 1) keyarray[sorted_unique_pore_ids] = unique_pore_ids return keyarray[pore_labels]
[docs]def fill_closed_pores(workspace, solid_cutoff, fill_value, return_pores=False): """ Identify the porespace and fill closed porosity :param workspace: domain :type workspace: pumapy.Workspace :param solid_cutoff: specify the solid range to discard from pores identification :type solid_cutoff: (int, int) :param fill_value: value to fill closed porosity with :type fill_value: int :param return_pores: specifies whether to return identified pores :type return_pores: bool :return: filled workspace with IDs set as: 0 for void, 1 for solid, fill_value for added filler material. In addition, if return_pores==True, then it also returns the porespace marked as: 0 solid, 1 largest pore (likely open porosity), >1 other pores :rtype: pumapy.Workspace :Example: >>> import pumapy as puma >>> ws = puma.generate_random_spheres((100, 100, 100), diameter=20, porosity=0.8, allow_intersect=True, segmented=False) Approximately ... spheres to be generated... >>> # puma.render_volume(ws[:ws.matrix.shape[0] // 2]) # to visualize workspace >>> ws.binarize_range((1, 250)) >>> # puma.render_volume(ws[:ws.matrix.shape[0] // 2]) >>> filled_ws, pores = puma.fill_closed_pores(ws, (1, 1), fill_value=2, return_pores=True) >>> # puma.render_volume(pores, cutoff=(0, pores.max()), cmap='jet') # to visualize pores """ pores = identify_porespace(workspace, solid_cutoff) if isinstance(pores, bool): return False ws = workspace.copy() ws.binarize_range(solid_cutoff) ws[np.where(pores > 1)] = np.uint16(fill_value) if return_pores: return ws, pores else: return ws