| import time, os, random, traceback, sys
|
| from pathlib import Path
|
|
|
| import matplotlib.pyplot as plt
|
| import torch
|
| import numpy as np
|
|
|
| from OCC.Core.BRepAdaptor import BRepAdaptor_Curve
|
| from tqdm import tqdm
|
| import trimesh
|
| import argparse
|
|
|
|
|
| from chamferdist import ChamferDistance
|
|
|
| from OCC.Core.STEPControl import STEPControl_Reader
|
| from OCC.Core.TopExp import TopExp_Explorer
|
| from OCC.Core.TopAbs import TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE
|
| from OCC.Core.BRep import BRep_Tool
|
| from OCC.Core.gp import gp_Pnt
|
| from OCC.Core.IFSelect import IFSelect_RetDone
|
| from OCC.Extend.DataExchange import read_step_file, write_step_file, write_stl_file
|
| from OCC.Core.BRepCheck import BRepCheck_Analyzer
|
|
|
| import ray
|
| import shutil
|
|
|
| from OCC.Core.TopoDS import TopoDS_Solid, TopoDS_Shell
|
| from OCC.Core.TopAbs import TopAbs_COMPOUND, TopAbs_SHELL, TopAbs_SOLID
|
|
|
| from diffusion.utils import get_primitives, get_triangulations, get_points_along_edge, get_curve_length
|
| from eval.check_valid import check_step_valid_soild
|
|
|
|
|
| def is_vertex_close(p1, p2, tol=1e-3):
|
| return np.linalg.norm(np.array(p1) - np.array(p2)) < tol
|
|
|
|
|
| def compute_statistics(eval_root, v_only_valid, listfile):
|
| all_folders = [folder for folder in os.listdir(eval_root) if os.path.isdir(os.path.join(eval_root, folder))]
|
| if listfile != '':
|
| valid_names = [item.strip() for item in open(listfile, 'r').readlines()]
|
| all_folders = list(set(all_folders) & set(valid_names))
|
| all_folders.sort()
|
| exception_folders = []
|
| results = {
|
| "prefix": []
|
| }
|
| for folder_name in tqdm(all_folders):
|
| if not os.path.exists(os.path.join(eval_root, folder_name, 'eval.npz')):
|
| exception_folders.append(folder_name)
|
| continue
|
|
|
| item = np.load(os.path.join(eval_root, folder_name, 'eval.npz'), allow_pickle=True)['results'].item()
|
| if item['num_recon_face'] == 1:
|
| exception_folders.append(folder_name)
|
| if v_only_valid:
|
| continue
|
|
|
| if v_only_valid and not os.path.exists(os.path.join(eval_root, folder_name, 'success.txt')):
|
| continue
|
|
|
| results["prefix"].append(folder_name)
|
| for key in item:
|
| if key not in results:
|
| results[key] = []
|
| results[key].append(item[key])
|
|
|
| if len(exception_folders) != 0:
|
| print(f"Found exception folders: {exception_folders}")
|
|
|
| for key in results:
|
| results[key] = np.array(results[key])
|
|
|
| results_str = ""
|
| results_str += "Number\n"
|
| results_str += f"Vertices: {np.mean(results['num_recon_vertex'])}/{np.mean(results['num_gt_vertex'])}\n"
|
| results_str += f"Edge: {np.mean(results['num_recon_edge'])}/{np.mean(results['num_gt_edge'])}\n"
|
| results_str += f"Face: {np.mean(results['num_recon_face'])}/{np.mean(results['num_gt_face'])}\n"
|
|
|
| results_str += "Chamfer\n"
|
| results_str += f"Vertices: {np.mean(results['vertex_cd'])}\n"
|
| results_str += f"Edge: {np.mean(results['edge_cd'])}\n"
|
| results_str += f"Face: {np.mean(results['face_cd'])}\n"
|
|
|
| results_str += "Detection\n"
|
| results_str += f"Vertices: {np.mean(results['vertex_fscore'])}\n"
|
| results_str += f"Edge: {np.mean(results['edge_fscore'])}\n"
|
| results_str += f"Face: {np.mean(results['face_fscore'])}\n"
|
|
|
| results_str += "Topology\n"
|
| results_str += f"FE: {np.mean(results['fe_fscore'])}\n"
|
| results_str += f"EV: {np.mean(results['ev_fscore'])}\n"
|
|
|
| results_str += "Accuracy\n"
|
| results_str += f"Vertices: {np.mean(results['vertex_acc_cd'])}\n"
|
| results_str += f"Edge: {np.mean(results['edge_acc_cd'])}\n"
|
| results_str += f"Face: {np.mean(results['face_acc_cd'])}\n"
|
| results_str += f"FE: {np.mean(results['fe_pre'])}\n"
|
| results_str += f"EV: {np.mean(results['ev_pre'])}\n"
|
|
|
| results_str += "Completeness\n"
|
| results_str += f"Vertices: {np.mean(results['vertex_com_cd'])}\n"
|
| results_str += f"Edge: {np.mean(results['edge_com_cd'])}\n"
|
| results_str += f"Face: {np.mean(results['face_com_cd'])}\n"
|
| results_str += f"FE: {np.mean(results['fe_rec'])}\n"
|
| results_str += f"EV: {np.mean(results['ev_rec'])}\n"
|
| print(results_str)
|
| print("{:.4f} {:.4f} {:.4f} {:.2f} {:.2f} {:.2f} {:.2f} {:.2f}".format(
|
| np.mean(results['vertex_cd']), np.mean(results['edge_cd']), np.mean(results['face_cd']),
|
| np.mean(results['vertex_fscore']), np.mean(results['edge_fscore']), np.mean(results['face_fscore']),
|
| np.mean(results['fe_fscore']), np.mean(results['ev_fscore']),
|
| ))
|
| print(
|
| "{:.0f}/{:.0f} {:.0f}/{:.0f} {:.0f}/{:.0f} {:.4f} {:.4f} {:.4f} {:.4f} {:.4f} {:.4f} {:.2f} {:.2f} {:.2f} {:.2f} {:.2f} {:.2f} {:.2f} {:.2f} {:.2f} {:.2f}".format(
|
| np.mean(results['num_recon_vertex']), np.mean(results['num_gt_vertex']),
|
| np.mean(results['num_recon_edge']), np.mean(results['num_gt_edge']),
|
| np.mean(results['num_recon_face']), np.mean(results['num_gt_face']),
|
| np.mean(results['vertex_acc_cd']), np.mean(results['edge_acc_cd']), np.mean(results['face_acc_cd']),
|
| np.mean(results['vertex_com_cd']), np.mean(results['edge_com_cd']), np.mean(results['face_com_cd']),
|
| np.mean(results['vertex_pre']), np.mean(results['edge_pre']), np.mean(results['face_pre']),
|
| np.mean(results['fe_pre']), np.mean(results['ev_pre']),
|
| np.mean(results['vertex_rec']), np.mean(results['edge_rec']), np.mean(results['face_rec']),
|
| np.mean(results['fe_rec']), np.mean(results['ev_rec'])
|
| ))
|
|
|
| print(f"{results['face_cd'].shape[0]}/{len(all_folders)} are valid")
|
|
|
| def draw():
|
| face_chamfer = results['face_cd']
|
| fig, ax = plt.subplots(1, 1, figsize=(6, 6))
|
| ax.hist(face_chamfer, bins=50, range=(0, 0.05), density=True, alpha=0.5, color='b', label='Face')
|
| ax.set_title('Face Chamfer Distance')
|
| ax.set_xlabel('Chamfer Distance')
|
| ax.set_ylabel('Density')
|
| ax.legend()
|
| plt.savefig(str(eval_root) + "_face_chamfer.png", dpi=600)
|
|
|
|
|
| draw()
|
| pass
|
|
|
|
|
| def get_data(v_shape, v_num_per_m=100):
|
| faces, face_points, edges, edge_points, vertices, vertex_points = [], [], [], [], [], []
|
| for face in get_primitives(v_shape, TopAbs_FACE, v_remove_half=True):
|
| try:
|
| v, f = get_triangulations(face, 0.1, 0.1)
|
| if len(f) == 0:
|
| print("Ignore 0 face")
|
| continue
|
| except:
|
| print("Ignore 1 face")
|
| continue
|
| mesh_item = trimesh.Trimesh(vertices=v, faces=f)
|
| area = mesh_item.area
|
| num_samples = min(max(int(v_num_per_m * v_num_per_m * area), 5), 10000)
|
| pc_item, id_face = trimesh.sample.sample_surface(mesh_item, num_samples)
|
| normals = mesh_item.face_normals[id_face]
|
| faces.append(face)
|
| face_points.append(np.concatenate((pc_item, normals), axis=1))
|
| for edge in get_primitives(v_shape, TopAbs_EDGE, v_remove_half=True):
|
| length = get_curve_length(edge)
|
| num_samples = min(max(int(v_num_per_m * length), 5), 10000)
|
| v = get_points_along_edge(edge, num_samples)
|
| edges.append(edge)
|
| edge_points.append(v)
|
| for vertex in get_primitives(v_shape, TopAbs_VERTEX, v_remove_half=True):
|
| vertices.append(vertex)
|
| vertex_points.append(np.asarray([BRep_Tool.Pnt(vertex).Coord()]))
|
| vertex_points = np.stack(vertex_points, axis=0)
|
| return faces, face_points, edges, edge_points, vertices, vertex_points
|
|
|
|
|
| def get_chamfer(v_recon_points, v_gt_points):
|
| device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
|
| chamfer_distance = ChamferDistance()
|
| recon_fp = torch.from_numpy(np.concatenate(v_recon_points, axis=0)).float().to(device)[:, :3]
|
| gt_fp = torch.from_numpy(np.concatenate(v_gt_points, axis=0)).float().to(device)[:, :3]
|
| fp_acc_cd = chamfer_distance(recon_fp.unsqueeze(0), gt_fp.unsqueeze(0),
|
| bidirectional=False, point_reduction='mean').cpu().item()
|
| fp_com_cd = chamfer_distance(gt_fp.unsqueeze(0), recon_fp.unsqueeze(0),
|
| bidirectional=False, point_reduction='mean').cpu().item()
|
| fp_cd = fp_acc_cd + fp_com_cd
|
| return fp_acc_cd, fp_com_cd, fp_cd
|
|
|
|
|
| def get_match_ids(v_recon_points, v_gt_points):
|
| from scipy.optimize import linear_sum_assignment
|
|
|
| cost = np.zeros([len(v_recon_points), len(v_gt_points)])
|
| for i in range(cost.shape[0]):
|
| for j in range(cost.shape[1]):
|
| _, _, cost[i][j] = get_chamfer(
|
| v_recon_points[i][..., :3][None, ..., :3],
|
| v_gt_points[j][..., :3][None, ..., :3]
|
| )
|
|
|
| recon_indices, recon_to_gt = linear_sum_assignment(cost)
|
|
|
| result_recon2gt = -1 * np.ones(len(v_recon_points), dtype=np.int32)
|
| result_gt2recon = -1 * np.ones(len(v_gt_points), dtype=np.int32)
|
|
|
| result_recon2gt[recon_indices] = recon_to_gt
|
| result_gt2recon[recon_to_gt] = recon_indices
|
| return result_recon2gt, result_gt2recon, cost
|
|
|
|
|
| def get_detection(id_recon_gt, id_gt_recon, cost_matrix, v_threshold=0.1):
|
| true_positive = 0
|
| for i in range(len(id_recon_gt)):
|
| if id_recon_gt[i] != -1 and cost_matrix[i, id_recon_gt[i]] < v_threshold:
|
| true_positive += 1
|
| precision = true_positive / (len(id_recon_gt) + 1e-6)
|
| recall = true_positive / (len(id_gt_recon) + 1e-6)
|
| return 2 * precision * recall / (precision + recall + 1e-6), precision, recall
|
|
|
|
|
| def get_topology(faces, edges, vertices):
|
| recon_face_edge, recon_edge_vertex = {}, {}
|
| for i_face, face in enumerate(faces):
|
| face_edge = []
|
| for edge in get_primitives(face, TopAbs_EDGE):
|
| face_edge.append(edges.index(edge) if edge in edges else edges.index(edge.Reversed()))
|
| recon_face_edge[i_face] = list(set(face_edge))
|
|
|
| for i_edge, edge in enumerate(edges):
|
| edge_vertex = []
|
| for vertex in get_primitives(edge, TopAbs_VERTEX):
|
| edge_vertex.append(vertices.index(vertex) if vertex in vertices else vertices.index(vertex.Reversed()))
|
| recon_edge_vertex[i_edge] = list(set(edge_vertex))
|
| return recon_face_edge, recon_edge_vertex
|
|
|
|
|
| def get_topo_detection(recon_face_edge, gt_face_edge, id_recon_gt_face, id_recon_gt_edge):
|
| positive = 0
|
| for i_recon_face, edges in recon_face_edge.items():
|
| i_gt_face = id_recon_gt_face[i_recon_face]
|
| if i_gt_face == -1:
|
| continue
|
| for i_edge in edges:
|
| if id_recon_gt_edge[i_edge] in gt_face_edge[i_gt_face]:
|
| positive += 1
|
| precision = positive / (sum([len(edges) for edges in recon_face_edge.values()]) + 1e-6)
|
| recall = positive / (sum([len(edges) for edges in gt_face_edge.values()]) + 1e-6)
|
| return 2 * precision * recall / (precision + recall + 1e-6), precision, recall
|
|
|
| def eval_one_with_try(eval_root, gt_root, folder_name, is_point2cad=False, v_num_per_m=100):
|
| try:
|
| eval_one(eval_root, gt_root, folder_name, is_point2cad, v_num_per_m)
|
| except:
|
| pass
|
|
|
| def eval_one(eval_root, gt_root, folder_name, is_point2cad=False, v_num_per_m=100):
|
| if os.path.exists(eval_root / folder_name / 'error.txt'):
|
| os.remove(eval_root / folder_name / 'error.txt')
|
| if os.path.exists(eval_root / folder_name / 'eval.npz'):
|
| os.remove(eval_root / folder_name / 'eval.npz')
|
|
|
|
|
| step_name = "recon_brep.step"
|
|
|
| if is_point2cad:
|
| if not (eval_root / folder_name / "clipped/mesh_transformed.ply").exists():
|
| print(f"Error: {folder_name} does not have mesh_transformed")
|
| return
|
| mesh = trimesh.load(eval_root / folder_name / "clipped/mesh_transformed.ply")
|
| color = np.stack((
|
| [item[1] for item in mesh.metadata['_ply_raw']['face']['data']],
|
| [item[2] for item in mesh.metadata['_ply_raw']['face']['data']],
|
| [item[3] for item in mesh.metadata['_ply_raw']['face']['data']],
|
| ), axis=1)
|
| color_map = [list(map(lambda item:int(item),item.strip().split(" "))) for item in open("src/brepnet/eval/point2cad_color.txt").readlines()]
|
| index = np.asarray([color_map.index(item.tolist()) for item in color])
|
| recon_face_points = [None]*(index.max()+1)
|
| for i in range(index.max() + 1):
|
| item_faces = mesh.faces[index == i]
|
| item_mesh = trimesh.Trimesh(vertices=mesh.vertices, faces=item_faces)
|
| num_samples = min(max(int(item_mesh.area * v_num_per_m * v_num_per_m), 5), 10000)
|
| pc_item, id_face = trimesh.sample.sample_surface(item_mesh, num_samples)
|
| normals = item_mesh.face_normals[id_face]
|
| recon_face_points[i] = np.concatenate((pc_item, normals), axis=1)
|
|
|
| if not (eval_root / folder_name / "clipped/curve_points.xyzc").exists():
|
| print(f"Error: {folder_name} does not have curve_points")
|
| return
|
| curve_points = np.asarray([list(map(lambda item: float(item),item.strip().split(" "))) for item in open(eval_root / folder_name / "clipped/curve_points.xyzc").readlines()])
|
| num_curves = int(curve_points.max(axis=0)[3]) + 1
|
| recon_edge_points = [None]*num_curves
|
| for i in range(num_curves):
|
| item_points = curve_points[curve_points[:,3] == i][:,:3]
|
| recon_edge_points[i] = item_points
|
|
|
| if (eval_root / folder_name / "clipped/remove_duplicates_corners.ply").exists():
|
| pc = trimesh.load(eval_root / folder_name / "clipped/remove_duplicates_corners.ply")
|
| recon_vertex_points = pc.vertices[:,None]
|
| else:
|
| recon_vertex_points = np.asarray((0,0,0), dtype=np.float32)[None,None]
|
|
|
| recon_face_edge = {}
|
| recon_edge_vertex = {}
|
| EV_mode = False
|
| for items in open(eval_root / folder_name / 'topo/topo_fix.txt', 'r').readlines():
|
| items = items.strip().split(" ")
|
| if items[0] == "EV":
|
| EV_mode = True
|
| continue
|
| if len(items) == 1:
|
| continue
|
| if not EV_mode:
|
| recon_face_edge[int(items[0])] = list(map(lambda item: int(item), items[1:]))
|
| else:
|
| recon_edge_vertex[int(items[0])] = list(map(lambda item: int(item), items[1:]))
|
| pass
|
| else:
|
| try:
|
|
|
| if (eval_root / folder_name / step_name).exists():
|
| valid, recon_shape = check_step_valid_soild(eval_root / folder_name / step_name, return_shape=True)
|
| else:
|
| print(f"Error: {folder_name} does not have {step_name}")
|
| raise
|
| if recon_shape is None:
|
| print(f"Error: {folder_name} 's {step_name} is not valid")
|
| raise
|
|
|
|
|
| recon_faces, recon_face_points, recon_edges, recon_edge_points, recon_vertices, recon_vertex_points = get_data(
|
| recon_shape, v_num_per_m)
|
|
|
|
|
| recon_face_edge, recon_edge_vertex = get_topology(recon_faces, recon_edges, recon_vertices)
|
| except:
|
| recon_face_points = [np.zeros((1, 6), dtype=np.float32)]
|
| recon_edge_points = [np.zeros((1, 6), dtype=np.float32)]
|
| recon_vertex_points = [np.zeros((1, 3), dtype=np.float32)]
|
| recon_face_edge = {}
|
| recon_edge_vertex = {}
|
|
|
|
|
| _, gt_shape = check_step_valid_soild(gt_root / folder_name / "normalized_shape.step", return_shape=True)
|
| gt_faces, gt_face_points, gt_edges, gt_edge_points, gt_vertices, gt_vertex_points = get_data(gt_shape, v_num_per_m)
|
| gt_face_edge, gt_edge_vertex = get_topology(gt_faces, gt_edges, gt_vertices)
|
|
|
|
|
| face_acc_cd, face_com_cd, face_cd = get_chamfer(recon_face_points, gt_face_points)
|
| edge_acc_cd, edge_com_cd, edge_cd = get_chamfer(recon_edge_points, gt_edge_points)
|
| vertex_acc_cd, vertex_com_cd, vertex_cd = get_chamfer(recon_vertex_points, gt_vertex_points)
|
|
|
|
|
| id_recon_gt_face, id_gt_recon_face, cost_face = get_match_ids(recon_face_points, gt_face_points)
|
| id_recon_gt_edge, id_gt_recon_edge, cost_edge = get_match_ids(recon_edge_points, gt_edge_points)
|
| id_recon_gt_vertex, id_gt_recon_vertex, cost_vertices = get_match_ids(recon_vertex_points, gt_vertex_points)
|
|
|
| face_fscore, face_pre, face_rec = get_detection(id_recon_gt_face, id_gt_recon_face, cost_face)
|
| edge_fscore, edge_pre, edge_rec = get_detection(id_recon_gt_edge, id_gt_recon_edge, cost_edge)
|
| vertex_fscore, vertex_pre, vertex_rec = get_detection(id_recon_gt_vertex, id_gt_recon_vertex, cost_vertices)
|
|
|
| fe_fscore, fe_pre, fe_rec = get_topo_detection(recon_face_edge, gt_face_edge, id_recon_gt_face, id_recon_gt_edge)
|
| ev_fscore, ev_pre, ev_rec = get_topo_detection(recon_edge_vertex, gt_edge_vertex, id_recon_gt_edge,
|
| id_recon_gt_vertex)
|
|
|
| results = {
|
| "face_cd": face_cd,
|
| "edge_cd": edge_cd,
|
| "vertex_cd": vertex_cd,
|
|
|
| "face_fscore": face_fscore,
|
| "edge_fscore": edge_fscore,
|
| "vertex_fscore": vertex_fscore,
|
| "fe_fscore": fe_fscore,
|
| "ev_fscore": ev_fscore,
|
|
|
| "face_acc_cd": face_acc_cd,
|
| "edge_acc_cd": edge_acc_cd,
|
| "vertex_acc_cd": vertex_acc_cd,
|
|
|
| "face_com_cd": face_com_cd,
|
| "edge_com_cd": edge_com_cd,
|
| "vertex_com_cd": vertex_com_cd,
|
|
|
| "fe_pre": fe_pre,
|
| "ev_pre": ev_pre,
|
| "fe_rec": fe_rec,
|
| "ev_rec": ev_rec,
|
|
|
| "vertex_pre": vertex_pre,
|
| "edge_pre": edge_pre,
|
| "face_pre": face_pre,
|
|
|
| "vertex_rec": vertex_rec,
|
| "edge_rec": edge_rec,
|
| "face_rec": face_rec,
|
|
|
| "num_recon_face": len(recon_face_points),
|
| "num_gt_face": len(gt_face_points),
|
| "num_recon_edge": len(recon_edge_points),
|
| "num_gt_edge": len(gt_edge_points),
|
| "num_recon_vertex": len(recon_vertex_points),
|
| "num_gt_vertex": len(gt_vertex_points),
|
| }
|
| np.savez_compressed(eval_root / folder_name / 'eval.npz', results=results, allow_pickle=True)
|
|
|
|
|
| if __name__ == '__main__':
|
| parser = argparse.ArgumentParser(description='Evaluate The Generated Brep')
|
| parser.add_argument('--eval_root', type=str, required=True)
|
| parser.add_argument('--gt_root', type=str, required=True)
|
| parser.add_argument('--use_ray', action='store_true')
|
| parser.add_argument('--num_cpus', type=int, default=16)
|
| parser.add_argument('--prefix', type=str, default='')
|
| parser.add_argument('--list', type=str, default='')
|
| parser.add_argument('--from_scratch', action='store_true')
|
| parser.add_argument('--is_point2cad', action='store_true')
|
| parser.add_argument('--only_valid', action='store_true')
|
| args = parser.parse_args()
|
| eval_root = Path(args.eval_root)
|
| gt_root = Path(args.gt_root)
|
| is_use_ray = args.use_ray
|
| num_cpus = args.num_cpus
|
| listfile = args.list
|
| from_scratch = args.from_scratch
|
| is_point2cad = args.is_point2cad
|
| only_valid = args.only_valid
|
|
|
| if not os.path.exists(eval_root):
|
| raise ValueError(f"Data root path {eval_root} does not exist.")
|
| if not os.path.exists(gt_root):
|
| raise ValueError(f"Output root path {gt_root} does not exist.")
|
|
|
| if args.prefix != '':
|
| eval_one(eval_root, gt_root, args.prefix, is_point2cad)
|
| exit()
|
|
|
| all_folders = [folder for folder in os.listdir(eval_root) if os.path.isdir(eval_root / folder)]
|
| ori_length = len(all_folders)
|
| if listfile != '':
|
| valid_names = [item.strip() for item in open(listfile, 'r').readlines()]
|
| all_folders = list(set(all_folders) & set(valid_names))
|
| all_folders.sort()
|
| print(f"Total {len(all_folders)}/{ori_length} folders to evaluate")
|
|
|
| if not from_scratch:
|
| print("Filtering the folders that have eval.npz")
|
| all_folders = [folder for folder in all_folders if not os.path.exists(eval_root / folder / 'eval.npz')]
|
| print(f"Total {len(all_folders)} folders to compute after caching")
|
|
|
| if not is_use_ray:
|
|
|
| for i in tqdm(range(len(all_folders))):
|
| eval_one(eval_root, gt_root, all_folders[i], is_point2cad)
|
| else:
|
| ray.init(
|
| dashboard_host="0.0.0.0",
|
| dashboard_port=8080,
|
| num_cpus=num_cpus,
|
|
|
| )
|
| eval_one_remote = ray.remote(max_retries=0)(eval_one_with_try)
|
| tasks = []
|
| timeout_cancel_list = []
|
| for i in range(len(all_folders)):
|
| tasks.append(eval_one_remote.remote(eval_root, gt_root, all_folders[i], is_point2cad))
|
| results = []
|
| for i in tqdm(range(len(all_folders))):
|
| try:
|
| results.append(ray.get(tasks[i], timeout=60 * 3))
|
| except ray.exceptions.GetTimeoutError:
|
| results.append(None)
|
| timeout_cancel_list.append(all_folders[i])
|
| ray.cancel(tasks[i])
|
| except:
|
| results.append(None)
|
| results = [item for item in results if item is not None]
|
| print(f"Cancel for timeout: {timeout_cancel_list}")
|
|
|
| print("Computing statistics...")
|
| compute_statistics(eval_root, only_valid, listfile)
|
| print("Done")
|
|
|