### Run High-Resolution Reconstruction with FlexiCubes Source: https://context7.com/nv-tlabs/flexicubes/llms.txt Command-line examples demonstrating how to run the `optimize.py` script for high-resolution mesh reconstruction. These examples showcase different configurations, including standard reconstruction, reconstruction with a developability regularizer for fabricable surfaces, and settings for visualization output. ```bash python examples/optimize.py \ --ref_mesh data/inputmodels/david.obj \ --out_dir out/david_hires \ --iter 2000 \ --train_res 2048 2048 \ --voxel_grid_res 128 \ --sdf_regularizer 0.2 ``` ```bash python examples/optimize.py \ --ref_mesh data/inputmodels/david.obj \ --out_dir out/david_dev \ --develop_reg True \ --iter 1250 ``` ```bash python examples/optimize.py \ --ref_mesh data/inputmodels/block.obj \ --out_dir out/block_viz \ --display_res 512 512 \ --save_interval 20 ``` -------------------------------- ### Complete Mesh Reconstruction Optimization Pipeline Source: https://context7.com/nv-tlabs/flexicubes/llms.txt A comprehensive example showcasing the full FlexiCubes optimization pipeline for mesh reconstruction from a reference mesh. It utilizes differentiable rendering with mask and depth losses to optimize SDF and weight parameters, matching a target shape. ```python import torch import numpy as np from flexicubes import FlexiCubes # Initialize FlexiCubes device = "cuda" fc = FlexiCubes(device) voxel_grid_res = 64 # Create voxel grid x_nx3, cube_fx8 = fc.construct_voxel_grid(voxel_grid_res) x_nx3 *= 2 # Scale grid # Initialize learnable parameters sdf = torch.rand_like(x_nx3[:, 0]) - 0.1 # Random initial SDF sdf = torch.nn.Parameter(sdf) # Combined weight tensor: [beta(12) + alpha(8) + gamma(1)] = 21 per cube weight = torch.zeros((cube_fx8.shape[0], 21), dtype=torch.float, device=device) weight = torch.nn.Parameter(weight) # Grid deformation (optional) deform = torch.nn.Parameter(torch.zeros_like(x_nx3)) # Get grid edges for regularization all_edges = cube_fx8[:, fc.cube_edges].reshape(-1, 2) grid_edges = torch.unique(all_edges, dim=0) # Setup optimizer optimizer = torch.optim.Adam([sdf, weight, deform], lr=0.01) # Training loop for iteration in range(1000): optimizer.zero_grad() # Apply deformation to grid vertices grid_verts = x_nx3 + (2 - 1e-8) / (voxel_grid_res * 2) * torch.tanh(deform) # Extract mesh with learnable weights vertices, faces, L_dev = fc( grid_verts, sdf, cube_fx8, voxel_grid_res, beta_fx12=weight[:, :12], alpha_fx8=weight[:, 12:20], gamma_f=weight[:, 20], training=True ) # Compute losses (example - replace with actual rendering losses) # SDF regularizer: encourages smooth SDF transitions sdf_f1x6x2 = sdf[grid_edges.reshape(-1)].reshape(-1, 2) mask = torch.sign(sdf_f1x6x2[..., 0]) != torch.sign(sdf_f1x6x2[..., 1]) sdf_diff = sdf_f1x6x2[mask] sdf_reg = torch.nn.functional.binary_cross_entropy_with_logits( sdf_diff[..., 0], (sdf_diff[..., 1] > 0).float() ) if mask.sum() > 0 else torch.tensor(0.0, device=device) # L_dev regularizer: improves mesh quality in flat regions l_dev_loss = L_dev.mean() * 0.5 # Weight regularizer: prevents extreme weight values weight_reg = weight[:, :20].abs().mean() * 0.1 total_loss = sdf_reg * 0.2 + l_dev_loss + weight_reg total_loss.backward() optimizer.step() if iteration % 100 == 0: print(f"Iter {iteration}: Loss={total_loss.item():.4f}, " f"Verts={vertices.shape[0]}, Faces={faces.shape[0]}") # Final mesh extraction (inference mode) with torch.no_grad(): final_verts, final_faces, _ = fc( grid_verts, sdf, cube_fx8, voxel_grid_res, beta_fx12=weight[:, :12], alpha_fx8=weight[:, 12:20], gamma_f=weight[:, 20], training=False ) # Export mesh import trimesh mesh = trimesh.Trimesh( vertices=final_verts.detach().cpu().numpy(), faces=final_faces.detach().cpu().numpy(), process=False ) mesh.export("output_mesh.obj") ``` -------------------------------- ### SDF Computation Utilities for FlexiCubes Source: https://context7.com/nv-tlabs/flexicubes/llms.txt Python functions for computing Signed Distance Fields (SDF) from mesh surfaces using `kaolin`. Includes `compute_sdf` to get signed distances for given points and `sample_random_points` to generate points for supervision. These utilities are crucial for training and evaluating SDF-based reconstruction models. ```python import torch import kaolin def compute_sdf(points, vertices, faces): """ Compute signed distance from points to mesh surface. Args: points: Query points [N, 3] vertices: Mesh vertices [V, 3] faces: Mesh faces [F, 3] Returns: Signed distance values [N] (negative inside, positive outside) """ face_vertices = kaolin.ops.mesh.index_vertices_by_faces( vertices.unsqueeze(0), faces ) distance = kaolin.metrics.trianglemesh.point_to_mesh_distance( points.unsqueeze(0), face_vertices )[0] with torch.no_grad(): sign = kaolin.ops.mesh.check_sign( vertices.unsqueeze(0), faces, points.unsqueeze(0) ) sign = (sign < 1).float() * 2 - 1 return (sign * distance).squeeze(0) def sample_random_points(n, mesh): """ Sample random points for SDF supervision. Args: n: Number of points to sample mesh: Mesh object with vertices and faces Returns: Points tensor [n, 3] - mix of random and near-surface points """ # Random points in unit cube pts_random = (torch.rand((n // 2, 3), device="cuda") - 0.5) * 2 # Points near surface with small perturbation pts_surface = kaolin.ops.mesh.sample_points( mesh.vertices.unsqueeze(0), mesh.faces, n // 2 )[0].squeeze(0) pts_surface += torch.randn_like(pts_surface) * 0.05 return torch.cat([pts_random, pts_surface]) # Example usage in optimization: from flexicubes import FlexiCubes fc = FlexiCubes(device="cuda") x_nx3, cube_fx8 = fc.construct_voxel_grid(64) x_nx3 *= 2 # Load target mesh gt_mesh = load_mesh("target.obj", device="cuda") # Sample supervision points pts = sample_random_points(1000, gt_mesh) gt_sdf = compute_sdf(pts, gt_mesh.vertices, gt_mesh.faces) # Extract current mesh and compute predicted SDF sdf = torch.nn.Parameter(torch.randn(x_nx3.shape[0], device="cuda")) vertices, faces, _ = fc(x_nx3, sdf, cube_fx8, 64, training=True) pred_sdf = compute_sdf(pts, vertices, faces) ``` -------------------------------- ### Basic Mesh Reconstruction Command Line Script Source: https://context7.com/nv-tlabs/flexicubes/llms.txt This bash command demonstrates the basic usage of the optimization script for mesh reconstruction from a reference 3D model using multiview rendering supervision. It specifies input mesh, output directory, iteration count, batch size, voxel grid resolution, and learning rate. ```bash # Basic mesh reconstruction python examples/optimize.py \ --ref_mesh data/inputmodels/block.obj \ --out_dir out/block \ --iter 1000 \ --batch 8 \ --voxel_grid_res 64 \ --learning_rate 0.01 ``` -------------------------------- ### Set Up Optimizer and Learning Rate Schedule Source: https://github.com/nv-tlabs/flexicubes/blob/main/examples/optimization.ipynb Defines a learning rate schedule with exponential falloff and sets up an Adam optimizer for the SDF, weights, and deformation parameters. The scheduler adjusts the learning rate during optimization. ```python def lr_schedule(iter): return max(0.0, 10 ** (-(iter) * 0.0002)) # Exponential falloff from [1.0, 0.1] over 5k epochs. optimizer = torch.optim.Adam([sdf, weight, deform], lr=learning_rate) scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda x: lr_schedule(x)) ``` -------------------------------- ### Load Mesh and Initialize FlexiCubes (Python) Source: https://github.com/nv-tlabs/flexicubes/blob/main/examples/optimization.ipynb Loads a reference mesh from an OBJ file, normalizes it (centering and scaling), and initializes a FlexiCubes object. It then constructs and scales a voxel grid, initializes SDF values, and sets up learnable weights for optimization. ```python gt_mesh = kal.io.obj.import_mesh('data/inputmodels/block.obj').cuda() vertices = gt_mesh.vertices vmin, vmax = vertices.min(dim=0)[0], vertices.max(dim=0)[0] scale = 1.8 / torch.max(vmax - vmin).item() vertices = vertices - (vmax + vmin) / 2 # Center mesh on origin gt_mesh.vertices = vertices * scale # Rescale to [-0.9, 0.9] fc = kal.ops.conversions.FlexiCubes(device) x_nx3, cube_fx8 = fc.construct_voxel_grid(voxel_grid_res) x_nx3 *= 2 # scale up the grid so that it's larger than the target object sdf = torch.rand_like(x_nx3[:,0]) - 0.1 # randomly initialize SDF sdf = torch.nn.Parameter(sdf.clone().detach(), requires_grad=True) # set per-cube learnable weights to zeros weight = torch.zeros((cube_fx8.shape[0], 21), dtype=torch.float, device='cuda') weight = torch.nn.Parameter(weight.clone().detach(), requires_grad=True) # Retrieve all the edges of the voxel grid; these edges will be utilized to ``` -------------------------------- ### Render and Visualize Initial Mesh Source: https://github.com/nv-tlabs/flexicubes/blob/main/examples/optimization.ipynb Renders the initial mesh and a ground truth mesh using specified camera settings and displays their normals. This helps in visually comparing the initial state with the target. ```python camera = render.get_rotate_camera(0) f, ax = plt.subplots(1, 2) output = render.render_mesh(init_mesh, camera, [512, 512], return_types=['normals']) ax[0].imshow(((output['normals'][0] + 1) / 2.).cpu().detach()) output = render.render_mesh(gt_mesh, camera, [512, 512], return_types=['normals']) ax[1].imshow(((output['normals'][0] + 1) / 2.).cpu().detach())plt.show() ``` -------------------------------- ### Interactive Mesh Visualization with Kaolin (Python) Source: https://github.com/nv-tlabs/flexicubes/blob/main/examples/extraction.ipynb Demonstrates interactive visualization of two meshes using Kaolin's `SplitVisualizer`. This allows users to explore the topology of the meshes by moving the camera and adjusting wireframe properties, providing a deeper understanding of the mesh extraction results. ```python render.SplitVisualizer(mesh_no_grad, mesh_with_grad, 512, 512).show(camera) ``` -------------------------------- ### Show FlexiCubes Training Progress with Timeline Visualizer Source: https://github.com/nv-tlabs/flexicubes/blob/main/examples/optimization.ipynb This Python code snippet utilizes the 'TimelineVisualizer' from the 'render' module to display the training progress of FlexiCubes over intermediate results. It requires a camera, resolution, and the list of intermediate results. The visualizer provides an interactive interface to observe the optimization. ```python render.TimelineVisualizer(intermediate_results, 512, 512).show(camera) ``` -------------------------------- ### Import Packages and Define Hyperparameters (Python) Source: https://github.com/nv-tlabs/flexicubes/blob/main/examples/optimization.ipynb Imports necessary libraries like PyTorch, NumPy, Kaolin, and Matplotlib for mesh optimization. Defines hyperparameters such as iteration count, batch size, training resolution, learning rate, voxel grid resolution, device, and SDF regularizer. ```python import torch import tqdm import numpy as np import kaolin as kal from matplotlib import pyplot as plt import render import loss iter = 1000 batch = 8 train_res = [2048, 2048] learning_rate = 0.01 voxel_grid_res = 64 device = 'cuda' sdf_regularizer = 0.2 ``` -------------------------------- ### Interactive Mesh Visualization Source: https://github.com/nv-tlabs/flexicubes/blob/main/examples/optimization.ipynb Utilizes Kaolin's SplitVisualizer for interactive visualization of two meshes, allowing camera manipulation and wireframe adjustments to inspect mesh topology. ```python render.SplitVisualizer(init_mesh, gt_mesh, 512, 512).show(camera) ``` -------------------------------- ### Output Tetrahedral Mesh with FlexiCubes Source: https://context7.com/nv-tlabs/flexicubes/llms.txt Demonstrates how to configure FlexiCubes to output tetrahedral meshes instead of triangular meshes by setting the `output_tetmesh` parameter to `True`. This is beneficial for physics simulations and volumetric analyses requiring interior structure. ```python import torch from flexicubes import FlexiCubes fc = FlexiCubes(device="cuda") voxel_grid_res = 32 x_nx3, cube_fx8 = fc.construct_voxel_grid(voxel_grid_res) x_nx3 *= 2 # Create SDF for a box sdf = torch.max(torch.abs(x_nx3), dim=-1)[0] - 0.4 # Extract tetrahedral mesh vertices, tets, L_dev = fc( x_nx3, sdf, cube_fx8, voxel_grid_res, output_tetmesh=True, # Output tetrahedra instead of triangles training=False ) print(f"Vertices: {vertices.shape}") # [V, 3] print(f"Tetrahedra: {tets.shape}") # [T, 4] - each row contains 4 vertex indices ``` -------------------------------- ### Mesh Extraction with QEF Solver and Gradient Function Source: https://context7.com/nv-tlabs/flexicubes/llms.txt Illustrates using FlexiCubes with a gradient function (`grad_func`) to leverage a Quadratic Error Function (QEF) solver. This method enhances dual vertex positioning accuracy for extracting meshes from known Signed Distance Functions (SDFs) without requiring optimization. ```python import torch from flexicubes import FlexiCubes fc = FlexiCubes(device="cuda") voxel_grid_res = 64 x_nx3, cube_fx8 = fc.construct_voxel_grid(voxel_grid_res) x_nx3 *= 2 # Define SDF and its gradient for a sphere def sphere_sdf(points): return torch.norm(points, dim=-1) - 0.5 def sphere_gradient(points): """Returns normalized gradient (surface normal direction)""" return torch.nn.functional.normalize(points, dim=-1) sdf = sphere_sdf(x_nx3) # Extract mesh using QEF solver with gradient function vertices, faces, L_dev = fc( x_nx3, sdf, cube_fx8, voxel_grid_res, grad_func=sphere_gradient, # Gradient function for QEF solver training=False ) print(f"High-quality mesh: {vertices.shape[0]} vertices, {faces.shape[0]} faces") ``` -------------------------------- ### Initialize Deformation for Grid Vertices Source: https://github.com/nv-tlabs/flexicubes/blob/main/examples/optimization.ipynb Initializes a deformable parameter and applies it to grid vertices to create a deformed grid. This is a crucial step before running isosurfacing to extract a mesh. ```python deform = torch.nn.Parameter(torch.zeros_like(x_nx3), requires_grad=True) grid_verts = x_nx3 + (2-1e-8) / (voxel_grid_res * 2) * torch.tanh(deform) # apply deformation to the grid vertices ``` -------------------------------- ### Initialize FlexiCubes Class Source: https://context7.com/nv-tlabs/flexicubes/llms.txt Initializes the FlexiCubes system by loading Dual Marching Cubes lookup tables and configuring device-specific tensors. It sets up necessary data structures for surface extraction, including edge tables, cube corner indices, and tetrahedralization lookup tables. The device can be 'cuda' or 'cpu'. ```python import torch from flexicubes import FlexiCubes # Initialize FlexiCubes on CUDA device fc = FlexiCubes(device="cuda", qef_reg_scale=1e-3, weight_scale=0.99) # Parameters: # - device: "cuda" or "cpu" for computation # - qef_reg_scale: Regularization scale for QEF solver (used when grad_func is provided) # - weight_scale: Scale of flexible weights, should be between 0 and 1 ``` -------------------------------- ### Compute Mesh Developability Regularization using PyTorch Source: https://context7.com/nv-tlabs/flexicubes/llms.txt The mesh_developable_reg function implements a developability regularizer to encourage mesh surfaces to be fabricable from flat panels. This is useful for architectural and manufacturing applications. It takes mesh vertices and faces as input and returns per-vertex developability energy. Dependencies include PyTorch and torch_scatter. ```python import torch import torch_scatter from flexicubes import FlexiCubes def mesh_developable_reg(vertices, faces): """ Compute developability regularization for fabricability. Args: vertices: Mesh vertices [V, 3] faces: Mesh faces [F, 3] Returns: Per-vertex developability energy [V] """ device = vertices.device V = vertices.shape[0] F = faces.shape[0] def normalize(vecs): return vecs / (torch.linalg.norm(vecs, dim=-1, keepdim=True) + 1e-6) tri_pos = vertices[faces] vert_normal_covariance_sum = torch.zeros((V, 9), device=device) vert_area = torch.zeros(V, device=device) vert_degree = torch.zeros(V, dtype=torch.int32, device=device) for iC in range(3): pRoot = tri_pos[:, iC, :] pA = tri_pos[:, (iC + 1) % 3, :] pB = tri_pos[:, (iC + 2) % 3, :] vA = normalize(pA - pRoot) vB = normalize(pB - pRoot) area_normal = torch.linalg.cross(pA - pRoot, pB - pRoot, dim=-1) face_area = 0.5 * torch.linalg.norm(area_normal, dim=-1) normal = normalize(area_normal) corner_angle = torch.acos(torch.clamp(torch.sum(vA * vB, dim=-1), -1., 1.)) outer = normal[:, :, None] @ normal[:, None, :] contrib = corner_angle[:, None] * outer.reshape(-1, 9) vert_normal_covariance_sum = torch_scatter.scatter_add( src=contrib, index=faces[:, iC], dim=-2, out=vert_normal_covariance_sum ) vert_area = torch_scatter.scatter_add( src=face_area / 3., index=faces[:, iC], dim=-1, out=vert_area ) vert_degree = torch_scatter.scatter_add( src=torch.ones(F, dtype=torch.int32, device=device), index=faces[:, iC], dim=-1, out=vert_degree ) vert_normal_covariance_sum = vert_normal_covariance_sum.reshape(-1, 3, 3) vert_normal_covariance_sum += torch.eye(3, device=device)[None, :, :] * 1e-6 min_eigvals = torch.min(torch.linalg.eigvals(vert_normal_covariance_sum).abs(), dim=-1).values vert_area = torch.where(vert_degree == 3, torch.tensor(0., device=device), vert_area) vert_area = vert_area * (V / torch.sum(vert_area)) return vert_area * min_eigvals # Usage with FlexiCubes: fc = FlexiCubes(device="cuda") x_nx3, cube_fx8 = fc.construct_voxel_grid(64) x_nx3 *= 2 sdf = torch.norm(x_nx3, dim=-1) - 0.5 vertices, faces, _ = fc(x_nx3, sdf, cube_fx8, 64, training=False) dev_loss = mesh_developable_reg(vertices, faces).mean() print(f"Developability loss: {dev_loss.item():.4f}") ``` -------------------------------- ### Construct Voxel Grid Source: https://context7.com/nv-tlabs/flexicubes/llms.txt Generates a voxel grid based on a specified resolution. This method creates vertex positions and cube corner indices required for the FlexiCubes algorithm. The resulting grid is centered at the origin with unit length dimensions. It supports both uniform and non-uniform resolutions. ```python import torch from flexicubes import FlexiCubes fc = FlexiCubes(device="cuda") # Create a uniform voxel grid with resolution 64x64x64 voxel_grid_res = 64 x_nx3, cube_fx8 = fc.construct_voxel_grid(voxel_grid_res) # x_nx3: Tensor of shape [N, 3] containing vertex coordinates centered at origin # cube_fx8: Tensor of shape [F, 8] containing indices of 8 corners for each cube print(f"Grid vertices shape: {x_nx3.shape}") # [68921, 3] for res=64 print(f"Cubes shape: {cube_fx8.shape}") # [262144, 3] for res=64 # Scale the grid to encompass target object x_nx3 *= 2 # Scale up to [-1, 1] range # For non-uniform resolution: x_nx3_nonuniform, cube_fx8_nonuniform = fc.construct_voxel_grid([32, 64, 48]) ``` -------------------------------- ### Calculate SDF Supervision Loss (PyTorch) Source: https://context7.com/nv-tlabs/flexicubes/llms.txt This snippet demonstrates how to compute the Signed Distance Function (SDF) supervision loss using PyTorch's mean squared error (MSE) loss function. It takes predicted SDF values and ground truth SDF values as input and scales the result by a factor of 2e3. This is a fundamental step in training models that reconstruct or generate 3D meshes. ```python sdf_loss = torch.nn.functional.mse_loss(pred_sdf, gt_sdf) * 2e3 ``` -------------------------------- ### Render and Display Mesh Normals with Matplotlib Source: https://github.com/nv-tlabs/flexicubes/blob/main/examples/optimization.ipynb This Python code snippet renders the last intermediate result of a mesh using a specified camera and resolution. It then displays the computed normals as an image using Matplotlib. Dependencies include the 'render' module and 'matplotlib.pyplot'. ```python camera = render.get_rotate_camera(0) output = render.render_mesh(intermediate_results[-1], camera, [512, 512], return_types=['normals']) plt.imshow(((output['normals'][0] + 1) / 2.).cpu().detach()) ``` -------------------------------- ### FlexiCubes Optimization Loop Source: https://github.com/nv-tlabs/flexicubes/blob/main/examples/optimization.ipynb Executes the main optimization loop, including sampling camera poses, rendering meshes, calculating reconstruction and regularization losses, and updating model parameters. It saves intermediate mesh results periodically. ```python intermediate_results = [init_mesh] for it in tqdm.tqdm(range(iter)): optimizer.zero_grad() # sample random camera poses cameras = render.get_random_camera_batch(batch, iter_res=train_res, device=device) # render gt mesh at sampled views target = render.render_mesh(gt_mesh, cameras, train_res) # extract and render FlexiCubes mesh grid_verts = x_nx3 + (2-1e-8) / (voxel_grid_res * 2) * torch.tanh(deform) vertices, faces, L_dev = fc( grid_verts, sdf, cube_fx8, voxel_grid_res, beta=weight[:,:12], alpha=weight[:,12:20], gamma_f=weight[:,20], training=True) flexicubes_mesh = kal.rep.SurfaceMesh(vertices=vertices, faces=faces) buffers = render.render_mesh(flexicubes_mesh, cameras, train_res) # evaluate reconstruction loss mask_loss = (buffers['mask'] - target['mask']).abs().mean() # mask loss depth_loss = (((((buffers['depth'] - (target['depth']))* target['mask'])**2).sum(-1)+1e-8)).sqrt().mean() * 10 # depth loss # evaluate regularization losses t_iter = it / iter # this is the regularization loss described in Equation 2 of the nvdiffrec paper by Munkberg et al., which serves to remove internal floating elements that are not visible to the user. sdf_weight = sdf_regularizer - (sdf_regularizer - sdf_regularizer/20)*min(1.0, 4.0 * t_iter) reg_loss = loss.sdf_reg_loss(sdf, grid_edges).mean() * sdf_weight reg_loss += L_dev.mean() * 0.5 # L_dev as in Equation 8 of our paper reg_loss += (weight[:,:20]).abs().mean() * 0.1 # regularize weights to be zeros to improve the stability of the optimization process total_loss = mask_loss + depth_loss + reg_loss total_loss.backward() optimizer.step() scheduler.step() if (it + 1) % 20 == 0: # save intermediate results every 100 iters with torch.no_grad(): # run the mesh extraction again with the parameter 'training=False' so that each quadrilateral face is divided into two triangles, as opposed to the four triangles during the training phase. vertices, faces, L_dev = fc( grid_verts, sdf, cube_fx8, voxel_grid_res, beta=weight[:,:12], alpha=weight[:,12:20], gamma_f=weight[:,20], training=False) intermediate_results.append(kal.rep.SurfaceMesh(vertices, faces)) ``` -------------------------------- ### Visualize Meshes with and without Gradients (Python) Source: https://github.com/nv-tlabs/flexicubes/blob/main/examples/extraction.ipynb Visualizes two meshes generated by FlexiCubes: one with gradient information (`mesh_with_grad`) and one without (`mesh_no_grad`). The code uses `matplotlib` and a custom `render` module to display the meshes, illustrating how gradient data improves the reconstruction of sharp features. ```python camera = render.get_rotate_camera(0, iter_res=[512, 512], device=device) f, ax = plt.subplots(1, 2) output = render.render_mesh(mesh_no_grad, camera, [512, 512], return_types=['normals']) ax[0].imshow(((output['normals'][0] + 1) / 2.).cpu()) output = render.render_mesh(mesh_with_grad, camera, [512, 512], return_types=['normals']) ax[1].imshow(((output['normals'][0] + 1) / 2.).cpu()) plt.show() ``` -------------------------------- ### Compute SDF Regularization Loss using PyTorch Source: https://context7.com/nv-tlabs/flexicubes/llms.txt The sdf_reg_loss function calculates a regularization term for Signed Distance Functions (SDFs) to promote smooth transitions across grid edges. This helps in removing internal artifacts and floaters. It takes SDF values and edge information as input and returns a scalar loss. Dependencies include PyTorch. ```python import torch def sdf_reg_loss(sdf, all_edges): """ Compute SDF regularization loss to eliminate internal structures. Args: sdf: SDF values at grid vertices [N] all_edges: Edge vertex indices [E, 2] Returns: Scalar regularization loss """ sdf_f1x6x2 = sdf[all_edges.reshape(-1)].reshape(-1, 2) mask = torch.sign(sdf_f1x6x2[..., 0]) != torch.sign(sdf_f1x6x2[..., 1]) sdf_f1x6x2 = sdf_f1x6x2[mask] sdf_diff = ( torch.nn.functional.binary_cross_entropy_with_logits( sdf_f1x6x2[..., 0], (sdf_f1x6x2[..., 1] > 0).float() ) + torch.nn.functional.binary_cross_entropy_with_logits( sdf_f1x6x2[..., 1], (sdf_f1x6x2[..., 0] > 0).float() ) ) return sdf_diff # Usage in optimization: from flexicubes import FlexiCubes fc = FlexiCubes(device="cuda") x_nx3, cube_fx8 = fc.construct_voxel_grid(64) # Get unique grid edges all_edges = cube_fx8[:, fc.cube_edges].reshape(-1, 2) grid_edges = torch.unique(all_edges, dim=0) # Example SDF sdf = torch.nn.Parameter(torch.randn(x_nx3.shape[0], device="cuda")) # Compute regularization reg_loss = sdf_reg_loss(sdf, grid_edges) print(f"SDF Regularization: {reg_loss.item():.4f}") ``` -------------------------------- ### Calculate Cube SDF and Gradient (Python) Source: https://github.com/nv-tlabs/flexicubes/blob/main/examples/extraction.ipynb Defines two Python functions: `cube_sdf` to compute the Signed Distance Field values for a given set of points relative to a cube, and `cube_sdf_gradient` to calculate the analytic gradient of the SDF. These are essential for mesh extraction algorithms that rely on SDF properties. ```python import torch import kaolin as kal from matplotlib import pyplot as plt import render ``` ```python def cube_sdf(x_nx3): sdf_values = 0.5 - torch.abs(x_nx3) sdf_values = torch.clamp(sdf_values, min=0.0) sdf_values = sdf_values[:, 0] * sdf_values[:, 1] * sdf_values[:, 2] sdf_values = -1.0 * sdf_values return sdf_values def cube_sdf_gradient(x_nx3): gradients = [] for i in range(x_nx3.shape[0]): x, y, z = x_nx3[i] grad_x, grad_y, grad_z = 0, 0, 0 max_val = max(abs(x) - 0.5, abs(y) - 0.5, abs(z) - 0.5) if max_val == abs(x) - 0.5: grad_x = 1.0 if x > 0 else -1.0 if max_val == abs(y) - 0.5: grad_y = 1.0 if y > 0 else -1.0 if max_val == abs(z) - 0.5: grad_z = 1.0 if z > 0 else -1.0 gradients.append(torch.tensor([grad_x, grad_y, grad_z])) return torch.stack(gradients).to(x_nx3.device) ``` -------------------------------- ### FlexiCubes Mesh Extraction (Differentiable and Inference) Source: https://context7.com/nv-tlabs/flexicubes/llms.txt The main mesh extraction function converts discrete signed distance fields to triangle or tetrahedral meshes. It takes SDF values at voxel vertices and optional per-cube weight parameters to produce differentiable mesh output for gradient-based optimization (training=True) or cleaner output for inference (training=False). ```python import torch from flexicubes import FlexiCubes fc = FlexiCubes(device="cuda") voxel_grid_res = 64 x_nx3, cube_fx8 = fc.construct_voxel_grid(voxel_grid_res) x_nx3 *= 2 # Initialize SDF (negative = inside surface, positive = outside) # Example: sphere SDF sdf = torch.norm(x_nx3, dim=-1) - 0.5 # Sphere with radius 0.5 # Initialize learnable weight parameters num_cubes = cube_fx8.shape[0] beta_fx12 = torch.zeros((num_cubes, 12), device="cuda") # Edge weights alpha_fx8 = torch.zeros((num_cubes, 8), device="cuda") # Corner weights gamma_f = torch.zeros((num_cubes,), device="cuda") # Quad split weights # Make parameters learnable sdf = torch.nn.Parameter(sdf) beta_fx12 = torch.nn.Parameter(beta_fx12) alpha_fx8 = torch.nn.Parameter(alpha_fx8) gamma_f = torch.nn.Parameter(gamma_f) # Extract mesh with training mode (differentiable quad splitting) vertices, faces, L_dev = fc( x_nx3, # Vertex positions [N, 3] sdf, # SDF values [N] cube_fx8, # Cube indices [F, 8] voxel_grid_res, # Grid resolution beta_fx12=beta_fx12, alpha_fx8=alpha_fx8, gamma_f=gamma_f, training=True # Enable differentiable quad splitting ) print(f"Vertices: {vertices.shape}") # [V, 3] print(f"Faces: {faces.shape}") # [F, 3] print(f"L_dev regularizer: {L_dev.shape}") # [V] per-vertex regularization # Extract mesh for inference (non-differentiable, cleaner output) vertices_inf, faces_inf, _ = fc( x_nx3, sdf, cube_fx8, voxel_grid_res, beta_fx12=beta_fx12, alpha_fx8=alpha_fx8, gamma_f=gamma_f, training=False ) ``` -------------------------------- ### Mesh Utility Class for FlexiCubes Source: https://context7.com/nv-tlabs/flexicubes/llms.txt Python code defining a `Mesh` utility class and a `load_mesh` function. The `Mesh` class stores vertices and faces and can compute face normals. The `load_mesh` function loads meshes using `trimesh`, normalizes them, and returns a `Mesh` object. This is used for handling mesh data within the FlexiCubes pipeline. ```python import torch import trimesh class Mesh: """Simple mesh container with automatic normal computation.""" def __init__(self, vertices, faces): self.vertices = vertices self.faces = faces def auto_normals(self): """Compute face normals for visualization.""" v0 = self.vertices[self.faces[:, 0], :] v1 = self.vertices[self.faces[:, 1], :] v2 = self.vertices[self.faces[:, 2], :] cross = torch.cross(v1 - v0, v2 - v0) self.nrm = cross / (torch.norm(cross, dim=-1, keepdim=True) + 1e-8) def load_mesh(path, device): """Load and normalize mesh from file.""" mesh_np = trimesh.load(path) vertices = torch.tensor(mesh_np.vertices, device=device, dtype=torch.float) faces = torch.tensor(mesh_np.faces, device=device, dtype=torch.long) # Normalize to [-0.9, 0.9] vmin, vmax = vertices.min(dim=0)[0], vertices.max(dim=0)[0] scale = 1.8 / torch.max(vmax - vmin).item() vertices = (vertices - (vmax + vmin) / 2) * scale return Mesh(vertices, faces) # Usage: from flexicubes import FlexiCubes # Load reference mesh gt_mesh = load_mesh("model.obj", device="cuda") gt_mesh.auto_normals() # Use with FlexiCubes fc = FlexiCubes(device="cuda") x_nx3, cube_fx8 = fc.construct_voxel_grid(64) sdf = torch.randn(x_nx3.shape[0], device="cuda") vertices, faces, _ = fc(x_nx3 * 2, sdf, cube_fx8, 64, training=False) flexicubes_mesh = Mesh(vertices, faces) flexicubes_mesh.auto_normals() ``` -------------------------------- ### Extract Mesh using FlexiCubes (Python) Source: https://github.com/nv-tlabs/flexicubes/blob/main/examples/extraction.ipynb This Python code snippet utilizes the FlexiCubes library to extract surface meshes from a scalar field. It demonstrates mesh generation both with and without providing explicit gradient information to the `fc` operator, highlighting the differences in the resulting meshes. ```python res = 5 device='cuda' fc = kal.ops.conversions.FlexiCubes(device) voxelgrid_vertices, cube_idx = fc.construct_voxel_grid(res) voxelgrid_vertices *= 1.1 # add small margin to boundary scalar_field = cube_sdf(voxelgrid_vertices) mesh_with_grad_v, mesh_with_grad_f, _ = fc( voxelgrid_vertices, scalar_field, cube_idx, res, grad_func=cube_sdf_gradient) mesh_with_grad = kal.rep.SurfaceMesh(vertices=mesh_with_grad_v, faces=mesh_with_grad_f) mesh_no_grad_v, mesh_no_grad_f, _ = fc( voxelgrid_vertices, scalar_field, cube_idx, res) mesh_no_grad = kal.rep.SurfaceMesh(vertices=mesh_no_grad_v, faces=mesh_no_grad_f) ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.