File size: 3,102 Bytes
29a229f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import numpy as np
import torch
from torch.nn import functional as F
# from psbody.mesh.visibility import visibility_compute

def uv_to_xyz_and_normals(verts, f, fmap, bmap, ftov):
    vn = estimate_vertex_normals(verts, f, ftov)
    pixels_to_set = torch.nonzero(fmap+1)
    x_to_set = pixels_to_set[:,0]
    y_to_set = pixels_to_set[:,1]
    b_coords = bmap[x_to_set, y_to_set, :]
    f_coords = fmap[x_to_set, y_to_set]
    v_ids = f[f_coords]
    points = (b_coords[:,0,None]*verts[:,v_ids[:,0]]
             + b_coords[:,1,None]*verts[:,v_ids[:,1]]
             + b_coords[:,2,None]*verts[:,v_ids[:,2]])
    normals = (b_coords[:,0,None]*vn[:,v_ids[:,0]]
             + b_coords[:,1,None]*vn[:,v_ids[:,1]]
             + b_coords[:,2,None]*vn[:,v_ids[:,2]])
    return points, normals, vn, f_coords

def estimate_vertex_normals(v, f, ftov):
    face_normals = TriNormalsScaled(v, f)
    non_scaled_normals = torch.einsum('ij,bjk->bik', ftov, face_normals)
    norms = torch.sum(non_scaled_normals ** 2.0, 2) ** 0.5
    norms[norms == 0] = 1.0
    return torch.div(non_scaled_normals, norms[:,:,None])

def TriNormalsScaled(v, f):
    return torch.cross(_edges_for(v, f, 1, 0), _edges_for(v, f, 2, 0))

def _edges_for(v, f, cplus, cminus):
    return v[:,f[:,cplus]] - v[:,f[:,cminus]]

def psbody_get_face_visibility(v, n, f, cams, normal_threshold=0.5):
    bn, nverts, _ = v.shape
    nfaces, _ = f.shape
    vis_f = np.zeros([bn, nfaces], dtype='float32')
    for i in range(bn):
        vis, n_dot_cam = visibility_compute(v=v[i], n=n[i], f=f, cams=cams)
        vis_v = (vis == 1) & (n_dot_cam > normal_threshold)
        vis_f[i] = np.all(vis_v[0,f],1)
    return vis_f

def compute_uvsampler(vt, ft, tex_size=6):
    """
    For this mesh, pre-computes the UV coordinates for
    F x T x T points.
    Returns F x T x T x 2
    """
    uv = obj2nmr_uvmap(ft, vt, tex_size=tex_size)
    uv = uv.reshape(-1, tex_size, tex_size, 2)
    return uv

def obj2nmr_uvmap(ft, vt, tex_size=6):
    """
    Converts obj uv_map to NMR uv_map (F x T x T x 2),
    where tex_size (T) is the sample rate on each face.
    """
    # This is F x 3 x 2
    uv_map_for_verts = vt[ft]

    # obj's y coordinate is [1-0], but image is [0-1]
    uv_map_for_verts[:, :, 1] = 1 - uv_map_for_verts[:, :, 1]

    # range [0, 1] -> [-1, 1]
    uv_map_for_verts = (2 * uv_map_for_verts) - 1

    alpha = np.arange(tex_size, dtype=np.float) / (tex_size - 1)
    beta = np.arange(tex_size, dtype=np.float) / (tex_size - 1)
    import itertools
    # Barycentric coordinate values
    coords = np.stack([p for p in itertools.product(*[alpha, beta])])

    # Compute alpha, beta (this is the same order as NMR)
    v2 = uv_map_for_verts[:, 2]
    v0v2 = uv_map_for_verts[:, 0] - uv_map_for_verts[:, 2]
    v1v2 = uv_map_for_verts[:, 1] - uv_map_for_verts[:, 2]
    # Interpolate the vertex uv values: F x 2 x T*2
    uv_map = np.dstack([v0v2, v1v2]).dot(coords.T) + v2.reshape(-1, 2, 1)

    # F x T*2 x 2  -> F x T x T x 2
    uv_map = np.transpose(uv_map, (0, 2, 1)).reshape(-1, tex_size, tex_size, 2)

    return uv_map