yfeng95 / PRNet

Joint 3D Face Reconstruction and Dense Alignment with Position Map Regression Network (ECCV 2018)
http://openaccess.thecvf.com/content_ECCV_2018/papers/Yao_Feng_Joint_3D_Face_ECCV_2018_paper.pdf
MIT License
4.96k stars 944 forks source link

Ridges and grooves in 3D Mesh #166

Open varunszapp opened 4 years ago

varunszapp commented 4 years ago

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?

seva100 commented 4 years ago

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