I have a python numpy.ndarray which has 3 dimensions and I would like to have the eventual goal of turning this into an stl file so that I can 3D print it.
I have found many cool examples of people using functions with numpy.meshgrid or other packages which can take an x/y relationship over a z dimension and (eventually) use this to generate an stl. However, in my case, I do not have a mathematical relationship between x, y and z and I also do not have a list of vertices describing the faces of my array.
I have yet to see something where I can directly take a 3 dimensional numpy.ndarray and convert this into a meshgrid/stl/list of vertices, etc.
Do you see something that I am missing? :)
Thank you!
You can use the marching cubes algorithm to convert a 3d voxel mask (1: object voxels, 0: background voxels; which is what you have, if I understand your question correctly) into a surface.
Here, I create the 50×60×70-element 3D NumPy array mask
as an exemplary input array, which represents the volume of an ellipsoid with radii 10, 20, 30. I am then using vtkMarchingCubes, which is VTK's implementation of the marching cubes algorithm, to get its surface. Smoothing with vtkSmoothPolyDataFilter, which implements Laplacian smoothing, might improve the somewhat rough resulting surface:
import numpy as np
import vtk
from vtk.util.numpy_support import numpy_to_vtk
def ellipsoid_mask(shape=(50, 60, 70), radii=(10, 20, 30)):
xyz = np.mgrid[*(slice(-s//2, s-s//2, 1) for s in shape)]
return (sum((c/r)**2 for c, r in zip(xyz, radii)) <= 1).astype(np.uint8)
mask = ellipsoid_mask() # Create binary mask of ellipsoid
# Convert to vtkImageData
vtk_data = vtk.vtkImageData()
vtk_data.SetDimensions(*mask.shape[::-1])
vtk_array = numpy_to_vtk(mask.ravel(), array_type=vtk.VTK_UNSIGNED_CHAR)
vtk_data.GetPointData().SetScalars(vtk_array)
# Apply marching cubes to get the surface
mc = vtk.vtkMarchingCubes()
mc.SetInputData(vtk_data)
mc.SetValue(0, 0.5)
# Smooth the surface
flt = vtk.vtkSmoothPolyDataFilter()
flt.SetInputConnection(mc.GetOutputPort())
flt.SetNumberOfIterations(25)
flt.SetRelaxationFactor(0.1)
# Save the resulting surfaces as STL
for src in (mc, flt):
stl_writer = vtk.vtkSTLWriter()
stl_writer.SetInputConnection(src.GetOutputPort())
stl_writer.SetFileName(f"{src.__class__.__name__}_surface.stl")
stl_writer.Write()
Rendering the resulting vtkMarchingCubes_surface.stl
file with MeshLab produces:
Rendering vtkSmoothPolyDataFilter_surface.stl
produces: