def signedDistance(f, box=None, parallel=False): if box is not None: adapt_box_to(f, box) else: create_box(f) ncpus = mp.cpu_count() if parallel else 1 lib_exe.execute( lib_exe.mshdist + "-ncpu " + str(ncpus) + " -noscale box.1.o.mesh " + f + " > /dev/null 2>&1") if os.path.exists("box.mesh"): os.remove("box.mesh") if os.path.exists("box.1.mesh"): os.remove("box.1.mesh")
def create_box(f): mesh = lib_msh.Mesh(f) cube = lib_msh.Mesh(cube=[mesh.xmin, mesh.xmax, mesh.ymin, mesh.ymax, mesh.zmin, mesh.zmax]) cube.verts[:,:3] -= mesh.center cube.verts[:,:3] *= 1.25 cube.verts[:,:3] += mesh.center cube.write("box.mesh") lib_exe.execute( lib_exe.tetgen + "-pgANEF box.mesh > /dev/null 2>&1" ) hausd = 0.02 * np.max(mesh.dims) lib_exe.execute( lib_exe.mmg3d + "box.1.mesh -hausd %f -hmax %f > /dev/null 2>&1" % (hausd, hausd) )
def intersects(f): lib_exe.execute(lib_exe.tetgen + "-d %s > log.txt" % (f)) res = True with open("log.txt", "r") as f: lines = f.readlines() if "No faces are intersecting" in "".join(lines): res = False else: res = True os.remove("log.txt") return res
def morph(f): IN = os.path.join(directories["signed"], f) OUT = os.path.join(directories["morphed"], f) TMP = templates["morphing_skull"] if "Skull" in f else templates[ "morphing_face"] REFS = [10, 2, 0] if "Skull" in f else [10, 2, 3] with tempfile.TemporaryDirectory() as tmp: os.chdir(tmp) lib_exe.execute( lib_exe.python_cmd("morph.py") + "-t %s -s %s -o %s --icotris %d --icotets %d --fixtris %d -n %d" % (TMP, IN, OUT, REFS[0], REFS[1], REFS[2], 250))
def adapt_box_to(f, box, maxNb=20000): shutil.copyfile(box,"box.mesh") cube = lib_msh.Mesh("box.mesh") mesh = lib_msh.Mesh(f) step = 1 if len(mesh.verts)<maxNb else int(len(mesh.verts)/maxNb)+1 dists, _ = nearest_neighbor(cube.verts[:,:3], mesh.verts[::step,:3]) cube.scalars = np.array(dists) ABS = np.absolute(cube.scalars) mini, maxi = 0.005*np.max(cube.dims), 0.5*np.max(cube.dims) cube.scalars = mini + (maxi-mini) * (ABS - np.min(ABS)) / (np.max(ABS) - np.min(ABS)) cube.write("box.1.mesh") cube.writeSol("box.1.sol") lib_exe.execute(lib_exe.mmg3d + "box.1.mesh -hgrad 1.5 > /dev/null 2>&1")
def to_mesh(f, output): #Convert a stl to mesh with meshlabserver meshlabScript = os.path.join(os.path.dirname(os.path.abspath(__file__)), "meshlab_scripts", "cleanSTL.mlx") tmp = f.replace(".stl", ".obj") lib_exe.execute(lib_exe.meshlabserver + "-i %s -o %s -s %s > /dev/null 2>&1" % (f, tmp, meshlabScript)) f = tmp #Convert a .obj to .mesh with open(f, "r") as _f: LINES = _f.readlines() mesh = lib_msh.Mesh() mesh.verts = np.array([[float(x) for x in l.split()[1:]] for l in LINES if l[0] == "v"]) mesh.tris = np.array([[int(x) - 1 for x in l.split()[1:]] for l in LINES if l[0] == "f"]) mesh.verts = np.insert(mesh.verts, 3, 0, axis=1) mesh.tris = np.insert(mesh.tris, 3, 0, axis=1) mesh.computeBBox() mesh.write(output)
if not os.path.splitext(args.input)[1] == ".mesh": print(args.input + " is not a .mesh file") sys.exit() if not os.path.splitext(args.output)[1] == ".mesh": print("Output file must be in the .mesh format") sys.exit() if intersects(args.input): print(args.input + " has intersecting facets") sys.exit() mesh = lib_msh.Mesh(args.input) ico = lib_msh.Mesh(ico=[args.center, args.radius]) ico.tris[:, -1] = 10 mesh.fondre(ico) mesh.write("out.mesh") lib_exe.execute(lib_exe.tetgen + "-pgANEYF out.mesh") lib_exe.execute(lib_exe.mmg3d + "out.1.mesh -nosurf -o " + args.output) os.remove(args.output.replace(".mesh", ".sol")) #Make the same references in args.output than in out.mesh def distance(a, b): return ((a[0] - b[0])**2 + (a[1] - b[1])**2 + (a[2] - b[2])**2)**0.5 final = lib_msh.Mesh(args.output) for i, t in enumerate(final.tris): vert1 = final.verts[t[0]] if distance(vert1, [0.5, 0.5, 0.65]) < 0.12: final.tris[i, -1] = 10 final.write(args.output) os.remove("out.mesh")
"--directory", type=str, help="directory containing the data to test", required=True) parser.add_argument("-m", "--medit", action="store_true", help="visualize each result with medit") args = parser.parse_args() args.directory = os.path.abspath(args.directory) # 1 - Carve a .mesh object print("Testing carve.py") IN = os.path.join(args.directory, "buddha.mesh") OUT = os.path.join(args.directory, "buddha.carved.mesh") lib_exe.execute( lib_exe.python_cmd("carve.py") + "-i %s -o %s -r %d" % (IN, OUT, 31)) if args.medit: lib_exe.execute(lib_exe.medit + "%s" % (OUT)) os.remove(OUT) # 2 - convert .obj and .stl objects to .mesh print("Testing convert.py") IN1 = os.path.join(args.directory, "buddha.obj") OUT1 = os.path.join(args.directory, "buddha1.mesh") lib_exe.execute( lib_exe.python_cmd("convert.py") + "-i %s -o %s" % (IN1, OUT1)) IN2 = os.path.join(args.directory, "buddha.stl") OUT2 = os.path.join(args.directory, "buddha2.mesh") lib_exe.execute( lib_exe.python_cmd("convert.py") + "-i %s -o %s" % (IN2, OUT2)) if args.medit: lib_exe.execute(lib_exe.medit + "%s %s" % (OUT1, OUT2)) buddha1 = lib_msh.Mesh(OUT1)
parser = argparse.ArgumentParser(description="Warps a mesh with a baloon-like object") parser.add_argument("-i", "--input", type=str, help="Mesh to wrap", required=True) parser.add_argument("-o", "--output", type=str, help="Output mesh", required=True) parser.add_argument("-t", "--template", type=str, help="Template to wrap", required=True) args = parser.parse_args(sys.argv[1:]) #checks args.template = os.path.abspath(args.template) if args.template else None args.input = os.path.abspath(args.input) args.output = os.path.abspath(args.output) #Copy the template to warp shutil.copyfile(args.template, "sphere.mesh") #Warp lib_exe.execute(lib_paths.wrapping + " %s -p -nit %d -load %f > /dev/null 2>&1" % (args.input, 150, 40) ) #Clean the mesh and extract the surface final = None try: number_max=-1 for f in [x for x in os.listdir(".") if "sphere.d." in x and ".mesh" in x]: number = int(f.split(".")[2]) if number>number_max: final = f except: final = "sphere.d.mesh" warped = lib_msh.Mesh(final) warped.tris = warped.tris[warped.tris[:,-1] != 2] warped.tets = np.array([])
os.path.join(args.templates, d + ".mesh")) return args, templates if __name__ == "__main__": #Get the templates and arguments args, templates = init() NAME = "reconstruction" # 1 - If needed, convert the .obj or .stl objects to .mesh for i, f in enumerate(args.input): if f.endswith(".obj") or f.endswith(".stl"): lib_exe.execute( lib_exe.python_cmd("convert.py") + "-i %s -o %s" % (f, f.replace(f[-3:], "mesh"))) args.input[i] = f.replace(f[-3:], "mesh") # 2 - If needed, merge everything together if len(args.input) > 1: lib_exe.execute( lib_exe.python_cmd("merge.py") + "-i %s -o %s" % (" ".join(args.input), NAME + ".mesh")) else: shutil.copyfile(args.input[0], NAME + ".mesh") # 3 - Transform to an object between 0.1 and 0.9 mesh = lib_msh.Mesh(NAME + ".mesh") S = 0.8 / np.max(mesh.dims) C = mesh.center
parser.add_argument("-d", "--icotris", type=int, help="Icosphere triangles reference", required=True) parser.add_argument("-e", "--icotets", type=int, help="Icosphere tetrahedra reference", required=True) parser.add_argument("-b", "--fixtris", type=int, help="Fixed triangles reference", required=True) args = parser.parse_args(sys.argv[1:]) #checks args.template = os.path.abspath(args.template) args.signed = os.path.abspath(args.signed) args.output = os.path.abspath(args.output) # dref: Fixed surface inside the template (number + ref) # elref: Elements inside the fixed surface # bref: Follower elements shutil.copyfile(args.template, "template.mesh") cmd = lib_exe.morphing + " %s %s -nit %d -dref 1 %d -elref 1 %d -bref 1 %d > /dev/null 2>&1" % (args.signed, "template.mesh", args.iterations, args.icotris, args.icotets, args.fixtris) lib_exe.execute(cmd) #Clean the mesh mesh = lib_msh.Mesh(args.signed[:-5] + ".1.mesh") mesh.readSol( args.signed[:-5] + ".1.depl.sol") mesh.tets = np.array([]) mesh.tris = mesh.tris[mesh.tris[:,-1]!=10] mesh.discardUnused() mesh.write(args.output) mesh.writeSol(args.output.replace("mesh", "sol")) #Remove the temporary files os.remove(args.signed[:-5] + ".1.mesh") os.remove(args.signed[:-5] + ".1.depl.sol")
help="Hausdorff distance", default=0.01) parser.add_argument("-r", "--reference", type=int, help="New surface reference", default=0) parser.add_argument("-l", "--levelset", type=float, help="Levelset value", default=0) args = parser.parse_args(sys.argv[1:]) #checks args.input = os.path.abspath(args.input) args.output = os.path.abspath(args.output) lib_exe.execute(lib_exe.mmg3d + "%s -ls %f -hausd %f -o tmp.mesh" % (args.input, args.levelset, args.distance)) mesh = lib_msh.Mesh("tmp.mesh") mesh.tets = np.array([]) mesh.tris = mesh.tris[mesh.tris[:, -1] == 10] mesh.discardUnused() mesh.tris[:, -1] = args.reference mesh.write(args.output) os.remove("tmp.mesh") os.remove("tmp.sol")
lib_exe.execute( lib_exe.mmg3d + "box.1.mesh -hausd %f -hmax %f > /dev/null 2>&1" % (hausd, hausd) ) def signedDistance(f, box=None, parallel=False): if box is not None: adapt_box_to(f, box) else: create_box(f) ncpus = mp.cpu_count() if parallel else 1 lib_exe.execute( lib_exe.mshdist + "-ncpu " + str(ncpus) + " -noscale box.1.o.mesh " + f + " > /dev/null 2>&1") if os.path.exists("box.mesh"): os.remove("box.mesh") if os.path.exists("box.1.mesh"): os.remove("box.1.mesh") if __name__ == "__main__": #arguments parser = argparse.ArgumentParser(description="Computes the signed distance to a .mesh file") parser.add_argument("-i", "--input", type=str, help="Input mesh", required=True) parser.add_argument("-o", "--output", type=str, help="Output mesh", required=True) parser.add_argument("-v", "--volume", type=str, help="Volume mesh to adapt") parser.add_argument("-p", action="store_true", help="Run in parallel", default=False) args = parser.parse_args(sys.argv[1:]) #checks args.input = os.path.abspath(args.input) args.output = os.path.abspath(args.output) args.volume = os.path.abspath(args.volume) if args.volume is not None else None signedDistance(args.input, box=args.volume, parallel=args.p) lib_exe.execute("mv box.1.o.mesh " + args.output) lib_exe.execute("mv box.1.o.sol " + args.output.replace(".mesh",".sol"))
#Merge the meshes and run tetgen exterior, interior = lib_msh.Mesh(args.exterior), lib_msh.Mesh( args.interior) #exterior.tris = exterior.tris[exterior.tris[:,-1]!=2] exterior.tris[:, -1] = 2 exterior.discardUnused() interior.tris[:, -1] = 1 interior.fondre(exterior) interior.write("mask.mesh") if intersects("mask.mesh"): print("mask is self-intersecting") sys.exit(3) #Run tetgen lib_exe.execute(lib_exe.tetgen + "-pgaANEFY mask.mesh") for f in os.listdir("."): if 'mask' in f and ".mesh" not in f and ".py" not in f: os.remove(f) lib_exe.execute( lib_exe.mmg3d + "-nosurf -nr -hgrad 1.75 mask.1.mesh -out mask.1.o.mesh" ) #Because the number of triangles is not good, therefore tetrahedra re not read!! os.remove("mask.1.o.sol") os.remove("mask.mesh") os.remove("mask.1.mesh") #Remove the inside of the mask mesh = lib_msh.Mesh("mask.1.o.mesh") ext_point_ind = np.argmin(mesh.verts[:, :3], axis=0)[0]
sys.exit() if not os.path.splitext(args.output)[1] == ".mesh": print("Output file must be in the .mesh format") sys.exit() if args.interiorIsovalue < args.exteriorIsovalue: print("The inner shell must be closer than the outer shell") sys.exit() if args.interiorIsovalue < 5 or args.exteriorIsovalue < 5: print("The shell must be closer than maxDim/5") sys.exit() args.input = os.path.abspath(args.input) args.output = os.path.abspath(args.output) #Carve the input mesh lib_exe.execute( lib_exe.python_cmd("carve.py") + "-i %s -o %s -r %d" % (args.input, args.output.replace(".mesh", ".carved.mesh"), 31)) #Create a box and mesh it to compute the signed distance on mesh = lib_msh.Mesh(args.input) c = np.max(mesh.dims) / 8 cube = lib_msh.Mesh(cube=[ mesh.xmin - c, mesh.xmax + c, mesh.ymin - c, mesh.ymax + c, mesh.zmin - c, mesh.zmax + c ]) cube.write("box.mesh") lib_exe.execute(lib_exe.tetgen + "-pgANEF %s" % ("box.mesh")) lib_exe.execute( lib_exe.mmg3d + "%s -hausd %f -hmax %f" % ("box.1.mesh", np.max(mesh.dims) / 50, np.max(mesh.dims) / 25))