import os
import pandas as pd
import pyvista as pv
import matplotlib as mpl
import numpy as np
import h5py
[docs]
def pathForResults(self):
"""
Returns the path to the directory where results are stored.
If a custom path is provided (`self.customPath`), it returns that path.
Otherwise, it returns a default path.
Parameters:
-----------
self : object
The instance of the class calling this function. It should have an attribute `customPath`.
Returns:
--------
resultsPath : str
The path to the results directory.
"""
if self.customPath is not None:
resultsPath=self.customPath
else:
resultsPath="/Users/alessandromastrofini/LocalFiles/acePlaqueGlobalResults/FinalOne/"
return resultsPath
[docs]
def importResults(self, ptName, step, customPath=None, myStr = ''):
"""
Imports results data from HDF5 files based on the patient name (`ptName`), step number (`step`), and an optional custom string filter (`myStr`).
This function searches for the appropriate file, reads the data from it, and returns it as a pandas DataFrame.
Parameters:
-----------
self : object
The instance of the class calling this function.
ptName : str
The name of the patient.
step : int
The step number in the process.
customPath : str, optional
A custom path to the results directory. If `None`, a default path is used.
myStr : str, optional
A custom string to filter the files. Defaults to an empty string.
Returns:
--------
df : pandas.DataFrame
The data loaded from the HDF5 file into a DataFrame.
Raises:
-------
FileNotFoundError
If no file matching the criteria is found.
"""
if customPath is not None:
dirResults=os.path.join(customPath,ptName,"Results")
resultsFiles=os.listdir(dirResults)
sorted_files = sorted(resultsFiles)
filtered_files = list(filter(lambda file: myStr in file, sorted_files))
sub = [s for s in filtered_files if "__step_"+str(step) in s]
if not sub:
raise FileNotFoundError(f"No file found for step {step}")
else:
file_to_load = os.path.join(dirResults,sub[0])
#print(file_to_load)
with h5py.File(file_to_load, 'r') as hdf:
# List all datasets (keys) in the HDF5 file
dataset_keys = list(hdf.keys())
# Initialize a dictionary to hold data for each key
data_dict = {}
# Loop over each dataset key and load it into the dictionary
for key in dataset_keys:
data_dict[key] = hdf[key][:]
# Convert the dictionary to a pandas DataFrame
df = pd.DataFrame(data_dict)
return df
[docs]
def importClassicResults(self,ptName,step,customPath=None):
"""
Imports classic results for a given patient and step number.
This function is a wrapper around `importResults`, specifically designed to filter files with the "classic_" prefix.
Parameters:
-----------
self : object
The instance of the class calling this function.
ptName : str
The name of the patient.
step : int
The step number in the process.
customPath : str, optional
A custom path to the results directory.
Returns:
--------
df : pandas.DataFrame
The classic results data loaded into a DataFrame.
"""
df = importResults(self, ptName, step, customPath, "classic_")
return df
[docs]
def importGrowthResults(self,ptName,step,customPath=None):
"""
Imports growth results for a given patient and step number.
This function is a wrapper around `importResults`, specifically designed to filter files with the "growth_" prefix.
Parameters:
-----------
self : object
The instance of the class calling this function.
ptName : str
The name of the patient.
step : int
The step number in the process.
customPath : str, optional
A custom path to the results directory.
Returns:
--------
df : pandas.DataFrame
The growth results data loaded into a DataFrame.
"""
df = importResults(self, ptName, step, customPath, "growth_")
return df
[docs]
def show3DmeshWithFields(mesh,field='field1',clim=[-10, 80],scaling='',plotter=None):
"""
Imports growth results for a given patient and step number.
This function is a wrapper around `importResults`, specifically designed to filter files with the "growth_" prefix.
Parameters:
-----------
self : object
The instance of the class calling this function.
ptName : str
The name of the patient.
step : int
The step number in the process.
customPath : str, optional
A custom path to the results directory.
Returns:
--------
df : pandas.DataFrame
The growth results data loaded into a DataFrame.
"""
p=get3DmeshWithFields(mesh,field=field,clim=clim,scaling=scaling,plotter=plotter)
p.show()
[docs]
def get3DmeshWithFields(mesh,field='field1',clim=[-10, 80],scaling='',plotter=None, center = None, cmap = None):
"""
Creates a 3D visualization of the mesh with slices along the x, y, and z axes and maps a scalar field to the mesh.
The function returns a plotter object for further customization or display.
Parameters:
-----------
mesh : pyvista.PolyData
The mesh data to be visualized.
field : str, optional
The name of the scalar field to display on the mesh. Defaults to `'field1'`.
clim : list, optional
Color limits for the scalar field. Defaults to `[-10, 80]`.
scaling : str, optional
A string for scaling information to be displayed as a label.
plotter : pyvista.Plotter, optional
An existing `pyvista` plotter instance. If `None`, a new one is created.
center : tuple, optional
The center point for slicing. If `None`, the mesh center is used.
cmap : str, optional
The colormap to use for the scalar field. Defaults to `'jet'`.
Returns:
--------
p : pyvista.Plotter
The plotter object with the 3D mesh and field visualized.
"""
if cmap is None:
cmap = 'jet'
if center is None:
center = mesh.center
else:
center = center
# Create slices at the center of the mesh
slice_x = mesh.slice(normal='x', origin=center)
slice_y = mesh.slice(normal='y', origin=center)
slice_z = mesh.slice(normal='z', origin=center)
# Create a plotter
if plotter is None:
p = pv.Plotter(window_size=[600, 800])
else:
p = plotter
# Add the slices to the plotter
p.add_mesh(slice_x, scalars=field, cmap=cmap,show_scalar_bar=False, clim=clim)
p.add_mesh(slice_y, scalars=field, cmap=cmap, show_scalar_bar=False, clim=clim)
p.add_mesh(slice_z, scalars=field, cmap=cmap, show_scalar_bar=False, clim=clim)
p.add_scalar_bar(title=None, label_font_size=10, title_font_size=14, position_x=0.22)
if scaling is not None:
p.add_text(field + " ["+scaling+"]", position="lower_edge", font_size=6, color="black")
p.view_isometric()
p.camera.zoom(1.2)
return p
[docs]
def get3DSlicesWithFields(mesh,field='field1',clim=[-10, 80],scaling='',plotter=None,normal=None, center=None, textLabel = True, scalarBar = True, cmap= None):
"""
Creates a 3D visualization of the mesh with a single slice along a specified normal axis, mapping a scalar field to the slice.
The function returns a plotter object for further customization or display.
Parameters:
-----------
mesh : pyvista.PolyData
The mesh data to be visualized.
field : str, optional
The name of the scalar field to display on the mesh slice. Defaults to `'field1'`.
clim : list, optional
Color limits for the scalar field. Defaults to `[-10, 80]`.
scaling : str, optional
A string for scaling information to be displayed as a label.
plotter : pyvista.Plotter, optional
An existing `pyvista` plotter instance. If `None`, a new one is created.
normal : str, optional
The normal axis for slicing the mesh (`'x'`, `'y'`, or `'z'`). Defaults to `'z'`.
center : tuple, optional
The center point for slicing. If `None`, the mesh center is used.
textLabel : bool, optional
Whether to add a text label indicating the field name and scaling. Defaults to `True`.
scalarBar : bool, optional
Whether to add a scalar bar to the plot. Defaults to `True`.
cmap : str, optional
The colormap to use for the scalar field. Defaults to `'jet'`.
Returns:
--------
p : pyvista.Plotter
The plotter object with the 3D mesh slice and field visualized.
"""
if cmap is None:
cmap = 'jet'
if plotter is None:
p = pv.Plotter(window_size=[600, 800])
else:
p = plotter
if normal is None:
n = 'z'
else:
n = normal
if center is None:
c = mesh.center
else:
c = center
# Create slices at the center of the mesh
slice = mesh.slice(normal=n, origin=c)
# Add the slices to the plotter
#p.add_mesh(slice, scalars='mats', cmap=cmap, show_scalar_bar=False)
p.add_mesh(slice, scalars=field, cmap=cmap, show_scalar_bar=False, clim=clim)
if scalarBar:
p.add_scalar_bar(title=None, label_font_size=10, title_font_size=14, position_x=0.22)
if scaling is not None:
scalingString = scaling
else:
scalingString = ''
if textLabel:
p.add_text(field + " ["+scalingString+"]", position="lower_edge", font_size=6, color="black")
p.camera.zoom(1.2)
if n == 'z':
p.view_xy()
elif n == 'y':
p.view_xz()
elif n == 'x':
p.view_yz()
return p
[docs]
def show3DmeshWithMaterials(mesh,plotter=None):
"""
Displays a 3D mesh with segmented material properties visualized on it using the `pyvista` library.
This function uses `get3DmeshWithMaterials` to prepare the plot and then displays it.
Parameters:
-----------
mesh : pyvista.PolyData
The mesh data to be visualized.
plotter : pyvista.Plotter, optional
An existing `pyvista` plotter instance. If `None`, a new one is created.
Returns:
--------
None
"""
p=get3DmeshWithMaterials(mesh,plotter=plotter)
p.show()
# def get3DmeshWithMaterials(mesh,plotter=None):
# if plotter is None:
# p = pv.Plotter(window_size=[600, 800])
# else:
# p = plotter
# mesh.set_active_scalars("mats")
# # Apply a threshold over a data range
# calc = mesh.threshold([-228000,-(228000+33200)/2])
# lipid = mesh.threshold([-(228000+33200)/2, (126000-33200)/2])
# cmap = mpl.colors.ListedColormap(["navy", "orange", "red"])
# p.add_mesh(mesh, color = 'red', opacity=0.1, label='MATX')
# p.add_mesh(calc, color = 'navy', opacity=0.4, label='CALC')
# p.add_mesh(lipid, color = 'orange', opacity=0.4, label='LRNC/IPH')
# p.add_legend(loc='lower right',bcolor='white', face='None')
# p.camera.zoom(1.2)
# p.view_isometric()
# return p
[docs]
def get3DmeshWithMaterials(mesh,plotter=None, addLegend=True):
"""
Creates a 3D visualization of the mesh with segmented material properties, applying different colors and opacities for each material.
The function returns a plotter object for further customization or display.
Parameters:
-----------
mesh : pyvista.PolyData
The mesh data to be visualized.
plotter : pyvista.Plotter, optional
An existing `pyvista` plotter instance. If `None`, a new one is created.
addLegend : bool, optional
Whether to add a legend to the plot indicating the materials. Defaults to `True`.
Returns:
--------
p : pyvista.Plotter
The plotter object with the 3D mesh and materials visualized.
"""
if plotter is None:
p = pv.Plotter(window_size=[600, 800])
else:
p = plotter
mesh.set_active_scalars("matsSegmented")
# Apply a threshold over a data range
calc = mesh.threshold([-0.1,0.1])
lipid = mesh.threshold([1.9, 2.1])
fibr = mesh.threshold([2.9, 3.1])
p.add_mesh(mesh, color = 'red', opacity=0.1, label='MATX')
p.add_mesh(calc, color = 'navy', opacity=0.3, label='CALC')
p.add_mesh(lipid, color = 'orange', opacity=0.3, label='LRNC')
p.add_mesh(fibr, color = 'darkred', opacity=0.3, label='FIBR')
if addLegend:
p.add_legend(loc='lower right',bcolor='white', face='None')
p.camera.zoom(1.2)
p.view_isometric()
return p
[docs]
def getSlicemeshWithMaterials(mesh,plotter=None,normal=None,center=None,addBar=False):
"""
Creates a 3D visualization of a single slice of the mesh with segmented material properties, applying different colors for each material.
The function returns a plotter object for further customization or display.
Parameters:
-----------
mesh : pyvista.PolyData
The mesh data to be visualized.
plotter : pyvista.Plotter, optional
An existing `pyvista` plotter instance. If `None`, a new one is created.
normal : str, optional
The normal axis for slicing the mesh (`'x'`, `'y'`, or `'z'`). Defaults to `'z'`.
center : tuple, optional
The center point for slicing. If `None`, the mesh center is used.
addBar : bool, optional
Whether to add a scalar bar to the plot. Defaults to `False`.
Returns:
--------
p : pyvista.Plotter
The plotter object with the 3D mesh slice and materials visualized.
"""
if plotter is None:
p = pv.Plotter(window_size=[600, 800])
else:
p = plotter
if normal is None:
n = 'z'
else:
n = normal
if center is None:
c = mesh.center
else:
c = center
# Create slices at the center of the mesh
#slice = mesh.slice(normal=n, origin=c)
sl = mesh.slice(normal=n, origin=c)
mask = sl.threshold(value=[-0.000001,0.00001], scalars='matsSegmented')
p.add_mesh(mask, color='navy')
mask2 = sl.threshold(value=[0.9,1.1], scalars='matsSegmented')
p.add_mesh(mask2, color='red')
mask3 = sl.threshold(value=[1.99999,2.000001], scalars='matsSegmented')
p.add_mesh(mask3, color='orange')
mask4 = sl.threshold(value=[2.99999,3.000001], scalars='matsSegmented')
p.add_mesh(mask4, color='darkred')
cmap = mpl.colors.ListedColormap(["navy", "red", "orange","darkred"])
# Add the slices to the plotter
#p.add_mesh(slice, scalars='mats', cmap=cmap, show_scalar_bar=False)
#p.add_mesh(slice, scalars='matsSegmented', cmap=cmap, show_scalar_bar=False, clim=[0,3])
if addBar:
p.add_scalar_bar(title=None, label_font_size=0, n_labels=0, title_font_size=14, position_x=0.22)
p.add_text("MATX", position=(400, 18), font_size=5, color="black")
p.add_text("LRNC/IPH", position=(280, 18), font_size=5, color="black")
p.add_text("CALC", position=(180, 18), font_size=5, color="black")
p.add_text("FIBR", position=(180, 18), font_size=5, color="black")
if n == 'z':
p.view_xy()
elif n == 'y':
p.view_xz()
elif n == 'x':
p.view_yz()
return p
[docs]
def show3DslicesWithMaterials(mesh,plotter=None):
"""
Displays 3D slices of the mesh with segmented material properties visualized on them using the `pyvista` library.
This function uses `get3DslicesWithMaterials` to prepare the plot and then displays it.
Parameters:
-----------
mesh : pyvista.PolyData
The mesh data to be visualized.
plotter : pyvista.Plotter
"""
p= get3DslicesWithMaterials(mesh,plotter=plotter)
p.show()
[docs]
def get3DslicesWithMaterials(mesh,plotter=None, normal=None, center=None, addBar= True):
"""
Creates a 3D visualization of the mesh with slices along the x, y, and z axes, showing segmented material properties.
The function returns a plotter object for further customization or display.
Parameters:
-----------
mesh : pyvista.PolyData
The mesh data to be visualized.
plotter : pyvista.Plotter, optional
An existing `pyvista` plotter instance. If `None`, a new one is created.
normal : str, optional
The normal axis for slicing the mesh. If `None`, slices are made along all three axes.
center : tuple, optional
The center point for slicing. If `None`, the mesh center is used.
addBar : bool, optional
Whether to add a scalar bar to the plot. Defaults to `True`.
Returns:
--------
p : pyvista.Plotter
The plotter object with the 3D mesh slices and materials visualized.
"""
if plotter is None:
p = pv.Plotter(window_size=[600, 800])
else:
p = plotter
p.add_mesh(mesh, opacity=0.2, color='red', show_scalar_bar=False)
cmap = mpl.colors.ListedColormap(["navy", "red", "orange", "darkred"])
if center is None:
c = mesh.center
else:
c = center
if normal is None or c is None:
# Create slices at the center of the mesh
slice_x = mesh.slice(normal='x', origin=c)
slice_y = mesh.slice(normal='y', origin=c)
slice_z = mesh.slice(normal='z', origin=c)
p.add_mesh(slice_x, scalars='matsSegmented', cmap=cmap, show_scalar_bar=False,clim=[0,3])
p.add_mesh(slice_y, scalars='matsSegmented', cmap=cmap, show_scalar_bar=False,clim=[0,3])
p.add_mesh(slice_z, scalars='matsSegmented', cmap=cmap, show_scalar_bar=False,clim=[0,3])
else:
slice = mesh.slice(normal=normal, origin=c)
p.add_mesh(slice, scalars='matsSegmented', cmap=cmap, show_scalar_bar=False,clim=[0,3])
# Add the slices to the plotter
if addBar:
p.add_scalar_bar(title=None, label_font_size=0, n_labels=0, title_font_size=14, position_x=0.22)
p.add_text("MATX", position=(400, 18), font_size=5, color="black")
p.add_text("LRNC/IPH", position=(280, 18), font_size=5, color="black")
p.add_text("CALC", position=(180, 18), font_size=5, color="black")
p.add_text("FIBR", position=(180, 18), font_size=5, color="black")
p.camera.zoom(1.2)
p.view_isometric()
return p
[docs]
def show3Dmesh(mesh,plotter=None):
"""
Displays a 3D mesh using the `pyvista` library.
This function uses `get3Dmesh` to prepare the plot and then displays it.
Parameters:
-----------
mesh : pyvista.PolyData
The mesh data to be visualized.
plotter : pyvista.Plotter, optional
An existing `pyvista` plotter instance. If `None`, a new one is created.
Returns:
--------
None
"""
p = get3Dmesh(mesh,plotter=plotter)
p.show()
[docs]
def get3Dmesh(mesh, plotter=None):
"""
Creates a 3D visualization of the mesh with slices along the x, y, and z axes.
The function returns a plotter object for further customization or display.
Parameters:
-----------
mesh : pyvista.PolyData
The mesh data to be visualized.
plotter : pyvista.Plotter, optional
An existing `pyvista` plotter instance. If `None`, a new one is created.
Returns:
--------
p : pyvista.Plotter
The plotter object with the 3D mesh and slices visualized.
"""
center = mesh.center
# Create slices at the center of the mesh
slice_x = mesh.slice(normal='x', origin=center)
slice_y = mesh.slice(normal='y', origin=center)
slice_z = mesh.slice(normal='z', origin=center)
if plotter is None:
p = pv.Plotter(window_size=[600, 800])
else:
p = plotter
p.add_mesh(mesh, scalars=None, color='red', opacity=0.3, show_scalar_bar=False)
# Add the slices to the plotter
p.add_mesh(slice_x, scalars=None, color='blue', opacity =0.2,show_scalar_bar=False)
p.add_mesh(slice_z, scalars=None, color='blue', opacity =0.2,show_scalar_bar=False)
p.add_mesh(slice_y, scalars=None, color='blue', opacity =0.2,show_scalar_bar=False)
p.view_isometric()
p.camera.zoom(1.2)
return p
[docs]
def saveSlice(mesh,normal,posRatio,path,clim=[-10, 80]):
"""
Saves screenshots of a slice of the mesh with field data and material properties.
This function creates a slice along a specified axis at a given position, visualizes the field data, and saves it as an image. It also visualizes segmented materials and saves another image.
Parameters:
-----------
mesh : pyvista.PolyData
The mesh data to be sliced and visualized.
normal : str
The axis normal to the slice (`'x'`, `'y'`, or `'z'`).
posRatio : float
The position ratio along the normal axis where the slice is made (0 to 100).
path : str
The base path where the screenshots will be saved.
clim : list, optional
Color limits for the scalar field. Defaults to `[-10, 80]`.
Returns:
--------
None
"""
center = mesh.center
origin=center
xmin, xmax, ymin, ymax, zmin, zmax = mesh.bounds
if normal=='x':
origin[0]= xmin+(xmax-xmin)*posRatio/100
elif normal=='y':
origin[1]= ymin+(ymax-ymin)*posRatio/100
elif normal=='z':
origin[2]= zmin+ (zmax-zmin)*posRatio/100
# Create slices at the center of the mesh
slice = mesh.slice(normal=normal, origin=center)
# Create a plotter
p = pv.Plotter(window_size=[600, 800])
# Add the slices to the plotter
p.add_mesh(slice, scalars='field1', cmap='jet',show_scalar_bar=False, clim=clim)
p.add_scalar_bar(title=None, label_font_size=10, title_font_size=14, position_x=0.22)
p.add_text("1° principal stress", position="lower_edge", font_size=6, color="black")
if normal=='x':
p.view_yz()
elif normal=='y':
p.view_xz()
elif normal=='z':
p.view_xy()
p.screenshot(path+"_stress.png")
# compute the position (max - min )* ratio
cmap = mpl.colors.ListedColormap(["red", "navy", "orange"])
p = pv.Plotter(window_size=[600, 800])
# Add the slices to the plotter
p.add_mesh(slice, scalars='matsSegmented', cmap=cmap, show_scalar_bar=False)
p.add_scalar_bar(title=None, label_font_size=0, n_labels=0, title_font_size=14, position_x=0.22)
p.add_text("MATX", position=(400, 18), font_size=5, color="black")
p.add_text("LRNC/IPH", position=(280, 18), font_size=5, color="black")
p.add_text("CALC", position=(180, 18), font_size=5, color="black")
if normal=='x':
p.view_yz()
elif normal=='y':
p.view_xz()
elif normal=='z':
p.view_xy()
p.screenshot(path+"_mats.png")
return
[docs]
def saveMeshIsoViewWithMaterialSlice(mesh,path):
"""
Saves a screenshot of the 3D mesh in an isometric view with material slices.
This function creates slices along the x, y, and z axes, visualizes the segmented material properties, and saves the resulting view as an image.
Parameters:
-----------
mesh : pyvista.PolyData
The mesh data to be sliced and visualized.
path : str
The path where the screenshot will be saved.
Returns:
--------
None
"""
center = mesh.center
# Create slices at the center of the mesh
slice_x = mesh.slice(normal='x', origin=center)
slice_y = mesh.slice(normal='y', origin=center)
slice_z = mesh.slice(normal='z', origin=center)
# Create a plotter
p = pv.Plotter(window_size=[600, 800])
# Create a plotter
p = pv.Plotter(window_size=[600, 800])
cmap = mpl.colors.ListedColormap(["navy", "red", "orange"])
# Add the slices to the plotter
p.add_mesh(mesh, opacity=0.1, show_scalar_bar=False)
p.add_mesh(slice_x, scalars='mats', cmap=cmap, show_scalar_bar=False)
p.add_mesh(slice_y, scalars='mats', cmap=cmap, show_scalar_bar=False)
p.add_mesh(slice_z, scalars='mats', cmap=cmap, show_scalar_bar=False)
#p.add_scalar_bar(title=None, label_font_size=0, n_labels=0, title_font_size=14, position_x=0.22)
#p.add_text("MATX", position=(400, 18), font_size=5, color="black")
#p.add_text("LRNC/IPH", position=(280, 18), font_size=5, color="black")
#p.add_text("CALC", position=(180, 18), font_size=5, color="black")
p.camera.zoom(1.2)
p.view_isometric()
p.screenshot(path+".png")
[docs]
def saveMeshIsoView(mesh,path):
"""
Saves a screenshot of the 3D mesh in an isometric view.
This function visualizes the mesh without any additional slices or field data, and saves the resulting view as an image.
Parameters:
-----------
mesh : pyvista.PolyData
The mesh data to be visualized.
path : str
The path where the screenshot will be saved.
Returns:
--------
None
"""
center = mesh.center
# Create slices at the center of the mesh
# Create a plotter
p = pv.Plotter(window_size=[600, 800])
# Create a plotter
p = pv.Plotter(window_size=[600, 800])
# Add the slices to the plotter
p.add_mesh(mesh, opacity=0.3, show_scalar_bar=False)
p.camera.zoom(1.2)
p.view_isometric()
p.screenshot(path+".png")