''' ----------------------------------------------------------------------------- Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. NVIDIA CORPORATION and its licensors retain all intellectual property and proprietary rights in and to this software, related documentation and any modifications thereto. Any use, reproduction, disclosure or distribution of this software and related documentation without an express license agreement from NVIDIA CORPORATION is strictly prohibited. ----------------------------------------------------------------------------- ''' import argparse import json import os import sys import numpy as np sys.path.append(os.getcwd()) from imaginaire.config import Config, recursive_update_strict, parse_cmdline_arguments # noqa: E402 from imaginaire.utils.distributed import init_dist, get_world_size, is_master, master_only_print as print # noqa: E402 from imaginaire.utils.gpu_affinity import set_affinity # noqa: E402 from imaginaire.trainers.utils.logging import init_logging # noqa: E402 from imaginaire.trainers.utils.get_trainer import get_trainer # noqa: E402 from projects.neuralangelo.utils.mesh import extract_mesh # noqa: E402 def parse_args(): parser = argparse.ArgumentParser(description="Training") parser.add_argument("--config", required=True, help="Path to the training config file.") parser.add_argument("--logdir", help="Dir for saving logs and models.") parser.add_argument("--checkpoint", default="", help="Checkpoint path.") parser.add_argument('--local_rank', type=int, default=os.getenv('LOCAL_RANK', 0)) parser.add_argument('--single_gpu', action='store_true') parser.add_argument("--resolution", default=512, type=int, help="Marching cubes resolution") parser.add_argument("--block_res", default=64, type=int, help="Block-wise resolution for marching cubes") parser.add_argument("--output_file", default="mesh.ply", type=str, help="Output file name") args, cfg_cmd = parser.parse_known_args() return args, cfg_cmd def main(): args, cfg_cmd = parse_args() set_affinity(args.local_rank) cfg = Config(args.config) cfg_cmd = parse_cmdline_arguments(cfg_cmd) recursive_update_strict(cfg, cfg_cmd) # If args.single_gpu is set to True, we will disable distributed data parallel. if not args.single_gpu: # this disables nccl timeout os.environ["NCLL_BLOCKING_WAIT"] = "0" os.environ["NCCL_ASYNC_ERROR_HANDLING"] = "0" cfg.local_rank = args.local_rank init_dist(cfg.local_rank, rank=-1, world_size=-1) print(f"Running mesh extraction with {get_world_size()} GPUs.") cfg.logdir = init_logging(args.config, args.logdir, makedir=True) # Initialize data loaders and models. trainer = get_trainer(cfg, is_inference=True, seed=0) # Load checkpoint. trainer.checkpointer.load(args.checkpoint, load_opt=False, load_sch=False) trainer.model.eval() # Set the coarse-to-fine levels. trainer.current_iteration = cfg.max_iter if cfg.model.object.sdf.encoding.coarse2fine.enabled: trainer.model_module.neural_sdf.set_active_levels(trainer.current_iteration) if cfg.model.object.sdf.gradient.mode == "numerical": trainer.model_module.neural_sdf.set_normal_epsilon() meta_fname = f"{cfg.data.root}/transforms.json" with open(meta_fname) as file: meta = json.load(file) if "aabb_range" in meta: bounds = (np.array(meta["aabb_range"]) - np.array(meta["sphere_center"])[..., None]) / meta["sphere_radius"] else: bounds = np.array([[-1.0, 1.0], [-1.0, 1.0], [-1.0, 1.0]]) mesh = extract_mesh(sdf_func=lambda x: -trainer.model_module.neural_sdf.sdf(x), bounds=bounds, intv=(2.0 / args.resolution), block_res=args.block_res) if is_master(): print(f"vertices: {len(mesh.vertices)}") print(f"faces: {len(mesh.faces)}") # center and scale mesh.vertices = mesh.vertices * meta["sphere_radius"] + np.array(meta["sphere_center"]) mesh.export(args.output_file) if __name__ == "__main__": main()