You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

220 lines
8.1 KiB

import os
import sys
# 设置项目根目录
project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
sys.path.append(project_dir)
os.chdir(project_dir)
# 导入日志系统
from utils.logger import logger
import numpy as np
from scipy.spatial import cKDTree
from scipy.spatial.distance import directed_hausdorff
import trimesh
import pandas as pd
import csv
import math
import pickle
import argparse
# parse args first and set gpu id
parser = argparse.ArgumentParser()
parser.add_argument('--gt_path', type=str,
default=os.path.join(project_dir, '../data/eval_data'),
help='ground truth data path')
parser.add_argument('--pred_path', type=str,
default=os.path.join(project_dir, '../data/output_data'),
help='converted data path')
parser.add_argument('--name_list', type=str, default='broken_bullet_name.txt', help='names of models to be evaluated, if you want to evaluate the whole dataset, please set it as all_names.txt')
parser.add_argument('--nsample', type=int, default=50000, help='point batch size')
parser.add_argument('--regen', default = False, action="store_true", help = 'regenerate feature curves')
args = parser.parse_args()
def distance_p2p(points_src, normals_src, points_tgt, normals_tgt):
''' Computes minimal distances of each point in points_src to points_tgt.
Args:
points_src (numpy array [N, 3]): source points
normals_src (numpy array [N, 3]): source normals
points_tgt (numpy array [M, 3]): target points
normals_tgt (numpy array [M, 3]): target
Returns:
dist (numpy array [N]): minimal distances of each point in points_src to points_tgt
normals_dot_product (numpy array [N]): dot product of normals of points_src and points_tgt
'''
kdtree = cKDTree(points_tgt)
dist, idx = kdtree.query(points_src)
if normals_src is not None and normals_tgt is not None:
normals_src = \
normals_src / np.linalg.norm(normals_src, axis=-1, keepdims=True)
normals_tgt = \
normals_tgt / np.linalg.norm(normals_tgt, axis=-1, keepdims=True)
normals_dot_product = (normals_tgt[idx] * normals_src).sum(axis=-1)
# Handle normals that point into wrong direction gracefully
# (mostly due to mehtod not caring about this in generation)
normals_dot_product = np.abs(normals_dot_product)
return dist, normals_dot_product
def distance_feature2mesh(points, mesh):
prox = trimesh.proximity.ProximityQuery(mesh)
signed_distance = prox.signed_distance(points)
return np.abs(signed_distance)
def distance_p2mesh(points_src, normals_src, mesh):
points_tgt, idx = mesh.sample(args.nsample, return_index=True)
points_tgt = points_tgt.astype(np.float32)
normals_tgt = mesh.face_normals[idx]
cd1, nc1 = distance_p2p(points_src, normals_src, points_tgt, normals_tgt) #pred2gt
hd1 = cd1.max()
cd1 = cd1.mean()
nc1 = np.clip(nc1, -1.0, 1.0)
angles1 = np.arccos(nc1) / math.pi * 180.0
angles1_mean = angles1.mean()
angles1_std = np.std(angles1)
cd2, nc2 = distance_p2p(points_tgt, normals_tgt, points_src, normals_src) #gt2pred
hd2 = cd2.max()
cd2 = cd2.mean()
nc2 = np.clip(nc2, -1.0, 1.0)
angles2 = np.arccos(nc2)/ math.pi * 180.0
angles2_mean = angles2.mean()
angles2_std = np.std(angles2)
cd = 0.5 * (cd1 + cd2)
hd = max(hd1, hd2)
angles_mean = 0.5 * (angles1_mean + angles2_mean)
angles_std = 0.5 * (angles1_std + angles2_std)
return cd, hd, angles_mean, angles_std, hd1, hd2
def distance_fea(gt_pa, pred_pa):
"""计算特征点之间的距离和角度差异
Args:
gt_pa: 真实特征点和角度 [N, 4]
pred_pa: 预测特征点和角度 [N, 4]
Returns:
dfg2p: 真实到预测的距离
dfp2g: 预测到真实的距离
fag2p: 真实到预测的角度差
fap2g: 预测到真实的角度差
"""
gt_points = gt_pa[:,:3]
pred_points = pred_pa[:,:3]
gt_angle = gt_pa[:,3]
pred_angle = pred_pa[:,3]
dfg2p = 0.0
dfp2g = 0.0
fag2p = 0.0
fap2g = 0.0
pred_kdtree = cKDTree(pred_points)
dist1, idx1 = pred_kdtree.query(gt_points)
dfg2p = dist1.mean()
assert(idx1.shape[0] == gt_points.shape[0])
fag2p = np.abs(gt_angle - pred_angle[idx1])
gt_kdtree = cKDTree(gt_points)
dist2, idx2 = gt_kdtree.query(pred_points)
dfp2g = dist2.mean()
fap2g = np.abs(pred_angle - gt_angle[idx2])
fag2p = fag2p.mean()
fap2g = fap2g.mean()
return dfg2p, dfp2g, fag2p, fap2g
def compute_all():
gt_path = args.gt_path
pred_mesh_path = args.pred_path
namelst = args.name_list
output_path = 'eval_results.csv'
with open(os.path.join(project_dir, 'evaluation', namelst), 'r') as f:
lines = f.readlines()
d = {'name':[], 'CD':[], 'HD':[], 'HDgt2pred':[], 'HDpred2gt':[], 'AngleDiffMean':[], 'AngleDiffStd':[], 'FeaDfgt2pred':[], 'FeaDfpred2gt':[], 'FeaDf':[], 'FeaAnglegt2pred':[], 'FeaAnglepred2gt':[], 'FeaAngle':[]}
for line in lines:
line = line.strip()[:-4]
print(line)
test_xyz = os.path.join(gt_path, line+'_50k.xyz')
ptnormal = np.loadtxt(test_xyz)
meshfile = os.path.join(pred_mesh_path, '{}_50k.ply'.format(line))
if not os.path.exists(meshfile):
print('file not exists: ', meshfile)
f = open(meshfile + 'noexists', 'w')
f.close()
continue
stat_file = meshfile + "_stat"
if not args.regen and os.path.exists(stat_file) and os.path.getsize(stat_file) > 0:
#load compuated ones
f = open(stat_file, 'rb')
cur_dict = pickle.load(f)
for k in cur_dict:
d[k].append(cur_dict[k])
f.close()
continue
d['name'].append(line)
mesh = trimesh.load(meshfile)
cd, hd, adm, ads, hd_pred2gt, hd_gt2pred = distance_p2mesh(ptnormal[:,:3], ptnormal[:,3:], mesh)
d['CD'].append(cd)
d['HD'].append(hd)
d['HDpred2gt'].append(hd_pred2gt)
d['HDgt2pred'].append(hd_gt2pred)
d['AngleDiffMean'].append(adm)
d['AngleDiffStd'].append(ads)
gt_ptangle = np.loadtxt(os.path.join(gt_path, line + '_detectfea4e-3.ptangle'))
pred_ptangle_path = meshfile[:-4]+'_4e-3.ptangle'
if not os.path.exists(pred_ptangle_path) or args.regen:
os.system('./MeshFeatureSample/build/SimpleSample -i {} -o {} -s 4e-3'.format(meshfile, pred_ptangle_path))
pred_ptangle = np.loadtxt(pred_ptangle_path).reshape(-1,4)
#for smooth case: if gt fea is empty, or pred fea is empty, then return 0
if len(gt_ptangle) == 0 or len(pred_ptangle) == 0:
d['FeaDfgt2pred'].append(0.0)
d['FeaDfpred2gt'].append(0.0)
d['FeaAnglegt2pred'].append(0.0)
d['FeaAnglepred2gt'].append(0.0)
d['FeaDf'].append(0.0)
d['FeaAngle'].append(0.0)
else:
dfg2p, dfp2g, fag2p, fap2g = distance_fea(gt_ptangle, pred_ptangle)
d['FeaDfgt2pred'].append(dfg2p)
d['FeaDfpred2gt'].append(dfp2g)
d['FeaAnglegt2pred'].append(fag2p)
d['FeaAnglepred2gt'].append(fap2g)
d['FeaDf'].append((dfg2p + dfp2g) / 2.0)
d['FeaAngle'].append((fag2p + fap2g) / 2.0)
cur_d = {}
for k in d:
cur_d[k] = d[k][-1]
f = open(stat_file,"wb")
pickle.dump(cur_d, f)
f.close()
d['name'].append('mean')
for key in d:
if key != 'name':
d[key].append(sum(d[key])/len(d[key]))
df = pd.DataFrame(d, columns=['name', 'CD', 'HD', 'HDpred2gt', 'HDgt2pred', 'AngleDiffMean', 'AngleDiffStd','FeaDfgt2pred', 'FeaDfpred2gt', 'FeaDf', 'FeaAnglegt2pred', 'FeaAnglepred2gt', 'FeaAngle'])
df.to_csv(output_path, index = False, header=True)
if __name__ == '__main__':
compute_all()