# This file is derived from [Atlas](https://github.com/magicleap/Atlas). # Originating Author: Zak Murez (zak.murez.com) # Modified for [NeuralRecon](https://github.com/zju3dv/NeuralRecon) by Yiming Xie. # Original header: # Copyright 2020 Magic Leap, Inc. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import open3d as o3d import numpy as np def eval_mesh(mesh_pred, mesh_trgt, threshold=.05, down_sample=.02): """ Compute Mesh metrics between prediction and target. Opens the Meshs and runs the metrics Args: file_pred: file path of prediction file_trgt: file path of target threshold: distance threshold used to compute precision/recal down_sample: use voxel_downsample to uniformly sample mesh points Returns: Dict of mesh metrics """ # pcd_pred = o3d.io.read_point_cloud(file_pred) # pcd_trgt = o3d.io.read_point_cloud(file_trgt) pcd_pred = o3d.geometry.PointCloud() pcd_pred.points = o3d.utility.Vector3dVector(mesh_pred.sample(100000).astype(np.float32)) pcd_trgt = o3d.geometry.PointCloud() pcd_trgt.points = o3d.utility.Vector3dVector(mesh_trgt.sample(100000).astype(np.float32)) if down_sample: pcd_pred = pcd_pred.voxel_down_sample(down_sample) pcd_trgt = pcd_trgt.voxel_down_sample(down_sample) verts_pred = np.asarray(pcd_pred.points) verts_trgt = np.asarray(pcd_trgt.points) _, dist1 = nn_correspondance(verts_pred, verts_trgt) _, dist2 = nn_correspondance(verts_trgt, verts_pred) dist1 = np.array(dist1) dist2 = np.array(dist2) precision = np.mean((dist2 < threshold).astype('float')) recal = np.mean((dist1 < threshold).astype('float')) fscore = 2 * precision * recal / (precision + recal) metrics = {'dist1': np.mean(dist2), 'dist2': np.mean(dist1), 'prec': precision, 'recal': recal, 'fscore': fscore, } return metrics def nn_correspondance(verts1, verts2): """ for each vertex in verts2 find the nearest vertex in verts1 Args: nx3 np.array's Returns: ([indices], [distances]) """ indices = [] distances = [] if len(verts1) == 0 or len(verts2) == 0: return indices, distances pcd = o3d.geometry.PointCloud() pcd.points = o3d.utility.Vector3dVector(verts1) kdtree = o3d.geometry.KDTreeFlann(pcd) for vert in verts2: _, inds, dist = kdtree.search_knn_vector_3d(vert, 1) indices.append(inds[0]) distances.append(np.sqrt(dist[0])) return indices, distances def eval_depth(depth_pred, depth_trgt): """ Computes 2d metrics between two depth maps Args: depth_pred: mxn np.array containing prediction depth_trgt: mxn np.array containing ground truth Returns: Dict of metrics """ mask1 = depth_pred > 0 # ignore values where prediction is 0 (% complete) mask = (depth_trgt < 10) * (depth_trgt > 0) * mask1 depth_pred = depth_pred[mask] depth_trgt = depth_trgt[mask] abs_diff = np.abs(depth_pred - depth_trgt) abs_rel = abs_diff / depth_trgt sq_diff = abs_diff ** 2 sq_rel = sq_diff / depth_trgt sq_log_diff = (np.log(depth_pred) - np.log(depth_trgt)) ** 2 thresh = np.maximum((depth_trgt / depth_pred), (depth_pred / depth_trgt)) r1 = (thresh < 1.25).astype('float') r2 = (thresh < 1.25 ** 2).astype('float') r3 = (thresh < 1.25 ** 3).astype('float') metrics = {} metrics['AbsRel'] = np.mean(abs_rel) metrics['AbsDiff'] = np.mean(abs_diff) metrics['SqRel'] = np.mean(sq_rel) metrics['RMSE'] = np.sqrt(np.mean(sq_diff)) metrics['LogRMSE'] = np.sqrt(np.mean(sq_log_diff)) metrics['r1'] = np.mean(r1) metrics['r2'] = np.mean(r2) metrics['r3'] = np.mean(r3) metrics['complete'] = np.mean(mask1.astype('float')) return metrics