pythonnumpy

How to convert a numpy 3D array into a meshgrid / stl


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!


Solution

  • 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 of marching cubes result

    Rendering vtkSmoothPolyDataFilter_surface.stl produces:

    rendering of smoothed result