def test_ambient_occlusion(self): n = igl.per_vertex_normals(self.v2, self.f2) s = igl.ambient_occlusion(self.v2, self.f2, self.v2, n, 2) self.assertEqual(s.dtype, self.v1.dtype) self.assertEqual(len(s.shape), 1) self.assertTrue(s.flags.c_contiguous)
def vertex_normals(verts, faces, n_neighbors_cloud=30): verts_np = toNP(verts) if isinstance(faces, list): is_cloud = faces == [] else: is_cloud = faces.numel() == 0 if is_cloud: # point cloud _, neigh_inds = find_knn(verts, verts, n_neighbors_cloud, omit_diagonal=True, method='cpu_kd') neigh_points = verts_np[neigh_inds, :] neigh_points = neigh_points - verts_np[:, np.newaxis, :] normals = neighborhood_normal(neigh_points) else: # mesh normals = igl.per_vertex_normals(verts_np, toNP(faces)) # if any are NaN, wiggle slightly and recompute bad_normals_mask = np.isnan(normals).any(axis=1, keepdims=True) if bad_normals_mask.any(): bbox = np.amax(verts_np, axis=0) - np.amin(verts_np, axis=0) scale = np.linalg.norm(bbox) * 1e-4 wiggle = (np.random.RandomState(seed=777).rand(*verts.shape) - 0.5) * scale wiggle_verts = verts_np + bad_normals_mask * wiggle normals = igl.per_vertex_normals(wiggle_verts, toNP(faces)) # if still NaN assign random normals (probably means unreferenced verts in mesh) bad_normals_mask = np.isnan(normals).any(axis=1) if bad_normals_mask.any(): normals[bad_normals_mask, :] = ( np.random.RandomState(seed=777).rand(*verts.shape) - 0.5)[bad_normals_mask, :] normals = normals / np.linalg.norm(normals, axis=-1)[:, np.newaxis] normals = torch.from_numpy(normals).to(device=verts.device, dtype=verts.dtype) if torch.any(torch.isnan(normals)): raise ValueError("NaN normals :(") return normals
def getNormals(self, mesh=None): """ Computes the normals for a mesh :param mesh: This should be some class that has mesh.vertices and mesh.faces of Vertex and Face class respectively :return: Puts vectors in self.vectors """ vertices = np.row_stack([vertex.coords for vertex in mesh.vertices]) faces = np.row_stack([face.vertex_ids for face in mesh.faces]) self.vectors = igl.per_vertex_normals(vertices, faces, weighting=1)
def set_parameters(self, V, F): # reset self.reset() # compute property given V and F self.N = igl.per_vertex_normals(V, F) self.L = igl.cotmatrix(V, F) VA = igl.massmatrix(V, F, 0) self.VA = VA.diagonal() # get face adjacency list VF, NI = igl.vertex_triangle_adjacency(F, V.shape[0]) adjFList = construct_adjacency_list(VF, NI) # arap self.K = igl.arap_rhs(V, F, d=3, energy=1) # they are all list since length can be different self.hEList = [None] * V.shape[0] self.WVecList = [None] * V.shape[0] self.dVList = [None] * V.shape[0] for i in range(0, V.shape[0]): adjF = adjFList[i] len_adjF = adjF.shape[0] self.hEList[i] = np.zeros((len_adjF * 3, 2), dtype=int) self.WVecList[i] = np.zeros(len_adjF * 3) self.dVList[i] = np.zeros((3, 3 * len_adjF)) for j in range(0, len_adjF): vIdx = adjF[j] v0 = F[vIdx, 0] v1 = F[vIdx, 1] v2 = F[vIdx, 2] # half edge indices # hE = np.array([[v0, v1], [v1, v2], [v2, v0]]) # self.hEList[i] = hE self.hEList[i][3 * j, 0] = v0 self.hEList[i][3 * j, 1] = v1 self.hEList[i][3 * j + 1, 0] = v1 self.hEList[i][3 * j + 1, 1] = v2 self.hEList[i][3 * j + 2, 0] = v2 self.hEList[i][3 * j + 2, 1] = v0 # weight vec self.WVecList[i][3 * j] = self.L[v0, v1] self.WVecList[i][3 * j + 1] = self.L[v1, v2] self.WVecList[i][3 * j + 2] = self.L[v2, v0] V_hE0 = V[self.hEList[i][:, 0], :] V_hE1 = V[self.hEList[i][:, 1], :] self.dVList[i] = np.transpose(V_hE1 - V_hE0) self.WVecList[i] = np.diag(self.WVecList[i]) # other var numV = V.shape[0] self.zAll = np.random.rand(3, numV) * 2.0 - 1.0 self.uAll = np.random.rand(3, numV) * 2.0 - 1.0 self.zAll = np.zeros((3, numV)) self.uAll = np.zeros((3, numV)) self.rhoAll = np.full(numV, self.param.rhoInit)
def select_views(self, views_e, F): """ Select a subset views that contain the most information about F. The pipeline is as follows: TSDF --> Mesh --> Simplify Mesh --> Normals --> Sample points weighted by curvature --> Compute scores for each view --> Greedily select best subset of views Args: F : 3D Fusion volume views_e : list of views expressed in end-effector space. """ # Project views from end-effector space f_T_e = np.linalg.inv(self.tsdf_vol_params.e_T_f) views_f = views_e_to_f(views_e, f_T_e) print("TSDF to mesh...") verts, faces = self.tsdf_to_mesh(F) print("Smooth data...") # success, verts, faces = self.simplify_mesh(verts, faces) v_star = smooth_data(verts, faces, self.params.w_smooth) print("Normals...") normals = igl.per_vertex_normals(v_star, faces) print("Sample vertices ...") verts_choice, _ = self.sample_verts_from_mesh(v_star, faces) # NOTE sample from original vertices verts = verts[verts_choice, :] normals = normals[verts_choice, :] num_views = len(views_e) scores = np.ndarray((num_views, self.params.n_sample_pts)) print("Compute scores ...") for i in range(num_views): scores[i, :] = self.compute_geometry_values( views_f[i], verts, normals) visibility = self.compute_visibility_values(views_f[i], verts, F) scores[i, :] *= visibility print("Greedy best subset ...") best_views, _ = self.greedy_best_subset(scores) return best_views
def ScalpReconstruct(V, nilr, cpc_inners=99, fibonacci_samples = 9801 * 0): if fibonacci_samples == 0: R, t = calibrate_nilr(nilr[0], nilr[1], nilr[2], nilr[3]) nilr = np.dot(nilr-t, R.T) mriV = np.dot(V-t, R.T) if len(V) > 1000: mriV = spherical_clip(np.dot(V-t, R.T), np.linalg.norm(nilr[-1])*0.8) # np.savetxt("runtime/mriV.obj", np.dot(mriV, R)+T, fmt="v %f %f %f") mV, mF, mCtrl, nilr_idx = MinimalSurface(mriV, nilr[0], nilr[1], nilr[2], nilr[3]) Nzidx, Izidx, Alidx, Aridx = nilr_idx cpc_V, cpc_CPC, cpc_F = CPCSampling.generate_cpcmesh(mV, mF, Nzidx, Izidx, Alidx, Aridx, n=cpc_inners) cpc_N = igl.per_vertex_normals(cpc_V, cpc_F, igl.PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE) T, B, N = TNBFrame.TNB_frame(cpc_V, cpc_N, cpc_inners) cpc_V, T, B, N = np.dot(cpc_V, R)+t, np.dot(T, R), np.dot(B, R), np.dot(N, R) return cpc_V, cpc_F, cpc_CPC, T, B, N else: _V, _F, _V_CPC, _T, _N, _B = ScalpReconstruct(V, nilr, cpc_inners=699) index, fb_CPC, fb_F = CPCSampling.load_fibonacci(fibonacci_samples) fb_V, fb_T, fb_N, fb_B = _V[index], _T[index], _N[index], _B[index] return fb_V, fb_F, fb_CPC, fb_T, fb_B, fb_N
return True elif key == ord('3'): viewer.data.set_normals(N_corners) return True return False # Load a mesh in OFF format igl.readOFF("../../tutorial/shared/fandisk.off", V, F); # Compute per-face normals N_faces = igl.eigen.MatrixXd() igl.per_face_normals(V,F,N_faces) # Compute per-vertex normals N_vertices = igl.eigen.MatrixXd() igl.per_vertex_normals(V,F,igl.PER_VERTEX_NORMALS_WEIGHTING_TYPE_AREA,N_vertices) # Compute per-corner normals, |dihedral angle| > 20 degrees --> crease N_corners = igl.eigen.MatrixXd() igl.per_corner_normals(V,F,20,N_corners) # Plot the mesh viewer = igl.viewer.Viewer() viewer.callback_key_pressed = key_pressed viewer.core.show_lines = False viewer.data.set_mesh(V, F) viewer.data.set_normals(N_faces) print("Press '1' for per-face normals.") print("Press '2' for per-vertex normals.") print("Press '3' for per-corner normals.") viewer.launch()
centroid += 0.5*dblA[i,0]/area*BC.row(i) U -= centroid.replicate(U.rows(),1) # Normalize to unit surface area (important for numerics) U = U / math.sqrt(area) else: return False # Send new positions, update normals, recenter viewer.data.set_vertices(U) viewer.data.compute_normals() viewer.core.align_camera_center(U,F) return True # Use original normals as pseudo-colors N = igl.eigen.MatrixXd() igl.per_vertex_normals(V,F,N) C = N.rowwiseNormalized()*0.5+0.5; # Initialize smoothing with base mesh U = V viewer.data.set_mesh(U, F) viewer.data.set_colors(C) viewer.callback_key_pressed = key_pressed print("Press [space] to smooth.") print("Press [r] to reset.") viewer.launch()
def draw_cpc_wireframe(p: pv.Plotter, V: np.ndarray, n=10): for i in np.linspace(0, 100, n + 1, dtype=np.int)[1:-1] - 1: p.add_mesh(polyline_from_points(V[i, :]), color=wireframe_color) for j in np.linspace(0, 100, n + 1, dtype=np.int)[1:-1] - 1: p.add_mesh(polyline_from_points(V[:, j]), color=wireframe_color) def draw_orient_definition(p: pv.Plotter, V: np.ndarray, N: np.ndarray): pass if __name__ == "__main__": V, F = igl.read_triangle_mesh("./data/12034_CPC.obj") N = igl.per_vertex_normals(V, F, igl.PER_VERTEX_NORMALS_WEIGHTING_TYPE_AREA) T, N, B = CPCScalpRecons.TNBFrame.TNB_frame(V, N, cpc_ratio=99) scalp = pv.PolyData() scalp.points, scalp.faces = V, np.column_stack( [np.full((len(F), 1), 3), F]) p = pv.Plotter() # p.add_text("Scalp TBN Frame", font_size=18) scalp_ = p.add_mesh(scalp, ambient=0.06, diffuse=0.75, opacity=1, color=(1., 0.8, 0.7)) #, scalars="yz_dot", cmap="jet") draw_cpc_wireframe(p, V[:9801, :].reshape((99, 99, -1)))
def test_per_vertex_normals(self): n = igl.per_vertex_normals(self.v1, self.f1, 0) self.assertEqual(n.shape, (self.v1.shape[0], 3)) self.assertEqual(n.dtype, self.v1.dtype) self.assertTrue(n.flags.c_contiguous)
def get_flipped_normals(vertices, faces): normals = igl.per_vertex_normals(vertices, faces) dot = np.sum(normals * vertices, axis=1) return sum(dot < 0) / vertices.shape[0]
Rvec = np.cross(lr_N, N, axis=1) Rvec /= np.linalg.norm(Rvec, axis=1)[:, np.newaxis] for i in range(len(lr_N)): projection_len = np.clip(np.dot(N[i], lr_N[i]), 0, 1) R = transform.Rotation.from_rotvec(Rvec[i] * np.arccos(projection_len)) lr_T[i], lr_N[i], lr_B[i] = map(R.apply, (lr_T[i], lr_N[i], lr_B[i])) return lr_T, lr_N, lr_B if __name__ == "__main__": import igl cpc_V, cpc_F = igl.read_triangle_mesh("./apps/data/12034_CPC.obj") surface_N = igl.per_vertex_normals( cpc_V, cpc_F, igl.PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE) T, N, B = TNB_frame(cpc_V, surface_N, cpc_ratio=99) import pyvista as pv scalp = pv.PolyData() scalp.points = cpc_V scalp.faces = np.column_stack([np.full((len(cpc_F), 1), 3), cpc_F]) p = pv.Plotter() p.add_text("Scalp CPC-TNBFrame", font_size=18) p.add_mesh(scalp, ambient=0.06, diffuse=0.75, opacity=1.0, color=(1., 0.8, 0.7)) #, scalars="yz_dot", cmap="jet")
def test_per_vertex_normals(self): n = igl.per_vertex_normals(self.v1, self.f1, 0) self.assertEqual(n.shape, (self.v1.shape[0], 3)) self.assertEqual(n.dtype, self.v1.dtype)