smoothing - Mesh smoothing

Smooth mesh with various filters

Smoothing in libigl

We implement Laplacian and Taubin smoothing for vertex positions using libigl’s Laplacian operator.


get_uniform_laplacian


def get_uniform_laplacian(
    tris, normalize:bool=True
):

Get uniform Laplacian (purely connectivity-based) as a sparse matrix.

If normalize, the diagonal = -1. Else, the diagonal equals the number of neighbors.


smooth_laplacian


def smooth_laplacian(
    mesh:ObjMesh, # Initial mesh.
    lamb:float=0.5, # Filter strength. Higher = more smoothing.
    n_iter:int=10, # Filter iterations
    method:str='explicit', # Can use "explicit" (fast, simple) or "implicit" (slow, more accurate) methods.
    boundary:str='fixed', # Whether to allow mesh boundaries to move
)->ObjMesh: # Smoothed mesh.

Smooth mesh vertex positions using Laplacian filter.

Assumes mesh is triangular.

# Load a test mesh
mesh_registered = tcmesh.ObjMesh.read_obj(f"datasets/wrapping_example/Drosophila_reference_registered.obj")
Warning: readOBJ() ignored non-comment line 1:
  o Drosophila_reference_preregistered
mesh_smoothed = smooth_laplacian(mesh_registered, lamb=0.5, n_iter=10, method="explicit")
mesh_smoothed.write_obj("datasets/wrapping_example/Drosophila_reference_smoothed_uniform_igl.obj")
boundary_vertices = igl.boundary_facets(mesh_registered.texture_tris)[0][:, 0]
np.allclose(mesh_smoothed.texture_vertices[boundary_vertices],
            mesh_registered.texture_vertices[boundary_vertices])
True

smooth_taubin


def smooth_taubin(
    mesh:ObjMesh, # Initial mesh.
    lamb:float=0.5, # Filter strength. Higher = more smoothing.
    nu:float=0.53, # Counteract shrinkage. Higher = more dilation.
    n_iter:int=10, # Filter iterations
)->ObjMesh: # Smoothed mesh.

Smooth using Taubin filter (like Laplacian, but avoids shrinkage).

Assumes mesh is triangular. See “Improved Laplacian Smoothing of Noisy Surface Meshes” J. Vollmer, R. Mencl, and H. Muller.

mesh_smoothed_taubin = smooth_taubin(mesh_registered, lamb=0.5, nu=0.53, n_iter=10)
mesh_smoothed_taubin.write_obj("datasets/wrapping_example/Drosophila_reference_smoothed_taubin_igl.obj")

Texture smoothing

Sometimes, UV maps can become very deformed, or even display self-intersection. Smoothing can fix this.


smooth_laplacian_texture


def smooth_laplacian_texture(
    mesh:ObjMesh, # Initial mesh.
    lamb:float=0.5, # Filter strength. Higher = more smoothing.
    n_iter:int=10, # Filter iterations
    boundary:str='fixed', # Whether to allow UV "island" boundary to move
)->ObjMesh: # Smoothed mesh.

Smooth mesh texture positions using Laplacian filter.

This function is very helpful in fixing UV maps with flipped triangles, as detected by igl.flipped_triangles. Assumes mesh is triangular.

mesh_texture_smoothed = smooth_laplacian_texture(mesh_registered, lamb=0.5, n_iter=10,)

On-surface smoothing

Smooth, but project at each step so the mesh vertices are back on the surface. This is very useful to smooth out surface-surface maps.


smooth_laplacian_on_surface


def smooth_laplacian_on_surface(
    mesh:ObjMesh, # Initial mesh.
    n_iter:int=10, # Number of iterations at each step
    lamb:float=0.5, # Filter strength. Higher = more smoothing.
    n_iter_laplace:int=10, # Laplace filter iterations. If reprojection messes up your mesh, decrease this number.
    boundary:str='fixed', # Whether to allow mesh boundaries to move
)->ObjMesh: # Smoothed mesh.

Smooth mesh vertex positions using Laplacian filter and project vertices back to the original surface.

Alternates between Laplacian smoothing and projecting back to the original surface. Uses explicit method for Laplacian smoothing

mesh_test = tcmesh.ObjMesh.read_obj(f"datasets/movie_example/meshes_wrapped_reverse/mesh_01_wrapped_reverse.obj")
mesh_smoothed = smooth_laplacian_on_surface(mesh_test, n_iter=20, lamb=0.5, n_iter_laplace=5)
mesh_smoothed.write_obj(f"datasets/movie_example/on_surface_smooth.obj")