# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. import math import numpy as np import sys import os from render.camera import Camera from render.color_render import ColorRender import trimesh import cv2 import os import argparse from termcolor import colored width = 512 height = 512 def normalize_v3(arr): ''' Normalize a numpy array of 3 component vectors shape=(n,3) ''' lens = np.sqrt(arr[:, 0]**2 + arr[:, 1]**2 + arr[:, 2]**2) eps = 0.00000001 lens[lens < eps] = eps arr[:, 0] /= lens arr[:, 1] /= lens arr[:, 2] /= lens return arr def compute_normal(vertices, faces): # Create a zeroed array with the same type and shape as our vertices i.e., per vertex normal norm = np.zeros(vertices.shape, dtype=vertices.dtype) # Create an indexed view into the vertex array using the array of three indices for triangles tris = vertices[faces] # Calculate the normal for all the triangles, by taking the cross product of the vectors v1-v0, and v2-v0 in each triangle n = np.cross(tris[::, 1] - tris[::, 0], tris[::, 2] - tris[::, 0]) # n is now an array of normals per triangle. The length of each normal is dependent the vertices, # we need to normalize these, so that our next step weights each normal equally. normalize_v3(n) # now we have a normalized array of normals, one per triangle, i.e., per triangle normals. # But instead of one per triangle (i.e., flat shading), we add to each vertex in that triangle, # the triangles' normal. Multiple triangles would then contribute to every vertex, so we need to normalize again afterwards. # The cool part, we can actually add the normals through an indexed view of our (zeroed) per vertex normal array norm[faces[:, 0]] += n norm[faces[:, 1]] += n norm[faces[:, 2]] += n normalize_v3(norm) return norm def make_rotate(rx, ry, rz): sinX = np.sin(rx) sinY = np.sin(ry) sinZ = np.sin(rz) cosX = np.cos(rx) cosY = np.cos(ry) cosZ = np.cos(rz) Rx = np.zeros((3, 3)) Rx[0, 0] = 1.0 Rx[1, 1] = cosX Rx[1, 2] = -sinX Rx[2, 1] = sinX Rx[2, 2] = cosX Ry = np.zeros((3, 3)) Ry[0, 0] = cosY Ry[0, 2] = sinY Ry[1, 1] = 1.0 Ry[2, 0] = -sinY Ry[2, 2] = cosY Rz = np.zeros((3, 3)) Rz[0, 0] = cosZ Rz[0, 1] = -sinZ Rz[1, 0] = sinZ Rz[1, 1] = cosZ Rz[2, 2] = 1.0 R = np.matmul(np.matmul(Rz, Ry), Rx) return R parser = argparse.ArgumentParser() parser.add_argument('-ww', '--width', type=int, default=512) parser.add_argument('-hh', '--height', type=int, default=512) parser.add_argument('--exp_name', type=str) parser.add_argument('--dataset', type=str) parser.add_argument('--mesh_ind', type=int, default=0) args = parser.parse_args() renderer = ColorRender(width=args.width, height=args.height) cam = Camera(width=1.0, height=args.height / args.width) cam.ortho_ratio = 1.2 cam.near = -100 cam.far = 10 data_root = 'data/result/if_nerf/{}/mesh'.format( args.exp_name) obj_path = os.path.join(data_root, '{:04d}.ply'.format(args.mesh_ind)) mesh_render_dir = os.path.join(data_root, 'mesh{}_render'.format(args.mesh_ind)) os.system('mkdir -p {}'.format(mesh_render_dir)) obj_files = [obj_path] if args.dataset == 'zju_mocap': R = make_rotate(0, math.radians(0), 0) # zju-mocap else: R = make_rotate(0, math.radians(90), math.radians(90)) # people-snapshot print(colored('the results are saved at {}'.format(mesh_render_dir), 'yellow')) for i, obj_path in enumerate(obj_files): print(obj_path) obj_file = obj_path.split('/')[-1] file_name = obj_file[:-4] if not os.path.exists(obj_path): continue mesh = trimesh.load(obj_path) vertices = mesh.vertices faces = mesh.faces rot = np.array([[1, 0, 0], [0, 0, 1], [0, -1, 0]]) vertices = np.dot(vertices, rot.T) mesh.vertices = vertices vertices = np.matmul(vertices, R.T) bbox_max = vertices.max(0) bbox_min = vertices.min(0) # notice that original scale is discarded to render with the same size vertices -= 0.5 * (bbox_max + bbox_min)[None, :] vertices /= bbox_max[1] - bbox_min[1] normals = compute_normal(vertices, faces) renderer.set_mesh(vertices, faces, 0.5 * normals + 0.5, faces) self_rot = make_rotate(i, math.radians(-90), 0) vertices = np.matmul(vertices, self_rot.T) cnt = 0 for j in range(0, 361, 4): cam.center = np.array([0, 0, 0]) cam.eye = np.array([ 2.0 * math.sin(math.radians(0)), 0, 2.0 * math.cos(math.radians(0)) ]) + cam.center self_rot = make_rotate(i, math.radians(-4), 0) vertices = np.matmul(vertices, self_rot.T) normals = compute_normal(vertices, faces) renderer.set_mesh(vertices, faces, 0.5 * normals + 0.5, faces) renderer.set_camera(cam) renderer.display() img = renderer.get_color(0) img = cv2.cvtColor(img, cv2.COLOR_RGBA2BGRA) img = img[..., :3] cv2.imwrite(os.path.join(mesh_render_dir, '%d.jpg' % cnt), 255 * img) cnt += 1