from pyevtk.hl import imageToVTK
import numpy as np
from skimage.io import imsave
import pickle
from pumapy.utilities.workspace import Workspace
from pumapy.utilities.logger import print_warning
from pumapy.utilities.isosurface import generate_isosurface
from TexGen.Core import *
from os import path
[docs]def export_vti(filename, dict_data, voxel_length=None):
""" Export either a puma.Workspace or numpy array to vti
:param filename: filepath and name
:type filename: string
:param dict_data: dictionary setup as {"name1": data1, "name2": data2 ...} containing either Workspaces or ndarrays
:type dict_data: dict
:param voxel_length: with voxel length to give to Numpy arrays (if any)
:type voxel_length: float, optional
:return: True if successful, False otherwise.
:rtype: bool
"""
# error path checks
if not isinstance(filename, str):
raise Exception("Filename has to be a string.")
filename_split = path.split(filename)
if filename_split[0] != '' and not path.exists(path.split(filename)[0]):
raise Exception("Directory " + filename_split[0] + " not found.")
if filename[-4:] == ".vti":
filename = filename[:-4]
print("Exporting " + filename + ".vti ... ", end='')
if isinstance(dict_data, Workspace) or isinstance(dict_data, np.ndarray):
dict_data = {"data": dict_data}
dict_to_export = dict()
for name, data in dict_data.items():
if isinstance(data, Workspace):
mat = data.matrix.copy() # need to copy to make sure data is "continuous"
dict_to_export[name] = mat
if data.orientation.shape[:3] == data.matrix.shape:
orientation = (data.orientation[:, :, :, 0].copy(),
data.orientation[:, :, :, 1].copy(),
data.orientation[:, :, :, 2].copy())
dict_to_export[name] = mat
dict_to_export[name + "_orient"] = orientation
if voxel_length is None:
voxel_length = data.voxel_length
elif isinstance(data, np.ndarray):
if voxel_length is None:
voxel_length = 1e-6
if data.ndim == 2: # 2D numpy array to 3D
data = np.expand_dims(data.copy(), axis=2)
elif data.ndim == 3:
data = data.copy()
elif data.ndim == 4 and data.shape[3] == 3:
data = (data[:, :, :, 0].copy(),
data[:, :, :, 1].copy(),
data[:, :, :, 2].copy())
else:
raise Exception("Numpy array has to be either 3D for scalars or 4D with shape[3]=3.")
dict_to_export[name] = data
else:
raise Exception("Data to export to vti needs to be either a pumapy.Workspace or Numpy array")
imageToVTK(filename, spacing=[voxel_length] * 3, cellData=dict_to_export)
print("Done")
return True
[docs]def export_3Dtiff(filename, ws_or_nparray, to8bit=False):
""" Export either a puma.Workspace or numpy array to 3Dtiff
:param filename: filepath and name
:type filename: string
:param ws_or_nparray: to be exported
:type ws_or_nparray: Workspace or ndarray
:param to8bit: if True, it converts the image to 8bit, otherwise 16bit is exported
:type to8bit: bool, optional
:return: True if successful, False otherwise.
:rtype: bool
"""
# error checks
if not isinstance(filename, str):
raise Exception("Filename has to be a string.")
filename_split = path.split(filename)
if filename_split[0] != '' and not path.exists(path.split(filename)[0]):
raise Exception("Directory " + filename_split[0] + " not found.")
if isinstance(ws_or_nparray, Workspace):
data = ws_or_nparray.matrix.copy()
elif isinstance(ws_or_nparray, np.ndarray):
data = ws_or_nparray.copy()
else:
raise Exception("Data to export to 3Dtiff needs to be either a pumapy.Workspace or Numpy array")
if filename[-4:] != '.tif' and filename[-5:] != '.tiff':
filename += '.tif'
if to8bit:
if data.max() > 255:
print_warning("Data max is more than 255, normalizing to range 0-255.")
data *= 255.0 / data.max()
data = data.astype(np.uint8)
else:
data = data.astype(np.uint16)
print("Exporting " + filename + " ... ", end='')
if data.ndim == 2:
imsave(filename, data.transpose((1, 0)), check_contrast=False)
else:
imsave(filename, data.transpose((2, 1, 0)), check_contrast=False)
print("Done")
return True
[docs]def export_bin(filename, ws):
""" Export a puma.Workspace to binary (.pumapy extension)
:param filename: filepath and name
:type filename: string
:param ws: to be exported
:type: Workspace
:return: True if successful, False otherwise.
:rtype: bool
"""
# error checks
if not isinstance(filename, str):
raise Exception("Filename has to be a string.")
filename_split = path.split(filename)
if filename_split[0] != '' and not path.exists(path.split(filename)[0]):
raise Exception("Directory " + filename_split[0] + " not found.")
if not isinstance(ws, Workspace):
raise Exception("Data to export to bin needs to be a pumapy.Workspace")
if filename[-7:] != ".pumapy":
filename += '.pumapy'
print("Exporting " + filename + " ... ", end='')
output = open(filename, 'wb')
pickle.dump(ws, output)
output.close()
print("Done")
return True
[docs]def export_sparta_implicit_surfaces(filename, ws):
""" Export a puma.Workspace to binary (.pumapy extension)
:param filename: filepath and name
:type filename: string
:param ws: to be exported
:type ws: Workspace
:return: True if successful, False otherwise.
:rtype: bool
"""
# error checks
if not isinstance(filename, str):
raise Exception("Filename has to be a string.")
filename_split = path.split(filename)
if filename_split[0] != '' and not path.exists(path.split(filename)[0]):
raise Exception("Directory " + filename_split[0] + " not found.")
if not isinstance(ws, Workspace):
raise Exception("Data to export to bin needs to be a pumapy.Workspace")
if filename[-7:] != ".pumapy.isurf":
filename += '.pumapy.isurf'
print("Exporting " + filename + " ... ", end='')
if ws.len_z() == 1:
f = open(filename, 'w+b')
byte_arr = [ws.len_x(), ws.len_y()]
binary_format = bytearray(byte_arr)
f.write(binary_format)
pickle.dump(ws.matrix, f)
f.close()
print("Done")
return True
[docs]def export_stl(filename, ws, cutoff, flag_closed_edges=True, flag_gaussian=False, binary=True):
""" Export a puma.Workspace to STL
:param filename: filepath and name
:type filename: string
:param ws: to be exported
:type ws: Workspace
:param cutoff: specify cutoff to binarize material
:type cutoff: tuple(int, int)
:param flag_closed_edges: close the surface edges on the boundaries
:type flag_closed_edges: bool, optional
:param flag_gaussian: apply Gaussian filter before creating surface
:type flag_gaussian: bool, optional
:return: True if successful, False otherwise.
:rtype: bool
"""
# error checks
if not isinstance(filename, str):
raise Exception("Filename has to be a string.")
filename_split = path.split(filename)
if filename_split[0] != '' and not path.exists(path.split(filename)[0]):
raise Exception("Directory " + filename_split[0] + " not found.")
if not isinstance(ws, Workspace):
raise Exception("Data to export to stl needs to be a pumapy.Workspace")
if filename[-4:] != ".stl":
filename += '.stl'
print("Exporting " + filename + " ... ", end='')
mesh = generate_isosurface(ws, cutoff, flag_closed_edges, flag_gaussian)
mesh.save(filename, binary)
print("Done")
return True
[docs]def export_weave_vtu(filename, weave, domain, max_dim_nvox, round_vox_up=True, export_orientation=True):
""" Exporting weave to vtu, to be read by pumapy
:param filename: filepath and name
:type filename: string
:param weave: weave object, as defined in TexGen
:type weave: CTextile or child class of CTextile
:param domain: domain size object, as defined in TexGen
:type domain: CDomainPlanes
:param max_dim_nvox: number of voxels to add in the largest domain dimension
:type max_dim_nvox: int
:param round_vox_up: for the shorter dimensions, round number of voxels up (for +/-1 vox)
:type round_vox_up: bool
:param export_orientation: specify whether to export orientation
:type export_orientation: bool
:return: filename of weave exported (input filename + dimensions)
:rtype: string
"""
if not isinstance(domain, CDomainPlanes):
raise Exception("Domain needs to be of CDomainPlanes type.")
if not isinstance(filename, str):
raise Exception("Filename has to be a string.")
if not path.exists(path.split(filename)[0]):
raise Exception("Directory " + path.split(filename)[0] + " not found.")
min_bounds = XYZ()
max_bounds = XYZ()
domain.GetBoxLimits(min_bounds, max_bounds)
weave.AssignDomain(CDomainPlanes(min_bounds, max_bounds))
lengths = np.array([max_bounds.x - min_bounds.x, max_bounds.y - min_bounds.y, max_bounds.z - min_bounds.z])
max_len = np.max(lengths)
mask = np.zeros(3, dtype=bool)
mask[lengths == max_len] = True
voxel_length = max_len / float(max_dim_nvox)
nvox = np.zeros(3, dtype=int)
nvox[mask] = max_dim_nvox
nvox[~mask] = (lengths[~mask] / voxel_length).astype(int) # truncates
rem = np.zeros(3, dtype=float)
rem[~mask] = lengths[~mask] - voxel_length * nvox[~mask]
if round_vox_up:
rem[~mask] = voxel_length - rem[~mask]
max_bounds = XYZ(max_bounds.x + rem[0],
max_bounds.y + rem[1],
max_bounds.z + rem[2])
nvox[~mask] += 1
else:
max_bounds = XYZ(max_bounds.x - rem[0], max_bounds.y - rem[1], max_bounds.z - rem[2])
weave.AssignDomain(CDomainPlanes(min_bounds, max_bounds))
mesh = CRectangularVoxelMesh()
print("Exporting " + filename + ".vtu ... ", end='')
filename += "_" + str(nvox[0]) + "_" + str(nvox[1]) + "_" + str(nvox[2])
mesh.SaveVoxelMesh(weave, filename, int(nvox[0]), int(nvox[1]), int(nvox[2]), False, export_orientation,
MATERIAL_CONTINUUM, 0, VTU_EXPORT)
print("Done")
return filename