def save_to_mesh(file_path, v, f): """ v : vertex data. f : face data. """ igl.write_triangle_mesh(file_path, v, f)
def transform(self, v: np.ndarray, f: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: # nv, nf, _, _, _ = igl.offset_surface(v, f, 0, 64, SIGNED_DISTANCE_TYPE_PSEUDONORMAL) igl.write_triangle_mesh("/home/zgong8/temp/mc_tmp_in.obj", v, f) subprocess.run(["/home/zgong8/libigl/tutorial/build/bin/705_MarchingCubes", "/home/zgong8/temp/mc_tmp_in.obj", "/home/zgong8/temp/mc_tmp_out_1.obj", "/home/zgong8/temp/mc_tmp_out_2.obj"]) nv, nf = igl.read_triangle_mesh("/home/zgong8/temp/mc_tmp_out_2.obj", np.float64) return nv, nf
def write_mesh(data_dir, vert, face, input_name): mesh_dir = os.path.join(data_dir, "meshes/") sysutil.mkdirs(mesh_dir) if vert.shape[0] < 10: vert, face = np.array([[0, 0, 0.]]), np.array([[0, 0, 0]]) igl.write_triangle_mesh(os.path.join(mesh_dir, input_name + ".ply"), vert, face, force_ascii=False)
def process_shape_file(self, in_file: str, out_file: str): assert self.is_full() vertices, faces = igl.read_triangle_mesh(in_file, np.float64) new_vertices, new_faces = self.process_shape_data(vertices, faces) if new_faces is not None: if len(new_faces) < max_output_faces: igl.write_triangle_mesh(out_file, new_vertices, new_faces) return True else: print("Too large, bad for connection, dropped in the end.") return False
def inner_surface(in_path, out_path, depth=1, nsteps=1): '''compute inner surfaces from mesh at in_path, save results''' # load input mesh v, f = igl.read_triangle_mesh(in_path) v2array = inner_surface_mesh(v, f, depth=depth, nsteps=nsteps) if nsteps == 1: igl.write_triangle_mesh(out_path, v2array[0], f, force_ascii=False) else: split = out_path.split(".") for step in range(nsteps): step_str = "%03i" % (step + 1) out_path_step = ".".join([*(split[:-2]), step_str, split[-1]]) igl.write_triangle_mesh(out_path_step, v2array[step], f, force_ascii=False)
try: # get binary mask nii = nib.load(in_file) binary_mask = nii.get_fdata() # get affine translation dx = nii.affine[0][3] dy = nii.affine[1][3] dz = nii.affine[2][3] aff = np.asarray([-dx, -dy, dz]) # print(nii.affine) # create mesh distance = distance_transform_edt(binary_mask) verts, faces, _, _ = measure.marching_cubes( distance, 0, spacing=(abs(nii.affine[0][0]), abs(nii.affine[1][1]), abs(nii.affine[2][2])), gradient_direction="ascent", method='lewiner') # apply affine translation to vertices verts2 = [v + aff for v in verts] verts2 = np.asarray(verts2) # save igl.write_triangle_mesh(out_file, verts2, faces) except: print('ERROR: cannot create ' + sys.argv[2])
# make apex covariance sharper c[0] *= 0.001 vs2, fs2 = loop_gps(v, f, c, 5) # make apex covariance vertically anisotropic (reduce variance in x direction) c[0] = [[0.001, 0, 0], [0, 0.01, 0], [0, 0, 0.01]] vs3, fs3 = loop_gps(v, f, c, 5) # make apex covariance horizontally anisotropic (reduce variance in y direction) c[0] = [[0.01, 0, 0], [0, 0.001, 0], [0, 0, 0.01]] vs4, fs4 = loop_gps(v, f, c, 5) # make apex covariance horizontally anisotropic and super flat c[0] = [[1, 0, 0], [0, 0.00001, 0], [0, 0, 1]] vs5, fs5 = loop_gps(v, f, c, 5) # save meshes to .off files igl.write_triangle_mesh(os.path.join(root_folder, "data", "cone.off"), v, f) print("Wrote control mesh to", os.path.join(root_folder, "data", "cone.off")) igl.write_triangle_mesh( os.path.join(root_folder, "data", "cone-pointy-gps.off"), vs2, fs2) igl.write_triangle_mesh( os.path.join(root_folder, "data", "cone-concave-gps.off"), vs3, fs3) igl.write_triangle_mesh( os.path.join(root_folder, "data", "cone-plateau-gps.off"), vs4, fs4) igl.write_triangle_mesh( os.path.join(root_folder, "data", "cone-cylinder-gps.off"), vs5, fs5) print("Wrote GPS meshes to", os.path.join(root_folder, "data", "cone-<variant>-gps.off"))
def main(): parser = argparse.ArgumentParser() parser.add_argument('model_weights_path', type=str, help='path to the model checkpoint') parser.add_argument('input_path', type=str, help='path to the input') parser.add_argument('--disable_cuda', action='store_true', help='disable cuda') parser.add_argument('--sample_cloud', type=int, help='run on sampled points') parser.add_argument('--n_rounds', type=int, default=5, help='number of rounds') parser.add_argument('--prob_thresh', type=float, default=.9, help='threshold for final surface') parser.add_argument( '--output', type=str, help='path to save the resulting high prob mesh to. also disables viz') parser.add_argument('--output_trim_unused', action='store_true', help='trim unused vertices when outputting') # Parse arguments args = parser.parse_args() set_args_defaults(args) viz = not args.output args.polyscope = False # Initialize polyscope if viz: polyscope.init() # === Load the input if args.input_path.endswith(".npz"): record = np.load(args.input_path) verts = torch.tensor(record['vert_pos'], dtype=args.dtype, device=args.device) surf_samples = torch.tensor(record['surf_pos'], dtype=args.dtype, device=args.device) samples = verts.clone() faces = torch.zeros((0, 3), dtype=torch.int64, device=args.device) polyscope.register_point_cloud("surf samples", toNP(surf_samples)) if args.input_path.endswith(".xyz"): raw_pts = np.loadtxt(args.input_path) verts = torch.tensor(raw_pts, dtype=args.dtype, device=args.device) samples = verts.clone() faces = torch.zeros((0, 3), dtype=torch.int64, device=args.device) polyscope.register_point_cloud("surf samples", toNP(verts)) else: print("reading mesh") verts, faces = utils.read_mesh(args.input_path) print(" {} verts {} faces".format(verts.shape[0], faces.shape[0])) verts = torch.tensor(verts, dtype=args.dtype, device=args.device) faces = torch.tensor(faces, dtype=torch.long, device=args.device) # verts = verts[::10,:] if args.sample_cloud: samples = mesh_utils.sample_points_on_surface( verts, faces, args.sample_cloud) else: samples = verts.clone() # === Load the model print("loading model weights") model = PointTriNet_Mesher() model.load_state_dict(torch.load(args.model_weights_path)) model.eval() with torch.no_grad(): # Sample lots of faces from the vertices print("predicting") candidate_triangles, candidate_probs = model.predict_mesh( samples.unsqueeze(0), n_rounds=args.n_rounds) candidate_triangles = candidate_triangles.squeeze(0) candidate_probs = candidate_probs.squeeze(0) print("done predicting") # Visualize high_prob = args.prob_thresh high_faces = candidate_triangles[candidate_probs > high_prob] closed_faces = mesh_utils.fill_holes_greedy(high_faces) if viz: polyscope.register_point_cloud("input points", toNP(samples)) spmesh = polyscope.register_surface_mesh("all faces", toNP(samples), toNP(candidate_triangles), enabled=False) spmesh.add_scalar_quantity("probs", toNP(candidate_probs), defined_on='faces') spmesh = polyscope.register_surface_mesh( "high prob mesh " + str(high_prob), toNP(samples), toNP(high_faces)) spmesh.add_scalar_quantity( "probs", toNP(candidate_probs[candidate_probs > high_prob]), defined_on='faces') spmesh = polyscope.register_surface_mesh("hole-closed mesh " + str(high_prob), toNP(samples), toNP(closed_faces), enabled=False) polyscope.show() # Save output if args.output: high_prob = args.prob_thresh out_verts = toNP(samples) out_faces = toNP(high_faces) out_faces_closed = toNP(closed_faces) if args.output_trim_unused: out_verts, out_faces, _, _ = igl.remove_unreferenced( out_verts, out_faces) igl.write_triangle_mesh(args.output + "_mesh.ply", out_verts, out_faces) write_ply_points(args.output + "_samples.ply", toNP(samples)) igl.write_triangle_mesh(args.output + "_pred_mesh.ply", out_verts, out_faces) igl.write_triangle_mesh(args.output + "_pred_mesh_closed.ply", out_verts, out_faces_closed) write_ply_points(args.output + "_samples.ply", toNP(samples))
# 1. Install miniconda (python 3.7 version, windows 64 bits) # 2. In a miniconda terminal run conda install igl # 3. Swap to this python compiler in pycharm import igl import numpy as np import os #Change this to point to your subject folder root_folder = '.' subject_id_usr = os.path.join(root_folder, 'subjects folder', 'subject_01') #Make sure you've at least simplified the mesh of the subject vertices = np.load(os.path.join(subject_id_usr, 'vertices_simple.npy')) faces = np.load(os.path.join(subject_id_usr, 'faces_simple.npy')) ret = igl.write_triangle_mesh(os.path.join(subject_id_usr, "bunny_out.obj"), vertices, faces) v, f = igl.read_triangle_mesh(os.path.join(subject_id_usr, "bunny_out.obj")) [pd1, pd2, pv1, pv2] = igl.principal_curvature(v, f, radius=5, use_k_ring=True) #Mean curvature is (pv1+pv2)/2 and Gaussian curvature is pv1*pv2 # k = igl.gaussian_curvature(v, f) np.save(os.path.join(subject_id_usr, 'mean_curv_simple.npy'), (pv1 + pv2) / 2) #Deletes temporary object os.remove(os.path.join(subject_id_usr, "bunny_out.obj"))
def test_write_triangle_mesh(self): ok = igl.write_triangle_mesh("out.obj", self.v, self.f) self.assertEqual(ok, True)
def mesh_model(model, res_path, convert=True, all_edges=True): fil = model.split("/")[-1][:-5] folder = "/".join(model.split("/")[:-1]) with fileinput.FileInput(model, inplace=True) as fi: for line in fi: print(line.replace( "UNCERTAINTY_MEASURE_WITH_UNIT( LENGTH_MEASURE( 1.00000000000000E-06 )", "UNCERTAINTY_MEASURE_WITH_UNIT( LENGTH_MEASURE( 1.00000000000000E-17 )" ), end='') occ_steps = read_step_file(model) bt = BRep_Tool() for occ_cnt in range(len(occ_steps)): if convert: try: nurbs_converter = BRepBuilderAPI_NurbsConvert( occ_steps[occ_cnt]) nurbs_converter.Perform(occ_steps[occ_cnt]) nurbs = nurbs_converter.Shape() except: print("Conversion failed") continue else: nurbs = occ_steps[occ_cnt] mesh = BRepMesh_IncrementalMesh(occ_steps[occ_cnt], 0.9, False, 0.5, True) mesh.Perform() if not mesh.IsDone(): print("Mesh is not done.") continue occ_topo = TopologyExplorer(nurbs) occ_top = Topo(nurbs) occ_topo1 = TopologyExplorer(occ_steps[occ_cnt]) occ_top1 = Topo(occ_steps[occ_cnt]) d1_feats = [] d2_feats = [] t_curves = [] tr_curves = [] stats = {} stats["model"] = model total_edges = 0 total_surfs = 0 stats["curves"] = [] stats["surfs"] = [] c_cnt = 0 t_cnt = 0 # Iterate over edges for edge in occ_topo.edges(): curve = BRepAdaptor_Curve(edge) stats["curves"].append(edge_map[curve.GetType()]) d1_feat = convert_curve(curve) if edge_map[curve.GetType()] == "Other": continue for f in occ_top.faces_from_edge(edge): if f == None: print("Broken face") continue su = BRepAdaptor_Surface(f) c = BRepAdaptor_Curve2d(edge, f) t_curve = { "surface": f, "3dcurve": edge, "3dcurve_id": c_cnt, "2dcurve_id": t_cnt } t_curves.append(t_curve) tr_curves.append(convert_2dcurve(c)) t_cnt += 1 d1_feats.append(d1_feat) c_cnt += 1 total_edges += 1 patches = [] faces1 = list(occ_topo1.faces()) # Iterate over faces for fci, face in enumerate(occ_topo.faces()): surf = BRepAdaptor_Surface(face) stats["surfs"].append(surf_map[surf.GetType()]) d2_feat = convert_surface(surf) if surf_map[surf.GetType()] == "Other": continue for tc in t_curves: if tc["surface"] == face: patch = { "3dcurves": [], "2dcurves": [], "orientations": [], "surf_orientation": face.Orientation(), "wire_ids": [], "wire_orientations": [] } for wc, fw in enumerate(occ_top.wires_from_face(face)): patch["wire_orientations"].append(fw.Orientation()) if all_edges: edges = [ i for i in WireExplorer(fw).ordered_edges() ] else: edges = list(occ_top.edges_from_wire(fw)) for fe in edges: for ttc in t_curves: if ttc["3dcurve"].IsSame(fe) and tc[ "surface"] == ttc["surface"]: patch["3dcurves"].append(ttc["3dcurve_id"]) patch["2dcurves"].append(ttc["2dcurve_id"]) patch["wire_ids"].append(wc) orientation = fe.Orientation() patch["orientations"].append(orientation) patches.append(patch) break location = TopLoc_Location() facing = (bt.Triangulation(faces1[fci], location)) if facing != None: tab = facing.Nodes() tri = facing.Triangles() verts = [] for i in range(1, facing.NbNodes() + 1): verts.append(list(tab.Value(i).Coord())) faces = [] for i in range(1, facing.NbTriangles() + 1): index1, index2, index3 = tri.Value(i).Get() faces.append([index1 - 1, index2 - 1, index3 - 1]) os.makedirs(res_path, exist_ok=True) igl.write_triangle_mesh( "%s/%s_%03i_mesh_%04i.obj" % (res_path, fil, occ_cnt, fci), np.array(verts), np.array(faces)) d2_feat["faces"] = faces d2_feat["verts"] = verts else: print("Missing triangulation") continue d2_feats.append(d2_feat) total_surfs += 1 bbox = get_boundingbox(occ_steps[occ_cnt], use_mesh=False) xmin, ymin, zmin, xmax, ymax, zmax = bbox[:6] bbox1 = [ "%.2f" % xmin, "%.2f" % ymin, "%.2f" % zmin, "%.2f" % xmax, "%.2f" % ymax, "%.2f" % zmax, "%.2f" % (xmax - xmin), "%.2f" % (ymax - ymin), "%.2f" % (zmax - zmin) ] stats["#edges"] = total_edges stats["#surfs"] = total_surfs # Fix possible orientation problems if convert: for p in patches: # Check orientation of first curve if len(p["2dcurves"]) >= 2: cur = tr_curves[p["2dcurves"][0]] nxt = tr_curves[p["2dcurves"][1]] c_ori = p["orientations"][0] n_ori = p["orientations"][1] if c_ori == 0: pole0 = np.array(cur["poles"][0]) pole1 = np.array(cur["poles"][-1]) else: pole0 = np.array(cur["poles"][-1]) pole1 = np.array(cur["poles"][0]) if n_ori == 0: pole2 = np.array(nxt["poles"][0]) pole3 = np.array(nxt["poles"][-1]) else: pole2 = np.array(nxt["poles"][-1]) pole3 = np.array(nxt["poles"][0]) d02 = np.abs(pole0 - pole2) d12 = np.abs(pole1 - pole2) d03 = np.abs(pole0 - pole3) d13 = np.abs(pole1 - pole3) amin = np.argmin([d02, d12, d03, d13]) if amin == 0 or amin == 2: # Orientation of first curve incorrect, fix p["orientations"][0] = abs(c_ori - 1) # Fix all orientations for i in range(len(p["2dcurves"]) - 1): cur = tr_curves[p["2dcurves"][i]] nxt = tr_curves[p["2dcurves"][i + 1]] c_ori = p["orientations"][i] n_ori = p["orientations"][i + 1] if c_ori == 0: pole1 = np.array(cur["poles"][-1]) else: pole1 = np.array(cur["poles"][0]) if n_ori == 0: pole2 = np.array(nxt["poles"][0]) pole3 = np.array(nxt["poles"][-1]) else: pole2 = np.array(nxt["poles"][-1]) pole3 = np.array(nxt["poles"][0]) d12 = np.abs(pole1 - pole2) d13 = np.abs(pole1 - pole3) amin = np.min([d12, d13]) if amin == 1: # Incorrect orientation, flip p["orientations"][i + 1] = abs(n_ori - 1) features = { "curves": d1_feats, "surfaces": d2_feats, "trim": tr_curves, "topo": patches, "bbox": bbox1 } os.makedirs(res_path, exist_ok=True) fip = fil + "_features2" with open("%s/%s_%03i.yml" % (res_path, fip, occ_cnt), "w") as fili: yaml.dump(features, fili, indent=2) fip = fil + "_features" with open("%s/%s_%03i.yml" % (res_path, fip, occ_cnt), "w") as fili: features2 = copy.deepcopy(features) for sf in features2["surfaces"]: del sf["faces"] del sf["verts"] yaml.dump(features2, fili, indent=2) # res_path = folder.replace("/step/", "/stat/") # fip = fil + "_stats" # with open("%s/%s_%03i.yml"%(res_path, fip, occ_cnt), "w") as fili: # yaml.dump(stats, fili, indent=2) print("Writing results for %s with %i parts." % (model, len(occ_steps)))
# 3. perform Gaussian-product subdivision # note: igl.loop only handles 3D subdivs -> split the 9D mesh into three 3D ones for _ in range(4): qq1, f = igl.loop(qq1, cm.f) qq2, f = igl.loop(qq2, cm.f) qlin, cm.f = igl.loop(qlin, cm.f) # 4. transform back to 3D cm.v = np.zeros((len(qlin),3)) for i, ql in enumerate(qlin): icov = [qq1[i], [qq1[i][1], qq2[i][0], qq2[i][1]], [qq1[i][2], qq2[i][1], qq2[i][2]]] cm.v[i] = np.linalg.inv(icov) @ ql #------------------------------------------------------------------------------------------ # save meshes to .off file path = os.path.join(root_folder, "data", "tweety-loop.off") igl.write_triangle_mesh(path, lmesh.v, lmesh.f) print("Wrote Loop mesh to", path) path = os.path.join(root_folder, "data", "tweety-gps.off") igl.write_triangle_mesh(path, cm.v, cm.f) print("Wrote GPS mesh to", path)