def get_per_point_visibility_mask(pointclouds: Pointclouds, fragments: NamedTuple) -> Pointclouds: """ compute per-point visibility (0/1), append value to pointclouds features Returns: boolean mask for packed tensors (P_total,) """ P_total = pointclouds.num_points_per_cloud().sum().item() P_max = pointclouds.num_points_per_cloud().max().item() try: mask = fragments.occupancy.bool() # float except: mask = fragments.idx[..., 0] >= 0 # bool pts_visibility = torch.full((P_total, ), False, dtype=torch.bool, device=pointclouds.device) # all rendered points (indices in packed points) visible_idx = fragments.idx[mask].unique().long().view(-1) visible_idx = visible_idx[visible_idx >= 0] pts_visibility[visible_idx] = True return pts_visibility
def point_mesh_face_distance(meshes: Meshes, pcls: Pointclouds): """ Computes the distance between a pointcloud and a mesh within a batch. Given a pair `(mesh, pcl)` in the batch, we define the distance to be the sum of two distances, namely `point_face(mesh, pcl) + face_point(mesh, pcl)` `point_face(mesh, pcl)`: Computes the squared distance of each point p in pcl to the closest triangular face in mesh and averages across all points in pcl `face_point(mesh, pcl)`: Computes the squared distance of each triangular face in mesh to the closest point in pcl and averages across all faces in mesh. The above distance functions are applied for all `(mesh, pcl)` pairs in the batch and then averaged across the batch. Args: meshes: A Meshes data structure containing N meshes pcls: A Pointclouds data structure containing N pointclouds Returns: loss: The `point_face(mesh, pcl) + face_point(mesh, pcl)` distance between all `(mesh, pcl)` in a batch averaged across the batch. """ if len(meshes) != len(pcls): raise ValueError("meshes and pointclouds must be equal sized batches") N = len(meshes) # packed representation for pointclouds points = pcls.points_packed() # (P, 3) points_first_idx = pcls.cloud_to_packed_first_idx() max_points = pcls.num_points_per_cloud().max().item() # packed representation for faces verts_packed = meshes.verts_packed() faces_packed = meshes.faces_packed() tris = verts_packed[faces_packed] # (T, 3, 3) tris_first_idx = meshes.mesh_to_faces_packed_first_idx() max_tris = meshes.num_faces_per_mesh().max().item() # point to face distance: shape (P,) point_to_face = point_face_distance(points, points_first_idx, tris, tris_first_idx, max_points) # weight each example by the inverse of number of points in the example point_to_cloud_idx = pcls.packed_to_cloud_idx() # (sum(P_i),) num_points_per_cloud = pcls.num_points_per_cloud() # (N,) weights_p = num_points_per_cloud.gather(0, point_to_cloud_idx) weights_p = 1.0 / weights_p.float() point_to_face = point_to_face * weights_p point_dist = point_to_face.sum() / N # face to point distance: shape (T,) face_to_point = face_point_distance(points, points_first_idx, tris, tris_first_idx, max_tris) # weight each example by the inverse of number of faces in the example tri_to_mesh_idx = meshes.faces_packed_to_mesh_idx() # (sum(T_n),) num_tris_per_mesh = meshes.num_faces_per_mesh() # (N, ) weights_t = num_tris_per_mesh.gather(0, tri_to_mesh_idx) weights_t = 1.0 / weights_t.float() face_to_point = face_to_point * weights_t face_dist = face_to_point.sum() / N return point_dist + face_dist
def point_mesh_edge_distance(meshes: Meshes, pcls: Pointclouds): """ Computes the distance between a pointcloud and a mesh within a batch. Given a pair `(mesh, pcl)` in the batch, we define the distance to be the sum of two distances, namely `point_edge(mesh, pcl) + edge_point(mesh, pcl)` `point_edge(mesh, pcl)`: Computes the squared distance of each point p in pcl to the closest edge segment in mesh and averages across all points in pcl `edge_point(mesh, pcl)`: Computes the squared distance of each edge segment in mesh to the closest point in pcl and averages across all edges in mesh. The above distance functions are applied for all `(mesh, pcl)` pairs in the batch and then averaged across the batch. Args: meshes: A Meshes data structure containing N meshes pcls: A Pointclouds data structure containing N pointclouds Returns: loss: The `point_edge(mesh, pcl) + edge_point(mesh, pcl)` distance between all `(mesh, pcl)` in a batch averaged across the batch. """ if len(meshes) != len(pcls): raise ValueError("meshes and pointclouds must be equal sized batches") N = len(meshes) # packed representation for pointclouds points = pcls.points_packed() # (P, 3) points_first_idx = pcls.cloud_to_packed_first_idx() max_points = pcls.num_points_per_cloud().max().item() # packed representation for edges verts_packed = meshes.verts_packed() edges_packed = meshes.edges_packed() segms = verts_packed[edges_packed] # (S, 2, 3) segms_first_idx = meshes.mesh_to_edges_packed_first_idx() max_segms = meshes.num_edges_per_mesh().max().item() # point to edge distance: shape (P,) point_to_edge = point_edge_distance(points, points_first_idx, segms, segms_first_idx, max_points) # weight each example by the inverse of number of points in the example point_to_cloud_idx = pcls.packed_to_cloud_idx() # (sum(P_i), ) num_points_per_cloud = pcls.num_points_per_cloud() # (N,) weights_p = num_points_per_cloud.gather(0, point_to_cloud_idx) weights_p = 1.0 / weights_p.float() point_to_edge = point_to_edge * weights_p point_dist = point_to_edge.sum() / N # edge to edge distance: shape (S,) edge_to_point = edge_point_distance(points, points_first_idx, segms, segms_first_idx, max_segms) # weight each example by the inverse of number of edges in the example segm_to_mesh_idx = meshes.edges_packed_to_mesh_idx() # (sum(S_n),) num_segms_per_mesh = meshes.num_edges_per_mesh() # (N,) weights_s = num_segms_per_mesh.gather(0, segm_to_mesh_idx) weights_s = 1.0 / weights_s.float() edge_to_point = edge_to_point * weights_s edge_dist = edge_to_point.sum() / N return point_dist + edge_dist
def _add_pointcloud_trace( fig: go.Figure, pointclouds: Pointclouds, trace_name: str, subplot_idx: int, ncols: int, max_points_per_pointcloud: int, marker_size: int, ): """ Adds a trace rendering a Pointclouds object to the passed in figure, with a given name and in a specific subplot. Args: fig: plotly figure to add the trace within. pointclouds: Pointclouds object to render. It can be batched. trace_name: name to label the trace with. subplot_idx: identifies the subplot, with 0 being the top left. ncols: the number of sublpots per row. max_points_per_pointcloud: the number of points to render, which are randomly sampled. marker_size: the size of the rendered points """ pointclouds = pointclouds.detach().cpu() verts = pointclouds.points_packed() features = pointclouds.features_packed() indices = None if pointclouds.num_points_per_cloud().max() > max_points_per_pointcloud: start_index = 0 index_list = [] for num_points in pointclouds.num_points_per_cloud(): if num_points > max_points_per_pointcloud: indices_cloud = np.random.choice(num_points, max_points_per_pointcloud, replace=False) index_list.append(start_index + indices_cloud) else: index_list.append(start_index + np.arange(num_points)) start_index += num_points indices = np.concatenate(index_list) verts = verts[indices] color = None if features is not None: if indices is not None: # Only select features if we selected vertices above features = features[indices] if features.shape[1] == 4: # rgba template = "rgb(%d, %d, %d, %f)" rgb = (features[:, :3].clamp(0.0, 1.0) * 255).int() color = [ template % (*rgb_, a_) for rgb_, a_ in zip(rgb, features[:, 3]) ] if features.shape[1] == 3: template = "rgb(%d, %d, %d)" rgb = (features.clamp(0.0, 1.0) * 255).int() color = [template % (r, g, b) for r, g, b in rgb] row = subplot_idx // ncols + 1 col = subplot_idx % ncols + 1 fig.add_trace( go.Scatter3d( # pyre-ignore[16] x=verts[:, 0], y=verts[:, 1], z=verts[:, 2], marker={ "color": color, "size": marker_size }, mode="markers", name=trace_name, ), row=row, col=col, ) # Access the current subplot's scene configuration plot_scene = "scene" + str(subplot_idx + 1) current_layout = fig["layout"][plot_scene] # update the bounds of the axes for the current trace verts_center = verts.mean(0) max_expand = (verts.max(0)[0] - verts.min(0)[0]).max() _update_axes_bounds(verts_center, max_expand, current_layout)
def cleanup_eval_depth( point_cloud: Pointclouds, camera: CamerasBase, depth: torch.Tensor, mask: torch.Tensor, sigma: float = 0.01, image=None, ): ba, _, H, W = depth.shape pcl = point_cloud.points_padded() n_pts = point_cloud.num_points_per_cloud() pcl_mask = ( torch.arange(pcl.shape[1], dtype=torch.int64, device=pcl.device)[None] < n_pts[:, None] ).type_as(pcl) pcl_proj = camera.transform_points(pcl, eps=1e-2)[..., :-1] pcl_depth = camera.get_world_to_view_transform().transform_points(pcl)[..., -1] depth_and_idx = torch.cat( ( depth, torch.arange(H * W).view(1, 1, H, W).expand(ba, 1, H, W).type_as(depth), ), dim=1, ) depth_and_idx_sampled = Fu.grid_sample( depth_and_idx, -pcl_proj[:, None], mode="nearest" )[:, :, 0].view(ba, 2, -1) depth_sampled, idx_sampled = depth_and_idx_sampled.split([1, 1], dim=1) df = (depth_sampled[:, 0] - pcl_depth).abs() # the threshold is a sigma-multiple of the standard deviation of the depth mu = wmean(depth.view(ba, -1, 1), mask.view(ba, -1)).view(ba, 1) std = ( wmean((depth.view(ba, -1) - mu).view(ba, -1, 1) ** 2, mask.view(ba, -1)) .clamp(1e-4) .sqrt() .view(ba, -1) ) good_df_thr = std * sigma good_depth = (df <= good_df_thr).float() * pcl_mask perc_kept = good_depth.sum(dim=1) / pcl_mask.sum(dim=1).clamp(1) # print(f'Kept {100.0 * perc_kept.mean():1.3f} % points') good_depth_raster = torch.zeros_like(depth).view(ba, -1) # pyre-ignore[16]: scatter_add_ good_depth_raster.scatter_add_(1, torch.round(idx_sampled[:, 0]).long(), good_depth) good_depth_mask = (good_depth_raster.view(ba, 1, H, W) > 0).float() # if float(torch.rand(1)) > 0.95: # depth_ok = depth * good_depth_mask # # visualize # visdom_env = 'depth_cleanup_dbg' # from visdom import Visdom # # from tools.vis_utils import make_depth_image # from pytorch3d.vis.plotly_vis import plot_scene # viz = Visdom() # show_pcls = { # 'pointclouds': point_cloud, # } # for d, nm in zip( # (depth, depth_ok), # ('pointclouds_unproj', 'pointclouds_unproj_ok'), # ): # pointclouds_unproj = get_rgbd_point_cloud( # camera, image, d, # ) # if int(pointclouds_unproj.num_points_per_cloud()) > 0: # show_pcls[nm] = pointclouds_unproj # scene_dict = {'1': { # **show_pcls, # 'cameras': camera, # }} # scene = plot_scene( # scene_dict, # pointcloud_max_points=5000, # pointcloud_marker_size=1.5, # camera_scale=1.0, # ) # viz.plotlyplot(scene, env=visdom_env, win='scene') # # depth_image_ok = make_depth_image(depths_ok, masks) # # viz.images(depth_image_ok, env=visdom_env, win='depth_ok') # # depth_image = make_depth_image(depths, masks) # # viz.images(depth_image, env=visdom_env, win='depth') # # # viz.images(rgb_rendered, env=visdom_env, win='images_render') # # viz.images(images, env=visdom_env, win='images') # import pdb; pdb.set_trace() return good_depth_mask