def deduplicate_trimesh_vertices(trimesh): """ Accepts a `TriMesh` and returns a new `TriMesh` with duplicate vertices removed """ def do_vert_hash(vert): return hash(str(set(vert))) new_verts = [] new_faces = [] seen_verts = {} for face in trimesh.faces: face_points = [trimesh.vs[i] for i in face] new_face = [] for vert in face_points: vert_hash = do_vert_hash(vert) if vert_hash in seen_verts: index = seen_verts[vert_hash] else: new_verts.append(vert) index = len(new_verts) - 1 seen_verts[vert_hash] = index new_face.append(index) new_faces.append(new_face) new_trimesh = TriMesh() new_trimesh.vs = np.array(new_verts) new_trimesh.faces = np.array(new_faces) return new_trimesh
def importFile(self, filename, invertfaces=False): """Import an OFF file. If invertfaces is True the orientation of each face is reversed. """ self.invertfaces = invertfaces self.texcoord_flag = False self.color_flag = False self.normal_flag = False self.four_flag = False self.ndim_flag = False self.ndim = 3 self.is_trimesh = True self.fhandle = file(filename) # Read the header z = self.readLine() self.parseHeaderKeyWord(z) # nOFF? if self.ndim_flag: # Read the dimension of vertices z = self.readLine() self.ndim = int(z) if self.ndim>3: raise ValueError("A vertex space dimension of %d is not supported"%(self.ndim)) # Read the number of vertices and faces... z = self.readLine().split(" ") self.numverts = int(z[0]) self.numfaces = int(z[1]) # Start with a TriMeshGeom # (this will be later converted into a PolyhedronGeom if faces # with more than 3 vertices are encountered) self.geom = TriMeshGeom() self.geom.verts.resize(self.numverts) self.geom.faces.resize(self.numfaces) # Read the vertices... self.readVertices() # Read the faces... self.readFaces() self.fhandle.close() # Create the actual object... nodename = os.path.basename(filename) nodename = os.path.splitext(nodename)[0] nodename = nodename.replace(" ","_") if self.is_trimesh: n = TriMesh(name=nodename) else: n = Polyhedron(name=nodename) n.geom = self.geom
def importFile(self, filename): """Import an IFS file.""" f = file(filename, "rb") # Check the header... s = self.readString(f) if s!="IFS": raise ValueError('The file "%s" is is not a IFS file.'%filename) # Read (and ignore) the version number s = f.read(4) ver = struct.unpack("<f", s)[0] # Read the model name modelname = self.readString(f) # Create the mesh geom tm = TriMeshGeom() # Read vertices... s = self.readString(f) if s!="VERTICES": raise ValueError("Vertices chunk expected, got '%s' instead."%s) s = f.read(4) numverts = int(struct.unpack("<I", s)[0]) tm.verts.resize(numverts) for i in range(numverts): s = f.read(12) x,y,z = struct.unpack("<fff", s) tm.verts[i] = vec3(x,y,z) # Read faces... s = self.readString(f) if s!="TRIANGLES": raise ValueError("Triangle chunk expected, got '%s' instead."%s) s = f.read(4) numfaces = int(struct.unpack("<I", s)[0]) tm.faces.resize(numfaces) for i in range(numfaces): s = f.read(12) a,b,c = struct.unpack("<III", s) tm.faces[i] = (int(a), int(b), int(c)) # Create a world object obj = TriMesh(name=modelname) obj.geom = tm
def importFile(self, filename): """Import an IFS file.""" f = file(filename, "rb") # Check the header... s = self.readString(f) if s != "IFS": raise ValueError('The file "%s" is is not a IFS file.' % filename) # Read (and ignore) the version number s = f.read(4) ver = struct.unpack("<f", s)[0] # Read the model name modelname = self.readString(f) # Create the mesh geom tm = TriMeshGeom() # Read vertices... s = self.readString(f) if s != "VERTICES": raise ValueError("Vertices chunk expected, got '%s' instead." % s) s = f.read(4) numverts = int(struct.unpack("<I", s)[0]) tm.verts.resize(numverts) for i in range(numverts): s = f.read(12) x, y, z = struct.unpack("<fff", s) tm.verts[i] = vec3(x, y, z) # Read faces... s = self.readString(f) if s != "TRIANGLES": raise ValueError("Triangle chunk expected, got '%s' instead." % s) s = f.read(4) numfaces = int(struct.unpack("<I", s)[0]) tm.faces.resize(numfaces) for i in range(numfaces): s = f.read(12) a, b, c = struct.unpack("<III", s) tm.faces[i] = (int(a), int(b), int(c)) # Create a world object obj = TriMesh(name=modelname) obj.geom = tm
def check_gradient(args): """ Compare the exact gradient to the result of the finite differences algorithm `optimize.approx_fprime` This is for debugging the gradient """ trimesh = TriMesh.FromOBJ_FileName(args.in_obj) trimesh = util.deduplicate_trimesh_vertices(trimesh) NUM_VERTS = len(trimesh.vs) energy_function = energy_lookup[args.energy] energy_only = lambda x: energy_function( x, trimesh, gradient_mode=0, NUM_VERTS=NUM_VERTS) grad_only = lambda x: energy_function( x, trimesh, gradient_mode=1, NUM_VERTS=NUM_VERTS) # the first guess at the solution is the object itself first_guess = trimesh.vs.reshape(NUM_VERTS * 3) approximate_gradient = \ optimize.approx_fprime(first_guess, energy_only, [0.01]) extact_gradient = grad_only(first_guess) print "The difference between exact and numerical grad is: {}".format( norm(approximate_gradient - extact_gradient))
def __init__(self, **kwargs): GLUTWindow.__init__(self) kwargs.setdefault('background_color', (.3, .3, .3)) kwargs.setdefault('mesh', TriMesh()) kwargs.setdefault('linestrips', []) kwargs.setdefault('points', []) kwargs.setdefault('camera', Camera()) kwargs.setdefault('draw_faces', True) kwargs.setdefault('draw_edges', True) kwargs.setdefault('draw_linestrips', True) kwargs.setdefault('draw_points', True) self.background_color = kwargs['background_color'] self.mesh = kwargs['mesh'] self.linestrips = kwargs['linestrips'] self.points = kwargs['points'] self.camera = kwargs['camera'] self.draw_faces = kwargs['draw_faces'] self.draw_edges = kwargs['draw_edges'] self.draw_linestrips = kwargs['draw_linestrips'] self.draw_points = kwargs['draw_points'] ## I don't want dynamic binding here because ## subclasses' constructors haven't run yet TriMeshWindow.reset(self)
def asTriMesh( self ): if 0 in self.data.shape: raise Warning('empty image') from trimesh import TriMesh grid = TriMesh.grid( self.data.shape[1] - 1, self.data.shape[2] - 1 ) return grid
def main(): if len(sys.argv) != 2: print >> sys.stderr, 'Usage:', sys.argv[0], 'mesh.obj' sys.exit(-1) mesh_path = sys.argv[1] pid = view_mesh(TriMesh.FromOBJ_FileName(mesh_path), mesh_path) print 'Background process id:', pid
def grid2trimesh( grid, four_eight = False, mask = None ): vs, faces, uvs = grid2vertices_and_faces_and_uvs( grid, four_eight = four_eight ) from trimesh import TriMesh mesh = TriMesh() mesh.vs = vs mesh.faces = faces if uvs is not None: assert len( uvs ) == len( vs ) mesh.uvs = uvs if mask is not None: mask = asarray( mask, dtype = bool ) assert mask.shape == grid.shape #''' print( 'Removing masked vertices...' ) remove_vs_indices = [ vi for vi in xrange(len( mesh.vs )) if not mask[ mask.shape[0] - 1 - mesh.vs[vi][1], mesh.vs[vi][0] ] ] #print( 'remove_vs_indices:' ) #print( remove_vs_indices ) mesh.remove_vertex_indices( remove_vs_indices ) print( 'Finished removing masked vertices.' ) ''' ## Alternative def vi_in_mask( vi ): return mask[ mask.shape[0] - 1 - mesh.vs[vi][1], mesh.vs[vi][0] ] print( 'Removing faces between masked and unmasked vertices...' ) remove_face_indices = [ fi for fi in xrange(len( mesh.faces )) if len(set([ vi_in_mask( vi ) for vi in mesh.faces[fi]])) != 1 ] #print( 'remove_face_indices:' ) #print( remove_face_indices ) mesh.remove_face_indices( remove_face_indices ) print( 'Finished removing faces between masked and unmasked vertices.' ) #''' return mesh
def buildmesh( self, levels ): '''builds a list of triangle meshs starting with an icosadehron this base mesh is subdivided into many levels each lower level has 4 times as many faces subdivided points are normalized to lie on the surface of the sphere''' lpyramid = [TriMesh.icosa()] for i in xrange(0, levels): lnext = lpyramid[i].subdivide() lnext.normalizepoints() lpyramid.append(lnext) self.mesh = lpyramid[-1] self.mesh.name = 'sphere'
def importFile(self, filename, flags=GEOM_INIT_ALL, parent=None): """Import a 3DS file.""" self.filename = filename self.ddds = _core.File3ds() self.ddds.load(filename) # f = self.ddds.current_frame f = getScene().timer().frame if f<self.ddds.segment_from: f = self.ddds.segment_from if f>self.ddds.segment_to: f = self.ddds.segment_to self.ddds.eval(f) # Create a dictionary containing all available meshes. # Key is the mesh name. This is used to check if all meshes have # been processed (i.e. if there were corresponding nodes). self.meshes = {} m = self.ddds.meshes() while m!=None: if self.meshes.has_key(m.name): print "Warning: Duplicate mesh names in 3ds file" self.meshes[m.name] = m m = m.next() # Create objects... self.materials = {} self.createObject(self.ddds.nodes(), parent=parent, flags=flags) del self.materials # Create TriMeshes for all left over meshes... for n in self.meshes: mesh = self.meshes[n] tm = TriMeshGeom() mesh.initGeom(tm, flags) worldobj = TriMesh(name = mesh.name, parent=parent) worldobj.geom = tm self.ddds.free()
def importFile(self, filename, flags=GEOM_INIT_ALL, parent=None): """Import a 3DS file.""" self.filename = filename self.ddds = _core.File3ds() self.ddds.load(filename) # f = self.ddds.current_frame f = getScene().timer().frame if f < self.ddds.segment_from: f = self.ddds.segment_from if f > self.ddds.segment_to: f = self.ddds.segment_to self.ddds.eval(f) # Create a dictionary containing all available meshes. # Key is the mesh name. This is used to check if all meshes have # been processed (i.e. if there were corresponding nodes). self.meshes = {} m = self.ddds.meshes() while m != None: if m.name in self.meshes: print("Warning: Duplicate mesh names in 3ds file") self.meshes[m.name] = m m = m.next() # Create objects... self.materials = {} self.createObject(self.ddds.nodes(), parent=parent, flags=flags) del self.materials # Create TriMeshes for all left over meshes... for n in self.meshes: mesh = self.meshes[n] tm = TriMeshGeom() mesh.initGeom(tm, flags) worldobj = TriMesh(name=mesh.name, parent=parent) worldobj.geom = tm self.ddds.free()
def optimize_energy(args): print "loading {}".format(args.in_obj) trimesh = TriMesh.FromOBJ_FileName(args.in_obj) energy_function = energy_lookup[args.energy] # a lot of meshes have duplicate vertices associated # with adjacent faces, clean that up. trimesh = util.deduplicate_trimesh_vertices(trimesh) NUM_VERTS = len(trimesh.vs) # curry different versions of the energy function and the starting mesh # these are now functions of a single variable that can be optimized energy_only = lambda x: energy_function( x, trimesh, gradient_mode=0, NUM_VERTS=NUM_VERTS) energy_and_grad = lambda x: energy_function( x, trimesh, gradient_mode=2, NUM_VERTS=NUM_VERTS) # the first guess at the solution is the object itself first_guess = trimesh.vs.reshape(NUM_VERTS * 3) print "starting optimization for mesh with {} verts".format(NUM_VERTS) result = \ optimize.minimize( fun=energy_and_grad if args.exact_grad else energy_only, x0=first_guess, options={'disp': True}, method="L-BFGS-B", constraints=None, jac=args.exact_grad) final_mesh_verts = result.x.reshape(NUM_VERTS, 3) trimesh.vs = final_mesh_verts print "writing: {}".format(args.out_obj) trimesh.write_OBJ(args.out_obj)
def grid2trimesh(grid, four_eight=False, mask=None): vs, faces, uvs = grid2vertices_and_faces_and_uvs(grid, four_eight=four_eight) from trimesh import TriMesh mesh = TriMesh() mesh.vs = vs mesh.faces = faces if uvs is not None: assert len(uvs) == len(vs) mesh.uvs = uvs if mask is not None: mask = asarray(mask, dtype=bool) assert mask.shape == grid.shape #''' print('Removing masked vertices...') remove_vs_indices = [ vi for vi in xrange(len(mesh.vs)) if not mask[mask.shape[0] - 1 - mesh.vs[vi][1], mesh.vs[vi][0]] ] #print( 'remove_vs_indices:' ) #print( remove_vs_indices ) mesh.remove_vertex_indices(remove_vs_indices) print('Finished removing masked vertices.') ''' ## Alternative def vi_in_mask( vi ): return mask[ mask.shape[0] - 1 - mesh.vs[vi][1], mesh.vs[vi][0] ] print( 'Removing faces between masked and unmasked vertices...' ) remove_face_indices = [ fi for fi in xrange(len( mesh.faces )) if len(set([ vi_in_mask( vi ) for vi in mesh.faces[fi]])) != 1 ] #print( 'remove_face_indices:' ) #print( remove_face_indices ) mesh.remove_face_indices( remove_face_indices ) print( 'Finished removing faces between masked and unmasked vertices.' ) #''' return mesh
def createObject(self, node, parent=None, flags=GEOM_INIT_ALL): """ \param node (\c Lib3dsNode) Current node \param parent (\c WorldObject) Parent object or None \param flags (\c int) Flags for mesh creation \todo worldtransform als Slot \todo Pivot so setzen wie in 3DS """ if node==None: return worldobj = None auto_insert = True if parent!=None: auto_insert = False # Object? if node.type==TYPE_OBJECT: if node.name!="$$$DUMMY" and node.name!="_Quader01": data = node.object_data mesh = self.ddds.meshByName(node.name) if self.meshes.has_key(mesh.name): del self.meshes[mesh.name] # print "###",node.name,"###" # print "Node matrix:" # print node.matrix # print "Pivot:",data.pivot # print "Mesh matrix:" # print mesh.matrix tm = TriMeshGeom() mesh.initGeom(tm, flags) if parent==None: PT = mat4().translation(-data.pivot) m = node.matrix*PT*mesh.matrix.inverse() else: PT = mat4().translation(-data.pivot) m = node.matrix*PT*mesh.matrix.inverse() # worldtransform von Parent bestimmen... pworldtransform = parent.worldtransform # pworldtransform = parent.localTransform() # p = parent.parent # while p!=None: # pworldtransform = p.localTransform()*pworldtransform # p = p.parent m = pworldtransform.inverse()*m worldobj = TriMesh(name=node.name, # pivot=data.pivot, transform=m, auto_insert=auto_insert) worldobj.geom = tm # Set the materials matnames = mesh.materialNames() worldobj.setNumMaterials(len(matnames)) for i in range(len(matnames)): if matnames[i]=="": material = GLMaterial() # Was the material already instantiated? elif matnames[i] in self.materials: material = self.materials[matnames[i]] else: mat = self.ddds.materialByName(matnames[i]) # print "Material:",matnames[i] # print " self_illum:",mat.self_illum # print " self_ilpct:",mat.self_ilpct material = Material3DS(name = matnames[i], ambient = mat.ambient, diffuse = mat.diffuse, specular = mat.specular, shininess = mat.shininess, shin_strength = mat.shin_strength, use_blur = mat.use_blur, transparency = mat.transparency, falloff = mat.falloff, additive = mat.additive, use_falloff = mat.use_falloff, self_illum = mat.self_illum, self_ilpct = getattr(mat, "self_ilpct", 0), shading = mat.shading, soften = mat.soften, face_map = mat.face_map, two_sided = mat.two_sided, map_decal = mat.map_decal, use_wire = mat.use_wire, use_wire_abs = mat.use_wire_abs, wire_size = mat.wire_size, texture1_map = self._createTexMap(mat.texture1_map), texture2_map = self._createTexMap(mat.texture2_map), opacity_map = self._createTexMap(mat.opacity_map), bump_map = self._createTexMap(mat.bump_map), specular_map = self._createTexMap(mat.specular_map), shininess_map = self._createTexMap(mat.shininess_map), self_illum_map = self._createTexMap(mat.self_illum_map), reflection_map = self._createTexMap(mat.reflection_map) ) self.materials[matnames[i]] = material worldobj.setMaterial(material, i) # Camera? elif node.type==TYPE_CAMERA: cam = self.ddds.cameraByName(node.name) # print "Camera:",node.name # print " Roll:",cam.roll # print node.matrix # Convert the FOV from horizontal to vertical direction # (Todo: What aspect ratio to use?) fov = degrees(atan(480/640.0*tan(radians(cam.fov/2.0))))*2.0 worldobj = TargetCamera(name = node.name, pos = cam.position, target = cam.target, fov = fov, auto_insert = auto_insert) # Light? elif node.type==TYPE_LIGHT: lgt = self.ddds.lightByName(node.name) worldobj = self._createLight(node, lgt, auto_insert) if worldobj!=None and parent!=None: parent.addChild(worldobj) if worldobj!=None: self.createObject(node.childs(), worldobj, flags=flags) else: self.createObject(node.childs(), parent, flags=flags) self.createObject(node.next(), parent, flags=flags)
def testmulti( solver_kind = SeamSolver.kplane): #kfree ): #kzup ): '''computes frustum/mesh intersection and other stuff build a pyramid, build frustums and find seams''' levels = 5 nfrustums = 25 frustumscale = .4 squishscale = .1 seed = seedarg() solver = SeamSolver( levels, solver_kind ) meshlist = [solver.mesh] squish = numpy.identity(4) if solver_kind == SeamSolver.kplane: squishvec = num.randunitpt( seed ) squish = num.vecspace( squishvec ) meshlist.append( vecmesh( squishvec, 'squish' ) ) if solver_kind in (SeamSolver.kzup, SeamSolver.kplane): squish[2, :] *= squishscale frustums = randfrustums( nfrustums, frustumscale, solver, squish, seed ) solver.markfrustums(frustums) solver.mesh.uvzeros() solver.mesh.uvs[:, 0] = 1-solver.vertweight solver.mesh.uvs[:, 1] = solver.vertvis seams = solver.run() if seams: dummy, vis, f = seams[0] polevec = num.pointmatrixmult( num.Z, f.ctw ) meshlist.append( vecmesh( polevec[0], 'pole' ) ) seamslice = TriMesh.cube() seamslice.points[:, 0] *= .5 seamslice.points[:, 0] -= .5 seamslice.points[:, 0] *= 1.1 seamslice.points[:, 1] *= solver.mesh.edgestats()[-1] seamslice.points[:, 1] *= solver.slicetolerance_halfwidth seamslice.points[:, 2] *= 1.1 seamslice.xform( f.ctw ) seamslice.name = 'seam' meshlist.append( seamslice ) flat = solver.mesh.copy().polar(f) flat.name = 'unwrap' meshlist.append( flat ) solver.mesh.uvs[vis] = 1, 1 else: print('no seams found')
def end(self, name): faces = [] for i in range(self.numfaces): faces.append(range(i * 3, i * 3 + 3)) TriMesh(name=name, verts=self.verts, faces=faces)
def createObject(self, node, parent=None, flags=GEOM_INIT_ALL): """ \param node (\c Lib3dsNode) Current node \param parent (\c WorldObject) Parent object or None \param flags (\c int) Flags for mesh creation \todo worldtransform als Slot \todo Pivot so setzen wie in 3DS """ if node == None: return worldobj = None auto_insert = True if parent != None: auto_insert = False # Object? if node.type == TYPE_OBJECT: if node.name != "$$$DUMMY" and node.name != "_Quader01": data = node.object_data mesh = self.ddds.meshByName(node.name) if mesh.name in self.meshes: del self.meshes[mesh.name] # print "###",node.name,"###" # print "Node matrix:" # print node.matrix # print "Pivot:",data.pivot # print "Mesh matrix:" # print mesh.matrix tm = TriMeshGeom() mesh.initGeom(tm, flags) if parent == None: PT = mat4().translation(-data.pivot) m = node.matrix * PT * mesh.matrix.inverse() else: PT = mat4().translation(-data.pivot) m = node.matrix * PT * mesh.matrix.inverse() # worldtransform von Parent bestimmen... pworldtransform = parent.worldtransform # pworldtransform = parent.localTransform() # p = parent.parent # while p!=None: # pworldtransform = p.localTransform()*pworldtransform # p = p.parent m = pworldtransform.inverse() * m worldobj = TriMesh( name=node.name, # pivot=data.pivot, transform=m, auto_insert=auto_insert, ) worldobj.geom = tm # Set the materials matnames = mesh.materialNames() worldobj.setNumMaterials(len(matnames)) for i in range(len(matnames)): if matnames[i] == "": material = GLMaterial() # Was the material already instantiated? elif matnames[i] in self.materials: material = self.materials[matnames[i]] else: mat = self.ddds.materialByName(matnames[i]) # print "Material:",matnames[i] # print " self_illum:",mat.self_illum # print " self_ilpct:",mat.self_ilpct material = Material3DS( name=matnames[i], ambient=mat.ambient, diffuse=mat.diffuse, specular=mat.specular, shininess=mat.shininess, shin_strength=mat.shin_strength, use_blur=mat.use_blur, transparency=mat.transparency, falloff=mat.falloff, additive=mat.additive, use_falloff=mat.use_falloff, self_illum=mat.self_illum, self_ilpct=getattr(mat, "self_ilpct", 0), shading=mat.shading, soften=mat.soften, face_map=mat.face_map, two_sided=mat.two_sided, map_decal=mat.map_decal, use_wire=mat.use_wire, use_wire_abs=mat.use_wire_abs, wire_size=mat.wire_size, texture1_map=self._createTexMap(mat.texture1_map), texture2_map=self._createTexMap(mat.texture2_map), opacity_map=self._createTexMap(mat.opacity_map), bump_map=self._createTexMap(mat.bump_map), specular_map=self._createTexMap(mat.specular_map), shininess_map=self._createTexMap(mat.shininess_map), self_illum_map=self._createTexMap(mat.self_illum_map), reflection_map=self._createTexMap(mat.reflection_map), ) self.materials[matnames[i]] = material worldobj.setMaterial(material, i) # Camera? elif node.type == TYPE_CAMERA: cam = self.ddds.cameraByName(node.name) # print "Camera:",node.name # print " Roll:",cam.roll # print node.matrix # Convert the FOV from horizontal to vertical direction # (Todo: What aspect ratio to use?) fov = degrees(atan(480 / 640.0 * tan(radians(cam.fov / 2.0)))) * 2.0 worldobj = TargetCamera( name=node.name, pos=cam.position, target=cam.target, fov=fov, auto_insert=auto_insert ) # Light? elif node.type == TYPE_LIGHT: lgt = self.ddds.lightByName(node.name) worldobj = self._createLight(node, lgt, auto_insert) if worldobj != None and parent != None: parent.addChild(worldobj) if worldobj != None: self.createObject(node.childs(), worldobj, flags=flags) else: self.createObject(node.childs(), parent, flags=flags) self.createObject(node.next(), parent, flags=flags)
import time start_time=time.clock() images=np.asfarray(Image.open(input_image_path).convert('RGB')).reshape((-1,3)) hull=ConvexHull(images) origin_hull=hull # visualize_hull(hull) write_convexhull_into_obj_file(hull, output_rawhull_obj_file) N=500 mesh=TriMesh.FromOBJ_FileName(output_rawhull_obj_file) print ('original vertices number:',len(mesh.vs)) for i in range(N): print ('loop:', i) old_num=len(mesh.vs) mesh=TriMesh.FromOBJ_FileName(output_rawhull_obj_file) mesh=remove_one_edge_by_finding_smallest_adding_volume_with_test_conditions(mesh,option=2) newhull=ConvexHull(mesh.vs) write_convexhull_into_obj_file(newhull, output_rawhull_obj_file) print ('current vertices number:', len(mesh.vs))
def createTriMesh(self, parent=None, name=None): """Create a triangle mesh from the current set of faces. This method may only be called if the faces really have only 3 vertices. Returns the TriMesh object. """ # Build lookup tables (so that only the verts that are really # required are stored in the TriMesh) # # Key: Original vertex index - Value: New vertex index vert_lut = {} has_normals = True has_tverts = True iv = 0 for f in self.faces: for v,tv,n in f: if v not in vert_lut: vert_lut[v] = iv iv+=1 if tv==None: has_tverts = False if n==None: has_normals = False numfaces = len(self.faces) numverts = len(vert_lut) tm = TriMeshGeom() tm.verts.resize(numverts) tm.faces.resize(numfaces) # Set vertices for v in vert_lut: newidx = vert_lut[v] tm.verts[newidx] = self.verts[v] # Set faces idx = 0 for i,f in enumerate(self.faces): fi = [] for v,tv,n in f: fi.append(vert_lut[v]) tm.faces[i] = fi # Create variable N for storing the normals if has_normals: tm.newVariable("N", FACEVARYING, NORMAL) N = tm.slot("N") idx = 0 for f in self.faces: for v,tv,n in f: N[idx] = self.normals[n] idx += 1 # Set texture vertices if has_tverts: tm.newVariable("st", FACEVARYING, FLOAT, 2) st = tm.slot("st") idx = 0 for f in self.faces: for v,tv,n in f: st[idx] = self.tverts[tv] idx += 1 obj = TriMesh(name=name, parent=parent) obj.geom = tm # Set the materials self.initMaterial(obj) return obj
def compute_color(input_im, N): ######*********************************************************************************************** #### 3D case: use method in paper: "Progressive Hulls for Intersection Applications" #### also using trimesh.py interface from yotam gingold def visualize_hull(hull, groundtruth_hull=None): from matplotlib import pyplot as plt fig = plt.figure(figsize=(8, 8)) ax = fig.add_subplot(1, 1, 1, projection='3d') vertex = hull.points[hull.vertices] ax.scatter(vertex[:, 0], vertex[:, 1], vertex[:, 2], marker='*', color='red', s=40, label='class') # num=hull.simplices.shape[0] # points=[] # normals=[] # for i in range(num): # face=hull.points[hull.simplices[i]] # avg_point=(face[0]+face[1]+face[2])/3.0 # points.append(avg_point) # points=np.asarray(points) # ax.quiver(points[:,0],points[:,1],points[:,2],hull.equations[:,0],hull.equations[:,1],hull.equations[:,2],length=0.01) for simplex in hull.simplices: faces = hull.points[simplex] xs = list(faces[:, 0]) xs.append(faces[0, 0]) ys = list(faces[:, 1]) ys.append(faces[0, 1]) zs = list(faces[:, 2]) zs.append(faces[0, 2]) # print xs,ys,zs plt.plot(xs, ys, zs, 'k-') if groundtruth_hull != None: groundtruth_vertex = groundtruth_hull.points[ groundtruth_hull.vertices] ax.scatter(groundtruth_vertex[:, 0], groundtruth_vertex[:, 1], groundtruth_vertex[:, 2], marker='o', color='green', s=80, label='class') plt.title("3D Scatter Plot") plt.show() from trimesh import TriMesh def write_convexhull_into_obj_file(hull, output_rawhull_obj_file): hvertices = hull.points[hull.vertices] points_index = -1 * np.ones(hull.points.shape[0], dtype=int) points_index[hull.vertices] = np.arange(len(hull.vertices)) #### start from index 1 in obj files!!!!! hfaces = np.array([points_index[hface] for hface in hull.simplices]) + 1 #### to make sure each faces's points are countclockwise order. for index in range(len(hfaces)): face = hvertices[hfaces[index] - 1] normals = hull.equations[index, :3] p0 = face[0] p1 = face[1] p2 = face[2] n = np.cross(p1 - p0, p2 - p0) if np.dot(normals, n) < 0: hfaces[index][[1, 0]] = hfaces[index][[0, 1]] myfile = open(output_rawhull_obj_file, 'w') for index in range(hvertices.shape[0]): myfile.write('v ' + str(hvertices[index][0]) + ' ' + str(hvertices[index][1]) + ' ' + str(hvertices[index][2]) + '\n') for index in range(hfaces.shape[0]): myfile.write('f ' + str(hfaces[index][0]) + ' ' + str(hfaces[index][1]) + ' ' + str(hfaces[index][2]) + '\n') myfile.close() def edge_normal_test(vertices, faces, old_face_index_list, v0_ind, v1_ind): selected_old_face_list = [] central_two_face_list = [] for index in old_face_index_list: face = faces[index] face_temp = np.array(face).copy() face_temp = list(face_temp) if v0_ind in face_temp: face_temp.remove(v0_ind) if v1_ind in face_temp: face_temp.remove(v1_ind) if len( face_temp ) == 2: ### if left 2 points, then this face is what we need. selected_old_face = [ np.asarray(vertices[face[i]]) for i in range(len(face)) ] selected_old_face_list.append(np.asarray(selected_old_face)) if len( face_temp ) == 1: ##### if left 1 points, then this face is central face. central_two_face = [ np.asarray(vertices[face[i]]) for i in range(len(face)) ] central_two_face_list.append(np.asarray(central_two_face)) assert (len(central_two_face_list) == 2) if len(central_two_face_list) + len(selected_old_face_list) != len( old_face_index_list): print 'error!!!!!!' central_two_face_normal_list = [] neighbor_face_dot_normal_list = [] for face in central_two_face_list: n = np.cross(face[1] - face[0], face[2] - face[0]) n = n / np.sqrt(np.dot(n, n)) central_two_face_normal_list.append(n) avg_edge_normal = np.average(np.array(central_two_face_normal_list), axis=0) for face in selected_old_face_list: n = np.cross(face[1] - face[0], face[2] - face[0]) neighbor_face_dot_normal_list.append(np.dot(avg_edge_normal, n)) if (np.array(neighbor_face_dot_normal_list) >= 0.0 - 1e-5).all(): return 1 else: return 0 def compute_tetrahedron_volume(face, point): n = np.cross(face[1] - face[0], face[2] - face[0]) return abs(np.dot(n, point - face[0])) / 6.0 #### this is different from function: remove_one_edge_by_finding_smallest_adding_volume(mesh) #### add some test conditions to accept new vertex. #### if option ==1, return a new convexhull. #### if option ==2, return a new mesh (using trimesh.py) def remove_one_edge_by_finding_smallest_adding_volume_with_test_conditions( mesh, option): edges = mesh.get_edges() mesh.get_halfedges() faces = mesh.faces vertices = mesh.vs temp_list1 = [] temp_list2 = [] count = 0 for edge_index in range(len(edges)): edge = edges[edge_index] vertex1 = edge[0] vertex2 = edge[1] face_index1 = mesh.vertex_face_neighbors(vertex1) face_index2 = mesh.vertex_face_neighbors(vertex2) face_index = list(set(face_index1) | set(face_index2)) related_faces = [faces[index] for index in face_index] old_face_list = [] #### now find a point, so that for each face in related_faces will create a positive volume tetrahedron using this point. ### minimize c*x. w.r.t. A*x<=b c = np.zeros(3) A = [] b = [] for index in range(len(related_faces)): face = related_faces[index] p0 = vertices[face[0]] p1 = vertices[face[1]] p2 = vertices[face[2]] old_face_list.append(np.asarray([p0, p1, p2])) n = np.cross(p1 - p0, p2 - p0) #### Currently use this line. without this line, test_fourcolors results are not good. n = n / np.sqrt( np.dot(n, n) ) ##### use normalized face normals means distance, not volume A.append(n) b.append(np.dot(n, p0)) c += n ########### now use cvxopt.solvers.lp solver A = -np.asfarray(A) b = -np.asfarray(b) c = np.asfarray(c) cvxopt.solvers.options['show_progress'] = False #cvxopt.solvers.options['glpk'] = dict(msg_lev='GLP_MSG_OFF') #res = cvxopt.solvers.lp( cvxopt.matrix(c), cvxopt.matrix(A), cvxopt.matrix(b), solver='glpk' ) # After installing another version of lapacke.h, this one is goofed now!!! res = cvxopt.solvers.lp(cvxopt.matrix(c), cvxopt.matrix(A), cvxopt.matrix(b)) #res = linprog(-1*c, A_ub=A, b_ub=-1*b, method = 'simplex',options={'disp':False}) if res['status'] == 'optimal': newpoint = np.asfarray(res['x']).squeeze() ######## using objective function to calculate (volume) or (distance to face) as priority. # volume=res['primal objective']+b.sum() ####### manually compute volume as priority,so no relation with objective function tetra_volume_list = [] for each_face in old_face_list: tetra_volume_list.append( compute_tetrahedron_volume(each_face, newpoint)) volume = np.asarray(tetra_volume_list).sum() temp_list1.append((count, volume, vertex1, vertex2)) temp_list2.append(newpoint) count += 1 # else: # print 'cvxopt.solvers.lp is not optimal ', res['status'], np.asfarray( res['x'] ).squeeze() # if res['status']!='unknown': ### means solver failed # ##### check our test to see if the solver fails normally # if edge_normal_test(vertices,faces,face_index,vertex1,vertex2)==1: ### means all normal dot value are positive # print '!!!edge_normal_neighbor_normal_dotvalue all positive, but solver fails' if option == 1: if len(temp_list1) == 0: print 'all fails' hull = ConvexHull(mesh.vs) else: min_tuple = min(temp_list1, key=lambda x: x[1]) # print min_tuple final_index = min_tuple[0] final_point = temp_list2[final_index] # print 'final_point ', final_point new_total_points = mesh.vs new_total_points.append(final_point) hull = ConvexHull(np.array(new_total_points)) return hull if option == 2: if len(temp_list1) == 0: print 'all fails' else: min_tuple = min(temp_list1, key=lambda x: x[1]) # print min_tuple final_index = min_tuple[0] final_point = temp_list2[final_index] # print 'final_point ', final_point v1_ind = min_tuple[2] v2_ind = min_tuple[3] face_index1 = mesh.vertex_face_neighbors(v1_ind) face_index2 = mesh.vertex_face_neighbors(v2_ind) face_index = list(set(face_index1) | set(face_index2)) related_faces_vertex_ind = [ faces[index] for index in face_index ] old2new = mesh.remove_vertex_indices([v1_ind, v2_ind]) ### give the index to new vertex. new_vertex_index = current_vertices_num = len( old2new[old2new != -1]) ### create new face with new vertex index. new_faces_vertex_ind = [] for face in related_faces_vertex_ind: new_face = [ new_vertex_index if x == v1_ind or x == v2_ind else old2new[x] for x in face ] if len(list(set(new_face))) == len(new_face): new_faces_vertex_ind.append(new_face) ##### do not clip coordinates to[0,255]. when simplification done, clip. mesh.vs.append(final_point) ##### clip coordinates during simplification! # mesh.vs.append(final_point.clip(0.0,255.0)) for face in new_faces_vertex_ind: mesh.faces.append(face) mesh.topology_changed() return mesh # ************ compute ************ output_rawhull_obj_file = 'misc_param/temp' + "-rawconvexhull.obj" E_vertice_num = 4 import time start_time = time.clock() #images=np.asfarray(Image.open(input_image_path).convert('RGB')).reshape((-1,3)) all_colors = input_im.reshape((-1, 3)) ## insert a white and black pixel to make sure we got one [somtimes max-project does not have dark pixels] #all_colors[1,:] = np.asarray([0,0,0]) #all_colors[0,:] = np.asarray([191,191,191]) # white pixels are uninformative #hull=ConvexHull(images) hull = ConvexHull(all_colors) origin_hull = hull write_convexhull_into_obj_file(hull, output_rawhull_obj_file) mesh = TriMesh.FromOBJ_FileName(output_rawhull_obj_file) print '- Original hull vertices number:', len(mesh.vs) for i in range(500): old_num = len(mesh.vs) mesh = TriMesh.FromOBJ_FileName(output_rawhull_obj_file) #print len(mesh.vs) mesh = remove_one_edge_by_finding_smallest_adding_volume_with_test_conditions( mesh, option=2) newhull = ConvexHull(mesh.vs) write_convexhull_into_obj_file(newhull, output_rawhull_obj_file) pigments_colors = newhull.points[newhull.vertices].clip( 0, 255).round().astype(np.uint8) pigments_colors = pigments_colors.reshape( (pigments_colors.shape[0], 1, pigments_colors.shape[1])) #print pigments_colors.shape # add white to make sure (Not optimal, Heuristic) pigments_colors = np.vstack( (pigments_colors, np.asarray([191, 191, 191]).reshape(1, 1, 3))) white_index = [] black_index = [] #del_color = [] for i in range(pigments_colors.shape[0]): if pigments_colors[i, 0, 0] > 190.0 and pigments_colors[ i, 0, 1] > 190.0 and pigments_colors[i, 0, 2] > 190.0: #print '- White color detected and deleted' # dont wanna keep whie, not informative white_index.append(i) if pigments_colors[i, 0, 0] < 15.0 and pigments_colors[ i, 0, 1] < 15.0 and pigments_colors[i, 0, 2] < 15.0: black_index.append(i) #assert len(black_index) > 0 #assert len(white_index) < 2 # more than 1 white #if len(white_index) == 0: print '- No white pixels found, you got one extra layer, decrease number of colors (N)!' #print len(white_index) # make sure blck is the first color #print black_index, pigments_colors.shape #temp2 = pigments_colors[black_index,:] #pigments_colors[black_index,:] = pigments_colors[0,:] #pigments_colors[0,:] = temp2 #if 0 in set(white_index): # white_index[0] = black_index[0] # erase gray and white pigments_colors = np.delete(pigments_colors, white_index + black_index, axis=0) # insert [0, 0, 0] (Not optimal, Heuristic solution) pigments_colors = np.vstack( (np.asarray([0, 0, 0]).reshape(1, 1, 3), pigments_colors)) #print len(newhull.vertices) if pigments_colors.shape[ 0] == N + 1: # colors + black bakground + possible white #print 'FFFFF: ' #print pigments_colors color_vertices = np.asfarray( pigments_colors.reshape(pigments_colors.shape[0], 3)) break #if len(mesh.vs)==old_num or len(mesh.vs)<=E_vertice_num: #print '- Final vertices number', len(mesh.vs) #break #color_vertices = np.asfarray(pigments_colors.reshape(pigments_colors.shape[0],3)) newhull = ConvexHull(mesh.vs) write_convexhull_into_obj_file(newhull, output_rawhull_obj_file) end_time = time.clock() print 'time: ', end_time - start_time print '- Computed color vertices from the image:' print color_vertices print color_vertices.shape return color_vertices
def createTriMesh(self, parent=None, name=None): """Create a triangle mesh from the current set of faces. This method may only be called if the faces really have only 3 vertices. Returns the TriMesh object. """ # Build lookup tables (so that only the verts that are really # required are stored in the TriMesh) # # Key: Original vertex index - Value: New vertex index vert_lut = {} has_normals = True has_tverts = True iv = 0 for f in self.faces: for v, tv, n in f: if v not in vert_lut: vert_lut[v] = iv iv += 1 if tv == None: has_tverts = False if n == None: has_normals = False numfaces = len(self.faces) numverts = len(vert_lut) tm = TriMeshGeom() tm.verts.resize(numverts) tm.faces.resize(numfaces) # Set vertices for v in vert_lut: newidx = vert_lut[v] tm.verts[newidx] = self.verts[v] # Set faces idx = 0 for i, f in enumerate(self.faces): fi = [] for v, tv, n in f: fi.append(vert_lut[v]) tm.faces[i] = fi # Create variable N for storing the normals if has_normals: tm.newVariable("N", FACEVARYING, NORMAL) N = tm.slot("N") idx = 0 for f in self.faces: for v, tv, n in f: N[idx] = self.normals[n] idx += 1 # Set texture vertices if has_tverts: tm.newVariable("st", FACEVARYING, FLOAT, 2) st = tm.slot("st") idx = 0 for f in self.faces: for v, tv, n in f: st[idx] = self.tverts[tv] idx += 1 obj = TriMesh(name=name, parent=parent) obj.geom = tm # Set the materials self.initMaterial(obj) return obj
def importFile(self, filename, invertfaces=False): """Import an OFF file. If invertfaces is True the orientation of each face is reversed. """ self.invertfaces = invertfaces self.texcoord_flag = False self.color_flag = False self.normal_flag = False self.four_flag = False self.ndim_flag = False self.ndim = 3 self.is_trimesh = True self.fhandle = file(filename) # Read the header z = self.readLine() self.parseHeaderKeyWord(z) # nOFF? if self.ndim_flag: # Read the dimension of vertices z = self.readLine() self.ndim = int(z) if self.ndim > 3: raise ValueError( "A vertex space dimension of %d is not supported" % (self.ndim)) # Read the number of vertices and faces... z = self.readLine().split(" ") self.numverts = int(z[0]) self.numfaces = int(z[1]) # Start with a TriMeshGeom # (this will be later converted into a PolyhedronGeom if faces # with more than 3 vertices are encountered) self.geom = TriMeshGeom() self.geom.verts.resize(self.numverts) self.geom.faces.resize(self.numfaces) # Read the vertices... self.readVertices() # Read the faces... self.readFaces() self.fhandle.close() # Create the actual object... nodename = os.path.basename(filename) nodename = os.path.splitext(nodename)[0] nodename = nodename.replace(" ", "_") if self.is_trimesh: n = TriMesh(name=nodename) else: n = Polyhedron(name=nodename) n.geom = self.geom