yfeng95/PRNet

Ridges and grooves in 3D Mesh

varunszapp opened this issue · 1 comments

Hi @YadiraF! Thank you for your great work!
I was using your code to generate some 3D faces from images. On viewing the mesh in Meshlab/Blender, I observed there are some ridges and grooves on the surface of the face. That is, the surface is not smooth, but instead has ups and downs. On zooming in, I noticed that this is because the vertices were selected in such a manner. The triangles were forming the ups and downs. Please see the images:
Screenshot from 2019-12-21 12-01-53
Screenshot from 2019-12-21 12-02-05
Screenshot from 2019-12-21 12-03-04

How do I make the surface smoother and eliminate these ups and downs?

I was able to eliminate these ridges by replacing 3D coordinate of each vertex by an average coordinate of its neighbors (which can be inferred from the triangles). This operation can be repeated several times. For example, the following code does the smoothing 5 times and returns a rather clean mesh:

import numpy as np


def get_avg_neighbors(points, tri):
    n_points = points.shape[0]
    avg_neighbors = np.zeros((n_points, 3), dtype=np.float32)    # averaged coordinate over neighbors of each vertex
    n_neighbors = np.zeros(n_points, dtype=np.float32)
    
    np.add.at(avg_neighbors, tri[:, 0], points[tri[:, 1]])
    np.add.at(avg_neighbors, tri[:, 0], points[tri[:, 2]])
    np.add.at(avg_neighbors, tri[:, 1], points[tri[:, 0]])
    np.add.at(avg_neighbors, tri[:, 1], points[tri[:, 2]])
    np.add.at(avg_neighbors, tri[:, 2], points[tri[:, 0]])
    np.add.at(avg_neighbors, tri[:, 2], points[tri[:, 1]])
    
    np.add.at(n_neighbors, tri[:, 0], 2)
    np.add.at(n_neighbors, tri[:, 1], 2)
    np.add.at(n_neighbors, tri[:, 2], 2)

    avg_neighbors = avg_neighbors / np.where(n_neighbors > 0, n_neighbors, 1)[:, np.newaxis]
    return avg_neighbors


if __name__ == '__main__':
    import open3d as o3d    # for mesh reading and saving

    input_obj_name = '0001.obj'    # input .obj name (returned by PRNet)
    output_obj_name = '0001_smooth.obj'    # smoothened .obj name
    n_rep = 5    # number of smoothing repetitions
	
    mesh = o3d.io.read_triangle_mesh(input_obj_name)
    pts = np.asarray(mesh.vertices)
    tri = np.asarray(mesh.triangles)

    mesh2 = mesh
    pts2 = np.copy(pts)

    for i in range(n_rep):
        pts2 = get_avg_neighbors(pts2, tri)
    mesh2.vertices = o3d.utility.Vector3dVector(pts2)

    o3d.io.write_triangle_mesh(output_obj_name, mesh2)

Before:

PRNet mesh orig

After:

PRNet mesh smoothened