How to save multiple meshes in python using numpy-stl

3.9k views Asked by At

I am working on a piece of python code that will take in an image in grey scale, scale it, and output a 3d model with the height of each pixel being determined by the value of the grey scale. I have everything working except the output of the 3d model. I am using numpy-stl to create it based on an array of values derived from the image. Using the numpy-stl library I create a box and then copy it as many times as i need for the image. then I translate each one to the position and height corresponding with the image. This all works. The problem comes when I try to save it all as one .stl file. I cant figure out how to combine all the individual meshes of the cubes into one.

Here is just the code dealing with the creation of the 3d array. I can plot the created meshes but not save them.

from stl import mesh
import math
import numpy

test = [[1,2],[2,1]]

a = [[1,2,3,4],
        [5,6,7,8],
        [9,10,11,12],
        [13,14,15,16]]


# Create 6 faces of a cube, 2 triagles per face
data = numpy.zeros(12, dtype=mesh.Mesh.dtype)
#cube defined in stl format 
# Top of the cube
data['vectors'][0] = numpy.array([[0, 1, 1],
                                  [1, 0, 1],
                                  [0, 0, 1]])
data['vectors'][1] = numpy.array([[1, 0, 1],
                                  [0, 1, 1],
                                  [1, 1, 1]])
# Right face
data['vectors'][2] = numpy.array([[1, 0, 0],
                                  [1, 0, 1],
                                  [1, 1, 0]])
data['vectors'][3] = numpy.array([[1, 1, 1],
                                  [1, 0, 1],
                                  [1, 1, 0]])
# Left face
data['vectors'][4] = numpy.array([[0, 0, 0],
                                  [1, 0, 0],
                                  [1, 0, 1]])
data['vectors'][5] = numpy.array([[0, 0, 0],
                                  [0, 0, 1],
                                  [1, 0, 1]])
# Bottem of the cube
data['vectors'][6] = numpy.array([[0, 1, 0],
                                  [1, 0, 0],
                                  [0, 0, 0]])
data['vectors'][7] = numpy.array([[1, 0, 0],
                                  [0, 1, 0],
                                  [1, 1, 0]])
# Right back
data['vectors'][8] = numpy.array([[0, 0, 0],
                                  [0, 0, 1],
                                  [0, 1, 0]])
data['vectors'][9] = numpy.array([[0, 1, 1],
                                  [0, 0, 1],
                                  [0, 1, 0]])
# Left back 
data['vectors'][10] = numpy.array([[0, 1, 0],
                                  [1, 1, 0],
                                  [1, 1, 1]])
data['vectors'][11] = numpy.array([[0, 1, 0],
                                  [0, 1, 1],
                                  [1, 1, 1]])


# Generate 4 different meshes so we can rotate them later
meshes = [mesh.Mesh(data.copy()) for _ in range(16)]

#iterates through the array and translates cube in the x and y direction according 
#to position in array and in the z direction according to eh value stored in the array
def ArrayToSTL(array, STLmesh):
  y_count = 0
  x_count = 0
  count = 0
  for row in array:
    x_count = 0
    for item in row:
      meshes[count].x += x_count
      meshes[count].y += y_count
      meshes[count].z += item
      x_count +=1
      count += 1
    y_count += 1

ArrayToSTL(a, meshes)



# Optionally render the rotated cube faces
from matplotlib import pyplot
from mpl_toolkits import mplot3d

# Create a new plot
figure = pyplot.figure()
axes = mplot3d.Axes3D(figure)

# Render the cube faces
for m in meshes:
    axes.add_collection3d(mplot3d.art3d.Poly3DCollection(m.vectors))

# Auto scale to the mesh size
scale = numpy.concatenate([m.points for m in meshes]).flatten(-1)
axes.auto_scale_xyz(scale, scale, scale)

# Show the plot to the screen
pyplot.show()
2

There are 2 answers

1
Tanay Agrawal On

This works well:

import numpy as np
import stl
from stl import mesh
import os

def combined_stl(meshes, save_path="./combined.stl"):
    combined = mesh.Mesh(np.concatenate([m.data for m in meshes]))
    combined.save(save_path, mode=stl.Mode.ASCII)

loading stored stl files and meshing them, use this.

direc = "path_of_directory"
paths = [os.path.join(direc, i) for i in os.listdir(direc)]
meshes = [mesh.Mesh.from_file(path) for path in paths]
combined_stl(meshes)
0
town_math On

I am working on the same problem. This code generates a valid STL file from a 2D numpy array of heights. The key is the scikit marching_cubes which is an idea I got from a Medium article that I can't seem to find to cite my source.

import numpy as np
from stl import mesh
import stl
from skimage import measure

def numpy2stl(data, fileName, scale=0.1, mask_val=0, ascii=False):

    #2d array of heights to a 3d array of 1s and 0s
    volArr=np.zeros((data.shape[0],data.shape[1],int(data.max())+1),dtype=np.float32)
    #print(volArr.shape)
    for i in range(data.shape[0]):
        for j in range(data.shape[1]):
            if data[i,j]>mask_val:
                kMax=int(scale*data[i,j])+1
                for k in range(1,kMax):
                    volArr[i,j,k]=1

    verts, faces, normals, values = measure.marching_cubes(volArr, 0)
    myMesh = mesh.Mesh(np.zeros(faces.shape[0], dtype=mesh.Mesh.dtype))

    for i, f in enumerate(faces):
        myMesh.vectors[i] = verts[f]

    if ascii:
        myMesh.save(fileName,mode=stl.Mode.ASCII)
    else:
        myMesh.save(fileName)