通过“面部正常值”阈值选择STL的面部

时间:2019-01-02 12:07:13

标签: python mesh meshlab numpy-stl

我想用Python编写一个脚本,该脚本可以根据Face Normal值条件在STL中生成面部组。例如,提供的是Stl的快照,不同的颜色表示包含满足我给定的脸部法线阈值的三角形脸部的脸部组。有没有简单的方法可以在python中做到这一点? Face Group STL

1 个答案:

答案 0 :(得分:1)

我确定有一个python库可以加载stl文件,但是我总是编写自己的文件,因为文件格式非常简单(有关文件格式的说明,请参见Wikipedia article)。

这是我读取stl文件的代码:

import numpy as np
import struct

def Unique(inputList):
      """ 
      Given an M x N list, this function gets the unique rows by treating all
      M Ntuples as single objects. This function also returns the indexing
      to convert the unique returned list back to the original non-unique list.
      """

      hashTable=dict()

      indexList=[]
      uniqueList=[]

      indx=0
      for ntuple in inputList:
            if not ntuple in hashTable:
                hashTable[ntuple]=indx
                indexList.append(indx)
                uniqueList.append(ntuple)
                indx+=1
            else:
                indexList.append(hashTable.get(ntuple))      

      return uniqueList, indexList


def IsBinarySTL(filename):
    try:
        with open(filename,'r') as f:
              test=f.readline()
    except UnicodeDecodeError:
        return True

    if len(test) < 5:
        return True
    elif test[0:5].lower() == 'solid':
        return False  # ASCII STL
    else:
        return True

def ReadSTL(filename):
    """ Returns numpy arrays for vertices and facet indexing """
    def GetListFromASCII(filename):
        """ Returns vertex listing from ASCII STL file """
        outputList=[]

        with open(filename,'r') as f:
            lines=[line.split() for line in f.readlines()]
        for line in lines:
            if line[0] == 'vertex':
                    outputList.append(tuple([float(x) for x in line[1:]]))
        return outputList

    def GetListFromBinary(filename):
        """ Returns vertex listing from binary STL file """
        outputList=[]
        with open(filename,'rb') as f:
            f.seek(80) # skip header
            nFacets=struct.unpack('I',f.read(4))[0] # number of facets in piece

            for i in range(nFacets):
                  f.seek(12,1) # skip normal
                  outputList.append(struct.unpack('fff',f.read(12))) # append each vertex triple to list (each facet has 3 vertices)
                  outputList.append(struct.unpack('fff',f.read(12))) 
                  outputList.append(struct.unpack('fff',f.read(12)))
                  f.seek(2,1) # skip attribute
        return outputList

    if IsBinarySTL(filename):
        vertexList = GetListFromBinary(filename)
    else:
        vertexList = GetListFromASCII(filename)

    coords, tempindxs = Unique(vertexList)

    indxs = list()
    templist = list()
    for i in range(len(tempindxs)):
        if (i > 0 ) and not (i % 3):
            indxs.append(templist)
            templist = list()
        templist.append(tempindxs[i])
    indxs.append(templist)

    return np.array(coords), np.array(indxs)

这是计算构面法线的代码(假设右手法则)

def GetNormals(vertices, facets):
    """ Returns normals for each facet of mesh """
    u = vertices[facets[:,1],:] - vertices[facets[:,0],:]
    v = vertices[facets[:,2],:] - vertices[facets[:,0],:]
    normals = np.cross(u,v)
    norms = np.sqrt(np.sum(normals*normals, axis=1))
    return normals/norms[:, np.newaxis]

最后,编写出stl文件的代码(假定每个构面的属性列表):

def WriteSTL(filename, vertices, facets, attributes, header):
    """
    Writes vertices and facets to an stl file. Notes:
    1.) header can not be longer than 80 characters
    2.) length of attributes must be equal to length of facets
    3.) attributes must be integers
    """
    nspaces = 80 - len(header)
    header += nspaces*'\0'

    nFacets = np.shape(facets)[0]
    stl = vertices[facets,:].tolist()

    with open(filename,'wb') as f: # binary
        f.write(struct.pack('80s', header.encode('utf-8'))) # header
        f.write(struct.pack('I',nFacets)) # number of facets
        for i in range(nFacets):
            f.write(struct.pack('fff',0,0,0)) # normals set to 0
            for j in range(3):
                f.write(struct.pack('fff',stl[i][j][0], stl[i][j][1], stl[i][j][2])) # 3 vertices per facet 
            f.write(struct.pack("H", attributes[i])) # 2-byte attribute

将所有内容放在一起,您可以执行以下操作:

if __name__ == "__main__":
    filename = "bunny.stl"

    vertices, facets = ReadSTL(filename)  # parse stl file
    normals = GetNormals(vertices, facets)  # compute normals

    # Get some value related to normals
    attributes = []
    for i in range(np.shape(normals)[0]):
        attributes.append(int(255*np.sum(normals[i])**2))

    # Write new stl file
    WriteSTL("output.stl", vertices, facets, attributes, "stlheader")

此代码段读取一个stl文件,计算法线,然后根据每个法线的平方和分配属性值(请注意,该属性必须是整数)。

此脚本的输入和输出如下所示: enter image description here