def smooth(self,
               vertices: np.ndarray,
               faces: np.ndarray,
               diffusion: ChunkGrid[float],
               original_mesh: Meshes,
               max_iteration=50):
        assert max_iteration > -1
        change = True
        iteration = 0
        loss_mesh = original_mesh.clone()
        smooth_verts = loss_mesh.verts_packed().clone()
        neighbors = self.compute_neighbors(vertices, faces)
        neighbor_len = torch.IntTensor(
            [len(neighbors[i]) for i in range(len(vertices))])
        neighbor_valences = torch.FloatTensor([
            sum([1 / neighbor_len[n] for n in neighbors[i]])
            for i in range(len(vertices))
        ])
        d = 1 + 1 / neighbor_len * neighbor_valences

        difference_max = torch.as_tensor(diffusion.get_values(vertices) + 1)

        while change and iteration < max_iteration:
            iteration += 1
            change = False

            for i in range(2):
                with torch.no_grad():
                    L = loss_mesh.laplacian_packed()
                loss = L.mm(loss_mesh.verts_packed())
                if i == 0:
                    loss_mesh = Meshes([loss], loss_mesh.faces_list())

            # new_vals = smooth_verts - (1/d).unsqueeze(1) * loss
            # difference = torch.sqrt(torch.sum(torch.pow(original_mesh.verts_packed() - new_vals, 2), dim=1))

            new_val = smooth_verts - (loss.T * (1 / d)).T
            differences = torch.linalg.norm(original_mesh.verts_packed() -
                                            new_val,
                                            dim=1)
            cond = differences < difference_max
            if torch.any(cond):
                smooth_verts[cond] = new_val[cond]
                change = True

            # for i, v in enumerate(vertices):
            #     new_val = smooth_verts[i] - (1 / d[i] * loss[i])
            #     difference = torch.dist(original_mesh.verts_packed()[i], new_val)
            #     if difference < difference_max[i]:
            #         smooth_verts[i] = new_val
            #         change = True

            loss_mesh = Meshes([smooth_verts], original_mesh.faces_list())
        return smooth_verts
Exemple #2
0
    def save(
        self,
        data: Meshes,
        path: Union[str, Path],
        path_manager: PathManager,
        binary: Optional[bool],
        decimal_places: Optional[int] = None,
        **kwargs,
    ) -> bool:
        if not endswith(path, self.known_suffixes):
            return False

        verts = data.verts_list()[0]
        faces = data.faces_list()[0]

        if data.has_verts_normals():
            verts_normals = data.verts_normals_list()[0]
        else:
            verts_normals = None

        if isinstance(data.textures, TexturesVertex):
            mesh_verts_colors = data.textures.verts_features_list()[0]
            n_colors = mesh_verts_colors.shape[1]
            if n_colors == 3:
                verts_colors = mesh_verts_colors
            else:
                warnings.warn(
                    f"Texture will not be saved as it has {n_colors} colors, not 3."
                )
                verts_colors = None
        else:
            verts_colors = None

        with _open_file(path, path_manager, "wb") as f:
            _save_ply(
                f=f,
                verts=verts,
                faces=faces,
                verts_colors=verts_colors,
                verts_normals=verts_normals,
                ascii=binary is False,
                decimal_places=decimal_places,
            )
        return True
def taubin_smoothing(meshes: Meshes,
                     lambd: float = 0.53,
                     mu: float = -0.53,
                     num_iter: int = 10) -> Meshes:
    """
    Taubin smoothing [1] is an iterative smoothing operator for meshes.
    At each iteration
        verts := (1 - λ) * verts + λ * L * verts
        verts := (1 - μ) * verts + μ * L * verts

    This function returns a new mesh with smoothed vertices.
    Args:
        meshes: Meshes input to be smoothed
        lambd, mu: float parameters for Taubin smoothing,
            lambd > 0, mu < 0
        num_iter: number of iterations to execute smoothing
    Returns:
        mesh: Smoothed input Meshes

    [1] Curve and Surface Smoothing without Shrinkage,
        Gabriel Taubin, ICCV 1997
    """
    verts = meshes.verts_packed()  # V x 3
    edges = meshes.edges_packed()  # E x 3

    for _ in range(num_iter):
        L = norm_laplacian(verts, edges)
        total_weight = torch.sparse.sum(L, dim=1).to_dense().view(-1, 1)
        verts = (1 - lambd) * verts + lambd * torch.mm(L, verts) / total_weight

        # pyre-ignore
        L = norm_laplacian(verts, edges)
        total_weight = torch.sparse.sum(L, dim=1).to_dense().view(-1, 1)
        verts = (1 - mu) * verts + mu * torch.mm(L, verts) / total_weight

    verts_list = struct_utils.packed_to_list(
        verts,
        meshes.num_verts_per_mesh().tolist())
    mesh = Meshes(verts=list(verts_list), faces=meshes.faces_list())
    return mesh
Exemple #4
0
    def save(
        self,
        data: Meshes,
        path: Union[str, Path],
        path_manager: PathManager,
        binary: Optional[bool],
        decimal_places: Optional[int] = None,
        **kwargs,
    ) -> bool:
        if not endswith(path, self.known_suffixes):
            return False

        verts = data.verts_list()[0]
        faces = data.faces_list()[0]
        save_obj(
            f=path,
            verts=verts,
            faces=faces,
            decimal_places=decimal_places,
            path_manager=path_manager,
        )
        return True
Exemple #5
0
    def save(
        self,
        data: Meshes,
        path: PathOrStr,
        path_manager: PathManager,
        binary: Optional[bool],
        decimal_places: Optional[int] = None,
        **kwargs,
    ) -> bool:
        if not endswith(path, self.known_suffixes):
            return False

        verts = data.verts_list()[0]
        faces = data.faces_list()[0]
        if isinstance(data.textures, TexturesVertex):
            [verts_colors] = data.textures.verts_features_list()
        else:
            verts_colors = None

        faces_colors = None
        if isinstance(data.textures, TexturesAtlas):
            [atlas] = data.textures.atlas_list()
            F, R, _, D = atlas.shape
            if R == 1:
                faces_colors = atlas[:, 0, 0, :]

        _save_off(
            file=path,
            verts=verts,
            faces=faces,
            verts_colors=verts_colors,
            faces_colors=faces_colors,
            decimal_places=decimal_places,
            path_manager=path_manager,
        )
        return True
Exemple #6
0
    def save(
        self,
        data: Meshes,
        path: Union[str, Path],
        path_manager: PathManager,
        binary: Optional[bool],
        decimal_places: Optional[int] = None,
        **kwargs,
    ) -> bool:
        if not endswith(path, self.known_suffixes):
            return False

        # TODO: normals are not saved. We only want to save them if they already exist.
        verts = data.verts_list()[0]
        faces = data.faces_list()[0]
        save_ply(
            f=path,
            verts=verts,
            faces=faces,
            ascii=binary is False,
            decimal_places=decimal_places,
            path_manager=path_manager,
        )
        return True