def _conductord_winding_squared(self, pts, distance): """Compute and returned the signed distances between particles and mesh. The distances are computed via the functions of winding number and squared distance provided by PyMesh. PyMesh's distance_to_mesh function needs to compute BVH engine every time it is called. The PyMesh library has been modified so distance_to_mesh can take BVH engine as an argument to avoid this time consuming step called every time. Args: pts: numpy.ndarray of Nx3 storing the positions of particles for query. distance: A list to store the computed distance Return: distance: A list. Points outside conductor have positive values, and those inside have negative values. """ winding_number = pymesh.compute_winding_number(self.surface, pts) winding_number[np.abs(winding_number) < self._winding_fuzz] = 0.0 pts_inside = winding_number > 0. try: distance[:], _, _ = pymesh.distance_to_mesh(self.surface, pts, bvh=self._bvh) except TypeError: distance[:], _, _ = pymesh.distance_to_mesh(self.surface, pts) distance[:] = np.sqrt(distance) distance[pts_inside] *= -1
def pick_points(mesh, mesh_pymesh, object_name): """ Function for inteactively picking points """ print("") print("1) Pick points using [shift + left click]") print(" Press [shift + right click] to undo point picking") print("2) After picking points, press q for close the window") # sample points on the mesh surface to be picked from pcd = mesh.sample_points_poisson_disk(10000) pcd.estimate_normals() # create interactive visualizer vis = o3dv.VisualizerWithEditing() vis.create_window(object_name) # vis.add_geometry(mesh) vis.add_geometry(pcd) vis.run() # user picks points vis.destroy_window() print("") pt_idxs = vis.get_picked_points() # get the picked points and normals of the faces closest to those points pts = np.asarray(pcd.points)[pt_idxs] _, face_idxs, _ = pymesh.distance_to_mesh(mesh_pymesh, pts) normals = np.asarray(mesh.triangle_normals)[face_idxs] # normals = np.asarray(pcd.normals)[pt_idxs] if normals.ndim == 1: normals = normals[np.newaxis, :] return pts, normals
def test_boundary_pts_geogram(self): mesh = generate_box_mesh(np.array([0, 0, 0]), np.array([1, 1, 1])) pts = np.array([[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]]) if "geogram" in BVH.available_engines: sq_dist, face_idx, closest_pts = distance_to_mesh( mesh, pts, "geogram") self.assert_array_equal(sq_dist, np.zeros(2))
def test_boundary_pts_cgal(self): mesh = generate_box_mesh( np.array([0, 0, 0]), np.array([1, 1, 1])); pts = np.array([ [0.0, 0.0, 0.0], [1.0, 1.0, 1.0] ]); sq_dist, face_idx, closest_pts = distance_to_mesh(mesh, pts, "cgal"); self.assert_array_equal(sq_dist, np.zeros(2));
def process_marker_locations(mesh_o3d, mesh_pymesh, markers): """ snaps marker locations to object surface and computes surface normals at marker locations """ dist2, face_idxs, nbrs = pymesh.distance_to_mesh(mesh_pymesh, markers) normals = np.asarray(mesh_o3d.triangle_normals)[face_idxs] # snap to surface vs = markers - nbrs vs /= np.linalg.norm(vs, axis=1, keepdims=True) markers -= np.sqrt(dist2)[:, np.newaxis] * vs return markers, normals
def calculate_rmse_and_density(ground_truth_mesh, cropped_pointcloud, depth_scale, camera_angle): # need to get the reference mesh and the pointcloud in the same units squared_distances, _, _ = pymesh.distance_to_mesh( ground_truth_mesh.reference_mesh, np.asarray(cropped_pointcloud.points) / depth_scale) rmse = np.sqrt(np.sum(squared_distances) / len(squared_distances)) distance_thresh = 2 # mm of distance threshold = distance_thresh**2 num_valid_pixels = len(squared_distances[squared_distances < threshold]) valid_pattern_surface_area = ground_truth_mesh.get_pattern_surface_area( camera_angle=camera_angle) return rmse, num_valid_pixels / valid_pattern_surface_area
def preprocess_to_find_kp_uv( self, kp3d, faces, verts, verts_sphere, ): mesh = pymesh.form_mesh(verts, faces) dist, face_ind, closest_pts = pymesh.distance_to_mesh(mesh, kp3d) dist_to_verts = np.square(kp3d[:, None, :] - verts[None, :, :]).sum(-1) closest_pts = closest_pts / np.linalg.norm( closest_pts, axis=1, keepdims=1) min_inds = np.argmin(dist_to_verts, axis=1) kp_verts_sphere = verts_sphere[min_inds] kp_uv = geom_utils.convert_3d_to_uv_coordinates(closest_pts) return kp_uv
def measure_distances_on_surface_non_registered_pymesh( source_obj_file, destination_obj_file, measure_on_source_vertices=ALL_POINTS): import pymesh #source_mesh = np.array(read_mesh(source_obj_file)) destination_mesh = pymesh.load_mesh(destination_obj_file) #for index_source in range(len(source_mesh)): # if index in list of given vertices or if list empty measure all distances # if (measure_on_source_vertices==ALL_POINTS or index_source in measure_on_source_vertices): if measure_on_source_vertices == ALL_POINTS: measure_on_source_vertices = range(destination_mesh.num_vertices) source_points = get_vertex_positions(source_obj_file, measure_on_source_vertices) squared_distances, face_indices, closest_points = pymesh.distance_to_mesh( destination_mesh, source_points) distances = [math.sqrt(d2) for d2 in squared_distances] return distances
def voxelized_pointcloud_boundary_sampling(path, sigmas, res, inp_points, sample_points): try: out_voxelization_file = path + '/voxelized_point_cloud_{}res_{}points.npz'.format( res, inp_points) off_path = path + '/mesh.off' mesh = trimesh.load(off_path) py_mesh = pymesh.load_mesh(off_path) # ===================== # Voxelized point cloud # ===================== if not os.path.exists(out_voxelization_file): bb_min = -0.5 bb_max = 0.5 point_cloud = mesh.sample(inp_points) # Grid Points used for computing occupancies grid_points = create_grid_points_from_bounds( bb_min, bb_max, args.res) # KDTree creation for fast querying nearest neighbour to points on the point cloud kdtree = KDTree(grid_points) _, idx = kdtree.query(point_cloud) occupancies = np.zeros(len(grid_points), dtype=np.int8) occupancies[idx] = 1 compressed_occupancies = np.packbits(occupancies) np.savez(out_voxelization_file, point_cloud=point_cloud, compressed_occupancies=compressed_occupancies, bb_min=bb_min, bb_max=bb_max, res=res) print('Finished Voxelized point cloud {}'.format(path)) # ================== # Boundary Sampling # ================== for sigma in sigmas: out_sampling_file = path + '/pymesh_boundary_{}_samples.npz'.format( sigma) if not os.path.exists(out_sampling_file): points = mesh.sample(sample_points) if sigma == 0: boundary_points = points else: boundary_points = points + sigma * np.random.randn( sample_points, 3) # Transform the boundary points to grid coordinates grid_coords = boundary_points.copy() grid_coords[:, 0], grid_coords[:, 2] = boundary_points[:, 2], boundary_points[:, 0] grid_coords = 2 * grid_coords # distance field calculation if sigma == 0: df = np.zeros(boundary_points.shape[0]) else: df = np.sqrt( pymesh.distance_to_mesh(py_mesh, boundary_points)[0]) np.savez(out_sampling_file, points=boundary_points, df=df, grid_coords=grid_coords) print( 'Finished boundary sampling {}'.format(out_sampling_file)) except Exception as err: print('Error with {}: {}'.format(path, traceback.format_exc()))
def contains_point(self, x, epsilon=0.001): (dists, faces, pts) = pymesh.distance_to_mesh(self.component_mesh, [x]) if dists == 0.0: return True return False
def map_shape_to_ico_sphere(mapping, uv_map_size=(1001, 1001), transform=None): vshape = mapping['vshape'].transpose(1,0).astype(np.float) vsphere = mapping['vsphere'].transpose(1,0).astype(np.float) faces = mapping['face'].transpose(1,0).astype(np.int) if transform is not None: vshape = np.dot(vshape, transform.transpose()) mesh_sphere = pymesh.form_mesh(vsphere, faces) mesh_shape = pymesh.form_mesh(vshape, faces) pymesh.meshio.save_mesh('test_shape.obj', mesh_shape) pymesh.meshio.save_mesh('test_sphere.obj', mesh_sphere) # # pdb.set_trace() map_H = uv_map_size[1] map_W = uv_map_size[0] x = np.arange(0, 1 + 1.0/(map_W-1), 1.0/(map_W-1)) y = np.arange(0, 1 + 1.0/(map_H-1), 1.0/(map_H-1)) xx, yy = np.meshgrid(x, y, indexing='xy') map_uv = np.stack([xx, yy], axis=2) map_uv = map_uv.reshape(-1, 2) map_3d = geom_utils.convert_uv_to_3d_coordinates(map_uv) # fig = plt.figure() # ax = fig.add_subplot(111, projection='3d') dist , face_inds, closest_points = pymesh.distance_to_mesh(mesh_sphere, map_3d) # face_ind = face_inds.reshape(map_H, map_W,) barycentric_coordinates = convert_to_barycentric_coordinates(faces, vsphere, face_inds, map_3d) # ax.scatter(map_3d[:,0],map_3d[:,1], map_3d[:,2] ,'r') # ax.set_xlabel('X Label') # ax.set_ylabel('Y Label') # ax.set_zlabel('Z Label') uv_cords = geom_utils.convert_3d_to_uv_coordinates(vsphere) # plt.savefig('3d_uv_points.png') face_verts_ind = faces[face_inds] uv_vertsA = uv_cords[face_verts_ind[:, 0]] uv_vertsB = uv_cords[face_verts_ind[:, 1]] uv_vertsC = uv_cords[face_verts_ind[:, 2]] new_uv = uv_vertsA*barycentric_coordinates[:,None,0] + uv_vertsB*barycentric_coordinates[:,None, 0] + uv_vertsC*barycentric_coordinates[:,None, 2] dist = dist.reshape(map_H, map_W) barycentric_coordinates = barycentric_coordinates.reshape(map_H, map_W, 3) face_inds = face_inds.reshape(map_H, map_W) map_3d = map_3d.reshape(map_H, map_W, -1) closest_points = closest_points.reshape(map_H, map_W, -1) map_uv = map_uv.reshape(map_H, map_W,2) stuff = {} stuff['map_3d'] = map_3d stuff['sphere_verts'] = vsphere stuff['verts'] = vshape stuff['faces'] = faces stuff['uv_verts'] = uv_cords stuff['uv_map'] = map_uv stuff['bary_cord'] = barycentric_coordinates stuff['face_inds'] = face_inds return stuff
def map_cmr_mean_to_uv_image(uv_map_size=(1001, 1001)): cub_cache_dir = '/home/nileshk/CMR/birds3d/cachedir/cub/' anno_sfm_path = osp.join(cub_cache_dir, 'sfm', 'anno_train.mat') anno_sfm = sio.loadmat(anno_sfm_path, struct_as_record=False, squeeze_me=True) sfm_mean_shape = (np.transpose(anno_sfm['S']), anno_sfm['conv_tri']-1) cmr_mean_shape_path = osp.join('/home/nileshk/CorrespNet/icn/cachedir/cub/', 'uv', 'cmr_mean.mat') cmr_mean = sio.loadmat(cmr_mean_shape_path) verts_proj, faces = cmr_mean['verts'].squeeze(), cmr_mean['faces'].squeeze() bird_mesh = pymesh.form_mesh(verts_proj, faces) # pymesh.meshio.save_mesh('cmr_bird.obj', bird_mesh) verts = verts_sphere = verts_proj/np.linalg.norm(verts_proj, axis=1, keepdims=True) mesh_sphere = pymesh.form_mesh(verts_sphere, faces) # pymesh.meshio.save_mesh('cmr_sphere.obj', sphere_mesh) # verts, faces = create_sphere(3) # verts_proj = project_verts_on_mesh(verts, sfm_mean_shape[0], sfm_mean_shape[1]) # mesh_sphere = pymesh.form_mesh(verts, faces) # mesh_shape = pymesh.form_mesh(verts_proj, faces) # pymesh.meshio.save_mesh('test_sphere.obj', mesh_sphere) # uv = np.zeros((uv_map_size[1], uv_map_size[0], 2)) # u_step = 1.0/1920 # v_step = 1.0/960 map_H = uv_map_size[1] map_W = uv_map_size[0] x = np.arange(0, 1 + 1.0/(map_W-1), 1.0/(map_W-1)) y = np.arange(0, 1 + 1.0/(map_H-1), 1.0/(map_H-1)) xx, yy = np.meshgrid(x, y, indexing='xy') map_uv = np.stack([xx, yy], axis=2) map_uv = map_uv.reshape(-1, 2) map_3d = geom_utils.convert_uv_to_3d_coordinates(map_uv) # # fig = plt.figure() # # ax = fig.add_subplot(111, projection='3d') dist , face_inds, closest_points = pymesh.distance_to_mesh(mesh_sphere, map_3d) # # face_ind = face_inds.reshape(map_H, map_W,) barycentric_coordinates = convert_to_barycentric_coordinates(faces, verts, face_inds, map_3d) # # ax.scatter(map_3d[:,0],map_3d[:,1], map_3d[:,2] ,'r') # # ax.set_xlabel('X Label') # # ax.set_ylabel('Y Label') # # ax.set_zlabel('Z Label') uv_cords = geom_utils.convert_3d_to_uv_coordinates(verts) # # plt.savefig('3d_uv_points.png') # face_verts_ind = faces[face_inds] # uv_vertsA = uv_cords[face_verts_ind[:, 0]] # uv_vertsB = uv_cords[face_verts_ind[:, 1]] # uv_vertsC = uv_cords[face_verts_ind[:, 2]] # new_uv = uv_vertsA*barycentric_coordinates[:,None,0] + uv_vertsB*barycentric_coordinates[:,None, 0] + uv_vertsC*barycentric_coordinates[:,None, 2] # dist = dist.reshape(map_H, map_W) # barycentric_coordinates = barycentric_coordinates.reshape(map_H, map_W, 3) # map_3d = map_3d.reshape(map_H, map_W, -1) # closest_points = closest_points.reshape(map_H, map_W, -1) face_inds = face_inds.reshape(map_H, map_W) map_uv = map_uv.reshape(map_H, map_W,2) stuff = {} stuff['sphere_verts'] = verts stuff['verts'] = verts_proj stuff['faces'] = faces stuff['uv_verts'] = uv_cords stuff['uv_map'] = map_uv # stuff['bary_cord'] = barycentric_coordinates stuff['face_inds'] = face_inds stuff['S'] = sfm_mean_shape[0] stuff['conv_tri'] = sfm_mean_shape[1] return stuff
def map_shape_to_ico_sphere(uv_map_size=(1001, 1001)): p3d_cache_dir = '/home/nileshk/CorrespNet/icn/cachedir/p3d/' anno_sfm_path = osp.join(p3d_cache_dir, 'sfm', '{}_train.mat'.format(p3d_class)) anno_sfm = sio.loadmat(anno_sfm_path, struct_as_record=False, squeeze_me=True) sfm_mean_shape = (np.transpose(anno_sfm['S']), anno_sfm['conv_tri']-1) verts, faces = create_sphere(3) verts_proj = project_verts_on_mesh(verts, sfm_mean_shape[0], sfm_mean_shape[1]) mesh_sphere = pymesh.form_mesh(verts, faces) mesh_shape = pymesh.form_mesh(verts_proj, faces) # pymesh.meshio.save_mesh('test_sphere.obj', mesh_sphere) # pymesh.meshio.save_mesh('{}.obj'.format(p3d_class), mesh_shape) # uv = np.zeros((uv_map_size[1], uv_map_size[0], 2)) # u_step = 1.0/1920 # v_step = 1.0/960 map_H = uv_map_size[1] map_W = uv_map_size[0] x = np.arange(0, 1 + 1.0/(map_W-1), 1.0/(map_W-1)) y = np.arange(0, 1 + 1.0/(map_H-1), 1.0/(map_H-1)) xx, yy = np.meshgrid(x, y, indexing='xy') map_uv = np.stack([xx, yy], axis=2) map_uv = map_uv.reshape(-1, 2) map_3d = geom_utils.convert_uv_to_3d_coordinates(map_uv) # fig = plt.figure() # ax = fig.add_subplot(111, projection='3d') dist , face_inds, closest_points = pymesh.distance_to_mesh(mesh_sphere, map_3d) # face_ind = face_inds.reshape(map_H, map_W,) barycentric_coordinates = convert_to_barycentric_coordinates(faces, verts, face_inds, map_3d) # ax.scatter(map_3d[:,0],map_3d[:,1], map_3d[:,2] ,'r') # ax.set_xlabel('X Label') # ax.set_ylabel('Y Label') # ax.set_zlabel('Z Label') uv_cords = geom_utils.convert_3d_to_uv_coordinates(verts) # plt.savefig('3d_uv_points.png') face_verts_ind = faces[face_inds] uv_vertsA = uv_cords[face_verts_ind[:, 0]] uv_vertsB = uv_cords[face_verts_ind[:, 1]] uv_vertsC = uv_cords[face_verts_ind[:, 2]] new_uv = uv_vertsA*barycentric_coordinates[:,None,0] + uv_vertsB*barycentric_coordinates[:,None, 0] + uv_vertsC*barycentric_coordinates[:,None, 2] dist = dist.reshape(map_H, map_W) barycentric_coordinates = barycentric_coordinates.reshape(map_H, map_W, 3) face_inds = face_inds.reshape(map_H, map_W) map_3d = map_3d.reshape(map_H, map_W, -1) closest_points = closest_points.reshape(map_H, map_W, -1) map_uv = map_uv.reshape(map_H, map_W,2) stuff = {} stuff['sphere_verts'] = verts stuff['verts'] = verts_proj # stuff['verts'] = verts ## Deliberate change, restore later stuff['faces'] = faces stuff['uv_verts'] = uv_cords stuff['uv_map'] = map_uv stuff['bary_cord'] = barycentric_coordinates stuff['face_inds'] = face_inds stuff['S'] = sfm_mean_shape[0] stuff['conv_tri'] = sfm_mean_shape[1] return stuff
def compute_vertices_to_mesh_distances(groundtruth_vertices, grundtruth_landmark_points, predicted_mesh_vertices, predicted_mesh_faces, predicted_mesh_landmark_points, out_filename): """ This script computes the reconstruction error between an input mesh and a ground truth mesh. :param groundtruth_vertices: An n x 3 numpy array of vertices from a ground truth scan. :param grundtruth_landmark_points: A 7 x 3 list with annotations of the ground truth scan. :param predicted_mesh_vertices: An m x 3 numpy array of vertices from a predicted mesh. :param predicted_mesh_faces: A k x 3 numpy array of vertex indices composing the predicted mesh. :param predicted_mesh_landmark_points: A 7 x 3 list containing the annotated 3D point locations in the predicted mesh. :param out_filename: Filename to write the resulting distances to (e.g. F1008_A_distances.txt). :return: A list of distances (errors), one for each vertex in the groundtruth mesh, and the associated vertex index in the ground truth scan. The grundtruth_landmark_points and predicted_mesh_landmark_points have to contain points in the following order: (1) right eye outer corner, (2) right eye inner corner, (3) left eye inner corner, (4) left eye outer corner, (5) nose bottom, (6) right mouth corner, (7) left mouth corner. """ # Do procrustes based on the 7 points: # The ground truth scan is in mm, so by aligning the prediction to the ground truth, we get meaningful units. d, Z, tform = procrustes(np.array(grundtruth_landmark_points), np.array(predicted_mesh_landmark_points), scaling=True, reflection='best') # Use tform to transform all vertices in predicted_mesh_vertices to the ground truth reference space: predicted_mesh_vertices_aligned = [] for v in predicted_mesh_vertices: s = tform['scale'] R = tform['rotation'] t = tform['translation'] transformed_vertex = s * np.dot(v, R) + t predicted_mesh_vertices_aligned.append(transformed_vertex) # Compute the mask: A circular area around the center of the face. Take the nose-bottom and go upwards a bit: nose_bottom = np.array(grundtruth_landmark_points[4]) nose_bridge = (np.array(grundtruth_landmark_points[1]) + np.array( grundtruth_landmark_points[2])) / 2 # between the inner eye corners face_centre = nose_bottom + 0.3 * (nose_bridge - nose_bottom) # Compute the radius for the face mask: outer_eye_dist = np.linalg.norm( np.array(grundtruth_landmark_points[0]) - np.array(grundtruth_landmark_points[3])) nose_dist = np.linalg.norm(nose_bridge - nose_bottom) mask_radius = 1.2 * (outer_eye_dist + nose_dist) / 2 # Find all the vertex indices in the ground truth scan that lie within the mask area: vertex_indices_mask = [ ] # vertex indices in the source mesh (the ground truth scan) points_on_groundtruth_scan_to_measure_from = [] for vertex_idx, vertex in enumerate(groundtruth_vertices): dist = np.linalg.norm( vertex - face_centre ) # We use Euclidean distance for the mask area for now. if dist <= mask_radius: vertex_indices_mask.append(vertex_idx) points_on_groundtruth_scan_to_measure_from.append(vertex) assert len(vertex_indices_mask) == len( points_on_groundtruth_scan_to_measure_from) # For each vertex on the ground truth mesh, find the closest point on the surface of the predicted mesh: predicted_mesh_pymesh = pymesh.meshio.form_mesh( np.array(predicted_mesh_vertices_aligned), predicted_mesh_faces) squared_distances, face_indices, closest_points = pymesh.distance_to_mesh( predicted_mesh_pymesh, points_on_groundtruth_scan_to_measure_from) distances = [sqrt(d2) for d2 in squared_distances] # Save the distances to a file, alongside with each vertex id of the ground truth scan that the distance has been computed for: with open(out_filename, 'w') as csv_file: wr = csv.writer(csv_file, delimiter=' ') vertex_indices_with_distances = [ [v_idx, v] for v_idx, v in zip(vertex_indices_mask, distances) ] wr.writerows(vertex_indices_with_distances)