import numpy as np
import cc3d
from upxo.viz import gsviz
import upxo.gsdataops.gid_ops as gidOps
import upxo.gsdataops.grid_ops as gridOps
import upxo.propOps.mpropOps as mpropOps
import upxo.uiOps.outputDisplay as opDisp
import upxo.gbops.grainBoundOps3d as gbOps
import upxo.flags_and_controls.flags as FLAGS
[docs]
class geometryfi3d:
"""
Usage
-----
from upxo.pxtalops.vox2geom import geometryfi3d
"""
__slots__ = ('lfi', 'fid', 'n', 'mprops', 'neigh_fid', 'spb', 'bboxes',
'scaleHistory'
)
n_plotTypes = FLAGS.n_plotType_3D_voxels
def __init__(self, lfi):
"""Initialise the instance."""
self.lfi = lfi
self.mprops = {}
self.scaleHistory = {0: 1}
self.bboxes = None
def __repr__(self):
"""Return a string representation of this instance."""
return f"VOX.2.GEOM. MID.{id(self)}"
# ================================================================================================
# NEIGHBOUR OPERATIONS
[docs]
def find_neigh(self):
"""Find neigh."""
neigh_fids = gidOps.find_neighs3d(self.lfi, 6)
return neigh_fids
[docs]
def get_feature_sizes(self):
"""Return the feature sizes."""
return mpropOps.get_feature_volumes(self.lfi)
[docs]
def find_mprops(self):
"""Find mprops."""
self.mprops['nvox'] = self.get_feature_sizes()
# ================================================================================================
# VOXEL GRID SCALING OPERATIONS
[docs]
def scale(self, scaleFactor, reindex=False, plotgs=True, alpha=1.0, cmap='nipy_spectral',
quickPlot_kwargs={'scalar_name': 'lfi', 'alpha': 0.75, 'cmap': 'viridis'}):
"""Scale."""
self.lfi = gridOps.rescale_grid_3d(self.lfi, scaleFactor, method='nearest')
if reindex:
self.reindex(findMprops=False, plotgs=False, quickPlot_kwargs=quickPlot_kwargs)
self.quick_plot(**quickPlot_kwargs) if plotgs else None
# ================================================================================================
# COMMON INTERFACE BOUNDARY DETECTION
[docs]
def find_spb(self):
"""Find spb."""
from skimage.segmentation import find_boundaries
spb = find_boundaries(self.lfi, connectivity=2, mode='subpixel', background=0)
# ================================================================================================
# VOXEL ID RENUMBERINGS & ASSOCIATED OPERATIONS
[docs]
def reindex(self, findMprops=True, plotgs=True,
quickPlot_kwargs={'scalar_name': 'lfi', 'alpha': 0.75, 'cmap': 'viridis'}):
"""Reindex the feature IDs to be contiguous and start from 0."""
self.lfi = gridOps.shuffle_feature_IDs(cc3d.connected_components(self.lfi,
connectivity=6, out_dtype=np.uint32).astype(np.int32))
self.fid = np.unique(self.lfi)
self.neigh_fid = self.find_neigh()
if findMprops:
self.find_mprops()
self.quick_plot(**quickPlot_kwargs) if plotgs else None
# ================================================================================================
# VOXEL GRAIN BOUNDARY CLEANING OPERATIONS - GLOBAL MORPHOLOGICAL OPERATIONS
[docs]
def prepare(self, run_pre_cleaning_step=True):
"""Prepare."""
if run_pre_cleaning_step:
self.prepare_pre_vox_cleaning(plotgs=False)
self.cleanVoxMorph_DE_npass(niterations=2,
DILfpSizes=[4, 4], ERSfpSizes=[4, 4],
footprints=['ball', 'ball'], removeEndVox=[True, True], plotgs=False)
self.scale(2, plotgs=False)
[docs]
def prepare_pre_vox_cleaning(self, plotgs=False,
quickPlot_kwargs={'scalar_name': 'lfi', 'alpha': 0.75, 'cmap': 'viridis'}):
"""Prepare pre vox cleaning."""
self.report(message="Pre-Initial cleaning report: \n")
print("\nInitiating pre-cleaning")
self.reindex(findMprops=False, plotgs=False)
self.detect_and_merge_islands()
self.reindex(findMprops=True, plotgs=False)
self.quick_plot(**quickPlot_kwargs) if plotgs else None
self.report(message="Post-Initial cleaning report: \n")
# ================================================================================================
[docs]
def detect_and_merge_islands(self):
"""Detect and merge islands."""
islands = self.detect_islands(self.neigh_fid)
if len(islands) > 0:
self.lfi = self.merge_islands(self.lfi, islands, self.neigh_fid)
self.reindex(plotgs=False)
[docs]
def detect_islands(self, neigh_fids):
"""Detect islands."""
return gidOps.detect_islands(neigh_fids)
[docs]
def merge_islands(self, lfi, islands, neigh_fids):
"""Merge islands."""
for island in islands:
lfi[lfi==island] = neigh_fids[island]
return lfi
# ================================================================================================
[docs]
def filter_small_inconsistant_features(self):
"""Filter small inconsistant features."""
raise NotImplementedError("filter_small_inconsistant_features is not yet implemented.")
# ================================================================================================
[docs]
def voxel_smoother(self, addMajFilter=True, majority_filter_kwargs={'n': 2, 'sizes': [3, 3]},
addDilErs=True, dilers_kwargs={'DILfpSizes': [4, 4], 'ERSfpSizes': [4, 4],
'footprints': ['ball', 'ball'], 'removeEndVox': [True, True]},
reindex=False, plotgs=False,
quickPlot_kwargs={'scalar_name': 'lfi', 'alpha': 0.75, 'cmap': 'viridis'}):
"""Voxel smoother."""
if addMajFilter:
self.lfi = self.cleanVoxMorph_majority_filter_npass(lfi=self.lfi, plotgs=False,
**majority_filter_kwargs)
self.report(message="Post majority filter report: \n")
if addDilErs:
self.lfi = self.cleanVoxMorph_DE_npass(self.lfi,
niterations=dilers_kwargs.get('niterations', 2),
DILfpSizes=dilers_kwargs.get('DILfpSizes', [4, 4]),
ERSfpSizes=dilers_kwargs.get('ERSfpSizes', [4, 4]),
footprints=dilers_kwargs.get('footprints', ['ball', 'ball']),
removeEndVox=dilers_kwargs.get('removeEndVox', [True, True]),
plotgs=False)
if reindex:
self.reindex(findMprops=False, plotgs=False)
self.quick_plot(**quickPlot_kwargs) if plotgs else None
self.report(message="Post voxel smoother report: \n")
[docs]
def cleanVoxMorph_majority_filter_npass(self, lfi=None, plotgs=True,
quickPlot_kwargs={'scalar_name': 'lfi', 'alpha': 0.75, 'cmap': 'viridis'},
**majority_filter_kwargs):
""" majority_filter_kwargs={'n': 2, 'sizes': [3, 3]} """
self.lfi = gridOps.majority_filter_3d_npass(lfi=self.lfi,
**majority_filter_kwargs)
self.quick_plot(**quickPlot_kwargs) if plotgs else None
[docs]
def cleanVoxMorph_DE_npass(self, niterations=2, DILfpSizes=[4, 4],
ERSfpSizes=[4, 4], footprints=['ball', 'ball'],
removeEndVox=[True, True], plotgs=True, alpha=1.0, cmap='nipy_spectral',
quickPlot_kwargs={'scalar_name': 'lfi', 'alpha': 0.75, 'cmap': 'viridis'}):
"""Cleanvoxmorph de npass."""
self.lfi = gridOps.smooth_voxMorph_npass(self.lfi, niterations=niterations,
DILfpSizes=DILfpSizes, ERSfpSizes=ERSfpSizes, footprints=footprints,
removeEndVox=removeEndVox)
self.quick_plot(**quickPlot_kwargs) if plotgs else None
# ================================================================================================
#
# ================================================================================================
# ================================================================================================
# ================================================================================================
# ================================================================================================
# ================================================================================================
# ================================================================================================
# ================================================================================================
# ================================================================================================
# ================================================================================================
# ================================================================================================
# ================================================================================================
# ================================================================================================
# ================================================================================================
# REPORTING
[docs]
def report(self, message='Report: \n'):
"""Report."""
opDisp.preManifold_clean_report(self.lfi, message=message)
# ================================================================================================
# PLOTTING
[docs]
def quick_plot(self, scalar_name='lfi', alpha=0.9, cmap='nipy_spectral', show_edges=False,
title='', xname='', yname='', zname=''):
"""Quick plot."""
gsviz.plot_pvgrid(gsviz.make_pvgrid(self.lfi, scalar_name=scalar_name),
scalar_name=scalar_name, show_edges=show_edges, alpha=alpha, title=title,
cmap=cmap, _xname_=xname, _yname_=yname, _zname_=zname)
[docs]
def see(self, plotType, **kwargs):
"""
plotType: 0 to n for different gsviz.vox2geom_plots. Value of n depends on the UPXO version.
I will add more features and n will increase to allow you more ways to plot. For now, we have
the following options for plotType, which are the options for gsviz.vox2geom_plots:
0: quick plot of the voxel grid with default settings. This is the same as
geometryfi3d.quick_plot() with default settings.
1: grain_viewer. -1 falls back to grain viewer.
2: view_boundary_voxels
3: see_clip_plane
4: see_mesh_slice
5: see_mesh_slice_ortho
"""
if plotType not in np.arange(0, self.n_plotTypes+1):
raise ValueError(f"Unknown plotType '{plotType}'. Available options: 0 to {self.n_plotTypes} for",
"different gsviz.vox2geom_plots")
if plotType == 0:
self.quick_plot(**kwargs)
return
gsviz.vox2geom_plots(plotType, lfi=self.lfi, **kwargs)