|
|
|
|
|
import trimesh |
|
import trimesh.proximity |
|
import trimesh.sample |
|
import numpy as np |
|
import math |
|
import os |
|
from PIL import Image |
|
|
|
import argparse |
|
|
|
def euler_to_rot_mat(r_x, r_y, r_z): |
|
R_x = np.array([[1, 0, 0], |
|
[0, math.cos(r_x), -math.sin(r_x)], |
|
[0, math.sin(r_x), math.cos(r_x)] |
|
]) |
|
|
|
R_y = np.array([[math.cos(r_y), 0, math.sin(r_y)], |
|
[0, 1, 0], |
|
[-math.sin(r_y), 0, math.cos(r_y)] |
|
]) |
|
|
|
R_z = np.array([[math.cos(r_z), -math.sin(r_z), 0], |
|
[math.sin(r_z), math.cos(r_z), 0], |
|
[0, 0, 1] |
|
]) |
|
|
|
R = np.dot(R_z, np.dot(R_y, R_x)) |
|
|
|
return R |
|
|
|
|
|
class MeshEvaluator: |
|
_normal_render = None |
|
|
|
@staticmethod |
|
def init_gl(): |
|
from .render.gl.normal_render import NormalRender |
|
MeshEvaluator._normal_render = NormalRender(width=512, height=512) |
|
|
|
def __init__(self): |
|
pass |
|
|
|
def set_mesh(self, src_path, tgt_path, scale_factor=1.0, offset=0): |
|
self.src_mesh = trimesh.load(src_path) |
|
self.tgt_mesh = trimesh.load(tgt_path) |
|
|
|
self.scale_factor = scale_factor |
|
self.offset = offset |
|
|
|
|
|
def get_chamfer_dist(self, num_samples=10000): |
|
|
|
src_surf_pts, _ = trimesh.sample.sample_surface(self.src_mesh, num_samples) |
|
tgt_surf_pts, _ = trimesh.sample.sample_surface(self.tgt_mesh, num_samples) |
|
|
|
_, src_tgt_dist, _ = trimesh.proximity.closest_point(self.tgt_mesh, src_surf_pts) |
|
_, tgt_src_dist, _ = trimesh.proximity.closest_point(self.src_mesh, tgt_surf_pts) |
|
|
|
src_tgt_dist[np.isnan(src_tgt_dist)] = 0 |
|
tgt_src_dist[np.isnan(tgt_src_dist)] = 0 |
|
|
|
src_tgt_dist = src_tgt_dist.mean() |
|
tgt_src_dist = tgt_src_dist.mean() |
|
|
|
chamfer_dist = (src_tgt_dist + tgt_src_dist) / 2 |
|
|
|
return chamfer_dist |
|
|
|
def get_surface_dist(self, num_samples=10000): |
|
|
|
src_surf_pts, _ = trimesh.sample.sample_surface(self.src_mesh, num_samples) |
|
|
|
_, src_tgt_dist, _ = trimesh.proximity.closest_point(self.tgt_mesh, src_surf_pts) |
|
|
|
src_tgt_dist[np.isnan(src_tgt_dist)] = 0 |
|
|
|
src_tgt_dist = src_tgt_dist.mean() |
|
|
|
return src_tgt_dist |
|
|
|
def _render_normal(self, mesh, deg): |
|
view_mat = np.identity(4) |
|
view_mat[:3, :3] *= 2 / 256 |
|
rz = deg / 180. * np.pi |
|
model_mat = np.identity(4) |
|
model_mat[:3, :3] = euler_to_rot_mat(0, rz, 0) |
|
model_mat[1, 3] = self.offset |
|
view_mat[2, 2] *= -1 |
|
|
|
self._normal_render.set_matrices(view_mat, model_mat) |
|
self._normal_render.set_normal_mesh(self.scale_factor*mesh.vertices, mesh.faces, mesh.vertex_normals, mesh.faces) |
|
self._normal_render.draw() |
|
normal_img = self._normal_render.get_color() |
|
return normal_img |
|
|
|
def _get_reproj_normal_error(self, deg): |
|
tgt_normal = self._render_normal(self.tgt_mesh, deg) |
|
src_normal = self._render_normal(self.src_mesh, deg) |
|
|
|
error = ((src_normal[:, :, :3] - tgt_normal[:, :, :3]) ** 2).mean() * 3 |
|
|
|
return error, src_normal, tgt_normal |
|
|
|
def get_reproj_normal_error(self, frontal=True, back=True, left=True, right=True, save_demo_img=None): |
|
|
|
|
|
if self._normal_render is None: |
|
print("In order to use normal render, " |
|
"you have to call init_gl() before initialing any evaluator objects.") |
|
return -1 |
|
|
|
side_cnt = 0 |
|
total_error = 0 |
|
demo_list = [] |
|
if frontal: |
|
side_cnt += 1 |
|
error, src_normal, tgt_normal = self._get_reproj_normal_error(0) |
|
total_error += error |
|
demo_list.append(np.concatenate([src_normal, tgt_normal], axis=0)) |
|
if back: |
|
side_cnt += 1 |
|
error, src_normal, tgt_normal = self._get_reproj_normal_error(180) |
|
total_error += error |
|
demo_list.append(np.concatenate([src_normal, tgt_normal], axis=0)) |
|
if left: |
|
side_cnt += 1 |
|
error, src_normal, tgt_normal = self._get_reproj_normal_error(90) |
|
total_error += error |
|
demo_list.append(np.concatenate([src_normal, tgt_normal], axis=0)) |
|
if right: |
|
side_cnt += 1 |
|
error, src_normal, tgt_normal = self._get_reproj_normal_error(270) |
|
total_error += error |
|
demo_list.append(np.concatenate([src_normal, tgt_normal], axis=0)) |
|
if save_demo_img is not None: |
|
res_array = np.concatenate(demo_list, axis=1) |
|
res_img = Image.fromarray((res_array * 255).astype(np.uint8)) |
|
res_img.save(save_demo_img) |
|
return total_error / side_cnt |
|
|
|
if __name__ == '__main__': |
|
parser = argparse.ArgumentParser() |
|
parser.add_argument('-r', '--root', type=str, required=True) |
|
parser.add_argument('-t', '--tar_path', type=str, required=True) |
|
args = parser.parse_args() |
|
|
|
evaluator = MeshEvaluator() |
|
evaluator.init_gl() |
|
|
|
def run(root, exp_name, tar_path): |
|
src_path = os.path.join(root, exp_name, 'recon') |
|
rp_path = os.path.join(tar_path, 'RP', 'GEO', 'OBJ') |
|
bf_path = os.path.join(tar_path, 'BUFF', 'GEO', 'PLY') |
|
|
|
buff_files = [f for f in os.listdir(bf_path) if '.ply' in f] |
|
|
|
src_names = ['0_0_00.obj', '90_0_00.obj', '180_0_00.obj', '270_0_00.obj'] |
|
|
|
total_vals = [] |
|
items = [] |
|
for file in buff_files: |
|
tar_name = os.path.join(bf_path, file) |
|
name = tar_name.split('/')[-1][:-4] |
|
|
|
for src in src_names: |
|
src_name = os.path.join(src_path, 'result_%s_%s' % (name, src)) |
|
if not os.path.exists(src_name): |
|
continue |
|
evaluator.set_mesh(src_name, tar_name, 0.13, -40) |
|
|
|
vals = [] |
|
vals.append(0.1 * evaluator.get_chamfer_dist()) |
|
vals.append(0.1 * evaluator.get_surface_dist()) |
|
vals.append(4.0 * evaluator.get_reproj_normal_error(save_demo_img=os.path.join(src_path, '%s_%s.png' % (name, src[:-4])))) |
|
|
|
item = { |
|
'name': '%s_%s' % (name, src), |
|
'vals': vals |
|
} |
|
|
|
total_vals.append(vals) |
|
items.append(item) |
|
|
|
vals = np.array(total_vals).mean(0) |
|
buf_val = vals |
|
|
|
np.save(os.path.join(root, exp_name, 'buff-item.npy'), np.array(items)) |
|
np.save(os.path.join(root, exp_name, 'buff-vals.npy'), total_vals) |
|
|
|
rp_files = [f for f in os.listdir(rp_path) if '.obj' in f] |
|
|
|
total_vals = [] |
|
items = [] |
|
for file in rp_files: |
|
tar_name = os.path.join(rp_path, file) |
|
name = tar_name.split('/')[-1][:-9] |
|
|
|
for src in src_names: |
|
src_name = os.path.join(src_path, 'result_%s_%s' % (name, src)) |
|
if not os.path.exists(src_name): |
|
continue |
|
|
|
evaluator.set_mesh(src_name, tar_name, 1.3, -120) |
|
|
|
vals = [] |
|
vals.append(evaluator.get_chamfer_dist()) |
|
vals.append(evaluator.get_surface_dist()) |
|
vals.append(4.0 * evaluator.get_reproj_normal_error(save_demo_img=os.path.join(src_path, '%s_%s.png' % (name, src[:-4])))) |
|
|
|
item = { |
|
'name': '%s_%s' % (name, src), |
|
'vals': vals |
|
} |
|
|
|
total_vals.append(vals) |
|
items.append(item) |
|
|
|
np.save(os.path.join(root, exp_name, 'rp-item.npy'), np.array(items)) |
|
np.save(os.path.join(root, exp_name, 'rp-vals.npy'), total_vals) |
|
|
|
vals = np.array(total_vals).mean(0) |
|
print('BUFF - chamfer: %.4f p2s: %.4f nml: %.4f' % (buf_val[0], buf_val[1], buf_val[2])) |
|
print('RP - chamfer: %.4f p2s: %.4f nml: %.4f' % (vals[0], vals[1], vals[2])) |
|
|
|
exp_list = ['pifuhd_final'] |
|
|
|
root = args.root |
|
tar_path = args.tar_path |
|
|
|
for exp in exp_list: |
|
run(root, exp, tar_path) |