DeepSDFStruct 1.7.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. DeepSDFStruct/SDF.py +1734 -0
  2. DeepSDFStruct/__init__.py +66 -0
  3. DeepSDFStruct/deep_sdf/__init__.py +69 -0
  4. DeepSDFStruct/deep_sdf/create_screenshots_from_plyfiles.py +65 -0
  5. DeepSDFStruct/deep_sdf/data.py +258 -0
  6. DeepSDFStruct/deep_sdf/metrics/__init__.py +14 -0
  7. DeepSDFStruct/deep_sdf/metrics/mesh_to_analytical.py +24 -0
  8. DeepSDFStruct/deep_sdf/models.py +197 -0
  9. DeepSDFStruct/deep_sdf/networks/__init__.py +8 -0
  10. DeepSDFStruct/deep_sdf/networks/analytic_round_cross.py +105 -0
  11. DeepSDFStruct/deep_sdf/networks/deep_sdf_decoder.py +155 -0
  12. DeepSDFStruct/deep_sdf/networks/hierarchical_deep_sdf_decoder.py +139 -0
  13. DeepSDFStruct/deep_sdf/networks/hierarchical_positional_sdf_decoder.py +190 -0
  14. DeepSDFStruct/deep_sdf/networks/resnet_positional_sdf_decoder.py +108 -0
  15. DeepSDFStruct/deep_sdf/nn_utils.py +70 -0
  16. DeepSDFStruct/deep_sdf/plotting.py +143 -0
  17. DeepSDFStruct/deep_sdf/reconstruction.py +165 -0
  18. DeepSDFStruct/deep_sdf/training.py +840 -0
  19. DeepSDFStruct/deep_sdf/workspace.py +363 -0
  20. DeepSDFStruct/design_of_experiments.py +235 -0
  21. DeepSDFStruct/flexicubes/__init__.py +15 -0
  22. DeepSDFStruct/flexicubes/flexicubes.py +1005 -0
  23. DeepSDFStruct/flexicubes/tables.py +2338 -0
  24. DeepSDFStruct/flexisquares/__init__.py +15 -0
  25. DeepSDFStruct/flexisquares/flexisquares.py +967 -0
  26. DeepSDFStruct/flexisquares/tables.py +63 -0
  27. DeepSDFStruct/lattice_structure.py +254 -0
  28. DeepSDFStruct/local_shapes.py +226 -0
  29. DeepSDFStruct/mesh.py +1184 -0
  30. DeepSDFStruct/optimization.py +357 -0
  31. DeepSDFStruct/parametrization.py +215 -0
  32. DeepSDFStruct/pretrained_models.py +128 -0
  33. DeepSDFStruct/sampling.py +670 -0
  34. DeepSDFStruct/sdf_operations.py +990 -0
  35. DeepSDFStruct/sdf_primitives.py +1416 -0
  36. DeepSDFStruct/splinepy_unitcells/__init__.py +35 -0
  37. DeepSDFStruct/splinepy_unitcells/chi_3D.py +1617 -0
  38. DeepSDFStruct/splinepy_unitcells/cross_lattice.py +165 -0
  39. DeepSDFStruct/splinepy_unitcells/double_lattice_extruded.py +298 -0
  40. DeepSDFStruct/splinepy_unitcells/snappy_3d.py +544 -0
  41. DeepSDFStruct/torch_spline.py +446 -0
  42. DeepSDFStruct/trained_models/analytic_round_cross/LatentCodes/latest.pth +0 -0
  43. DeepSDFStruct/trained_models/analytic_round_cross/ModelParameters/latest.pth +0 -0
  44. DeepSDFStruct/trained_models/analytic_round_cross/OptimizerParameters/latest.pth +0 -0
  45. DeepSDFStruct/trained_models/analytic_round_cross/specs.json +45 -0
  46. DeepSDFStruct/trained_models/chi_and_cross/LatentCodes/latest.pth +0 -0
  47. DeepSDFStruct/trained_models/chi_and_cross/Logs.pth +0 -0
  48. DeepSDFStruct/trained_models/chi_and_cross/ModelParameters/latest.pth +0 -0
  49. DeepSDFStruct/trained_models/chi_and_cross/OptimizerParameters/latest.pth +0 -0
  50. DeepSDFStruct/trained_models/chi_and_cross/specs.json +76 -0
  51. DeepSDFStruct/trained_models/primitives/LatentCodes/latest.pth +0 -0
  52. DeepSDFStruct/trained_models/primitives/ModelParameters/latest.pth +0 -0
  53. DeepSDFStruct/trained_models/primitives/OptimizerParameters/latest.pth +0 -0
  54. DeepSDFStruct/trained_models/primitives/specs.json +46 -0
  55. DeepSDFStruct/trained_models/primitives_2d/LatentCodes/latest.pth +0 -0
  56. DeepSDFStruct/trained_models/primitives_2d/ModelParameters/latest.pth +0 -0
  57. DeepSDFStruct/trained_models/primitives_2d/OptimizerParameters/latest.pth +0 -0
  58. DeepSDFStruct/trained_models/primitives_2d/specs.json +46 -0
  59. DeepSDFStruct/trained_models/round_cross/LatentCodes/latest.pth +0 -0
  60. DeepSDFStruct/trained_models/round_cross/Logs.pth +0 -0
  61. DeepSDFStruct/trained_models/round_cross/ModelParameters/latest.pth +0 -0
  62. DeepSDFStruct/trained_models/round_cross/OptimizerParameters/latest.pth +0 -0
  63. DeepSDFStruct/trained_models/round_cross/specs.json +45 -0
  64. DeepSDFStruct/trained_models/test_experiment/LatentCodes/latent_code_data_map.json +106 -0
  65. DeepSDFStruct/trained_models/test_experiment/specs.json +45 -0
  66. DeepSDFStruct/trained_models/test_experiment/training_summary.json +10 -0
  67. DeepSDFStruct/trained_models/test_experiment_hierarchical/LatentCodes/latent_code_data_map.json +106 -0
  68. DeepSDFStruct/trained_models/test_experiment_hierarchical/specs.json +45 -0
  69. DeepSDFStruct/trained_models/test_experiment_hierarchical/training_summary.json +10 -0
  70. DeepSDFStruct/trained_models/test_experiment_homogenization/LatentCodes/latent_code_data_map.json +5006 -0
  71. DeepSDFStruct/trained_models/test_experiment_homogenization/LatentCodes/latest.pth +0 -0
  72. DeepSDFStruct/trained_models/test_experiment_homogenization/Logs.png +0 -0
  73. DeepSDFStruct/trained_models/test_experiment_homogenization/ModelParameters/latest.pth +0 -0
  74. DeepSDFStruct/trained_models/test_experiment_homogenization/OptimizerParameters/latest.pth +0 -0
  75. DeepSDFStruct/trained_models/test_experiment_homogenization/specs.json +83 -0
  76. DeepSDFStruct/trained_models/test_experiment_homogenization/training_summary.json +10 -0
  77. DeepSDFStruct/utils.py +96 -0
  78. DeepSDFStruct/visualization/latent_viewer.py +211 -0
  79. DeepSDFStruct/visualization/latent_viewer_cli.py +8 -0
  80. benchmarks/generate_sdf_showcase.py +622 -0
  81. benchmarks/sdf_from_mesh_benchmark.py +52 -0
  82. benchmarks/sdf_from_mesh_comparison.md +33 -0
  83. benchmarks/sdf_showcase/operations/boolean/difference_sphere_box.png +0 -0
  84. benchmarks/sdf_showcase/operations/boolean/union_sphere_box.png +0 -0
  85. benchmarks/sdf_showcase/operations/transformations/circular_array_sphere.png +0 -0
  86. benchmarks/sdf_showcase/operations/transformations/dilate_sphere.png +0 -0
  87. benchmarks/sdf_showcase/operations/transformations/mirror_sphere.png +0 -0
  88. benchmarks/sdf_showcase/operations/transformations/repeat_sphere.png +0 -0
  89. benchmarks/sdf_showcase/operations/transformations/revolve_circle.png +0 -0
  90. benchmarks/sdf_showcase/operations/transformations/shell_sphere.png +0 -0
  91. benchmarks/sdf_showcase/operations/transformations/twist_torus.png +0 -0
  92. benchmarks/sdf_showcase/primitives/2D/circle.png +0 -0
  93. benchmarks/sdf_showcase/primitives/2D/equilateral_triangle.png +0 -0
  94. benchmarks/sdf_showcase/primitives/2D/hexagon.png +0 -0
  95. benchmarks/sdf_showcase/primitives/2D/polygon_pentagon.png +0 -0
  96. benchmarks/sdf_showcase/primitives/2D/rectangle.png +0 -0
  97. benchmarks/sdf_showcase/primitives/2D/rounded_rectangle.png +0 -0
  98. benchmarks/sdf_showcase/primitives/3D/box.png +0 -0
  99. benchmarks/sdf_showcase/primitives/3D/capped_cone.png +0 -0
  100. benchmarks/sdf_showcase/primitives/3D/capped_cylinder.png +0 -0
  101. benchmarks/sdf_showcase/primitives/3D/capsule.png +0 -0
  102. benchmarks/sdf_showcase/primitives/3D/cone.png +0 -0
  103. benchmarks/sdf_showcase/primitives/3D/corner_spheres.png +0 -0
  104. benchmarks/sdf_showcase/primitives/3D/cross_ms.png +0 -0
  105. benchmarks/sdf_showcase/primitives/3D/cylinder.png +0 -0
  106. benchmarks/sdf_showcase/primitives/3D/dodecahedron.png +0 -0
  107. benchmarks/sdf_showcase/primitives/3D/ellipsoid.png +0 -0
  108. benchmarks/sdf_showcase/primitives/3D/icosahedron.png +0 -0
  109. benchmarks/sdf_showcase/primitives/3D/octahedron.png +0 -0
  110. benchmarks/sdf_showcase/primitives/3D/pyramid.png +0 -0
  111. benchmarks/sdf_showcase/primitives/3D/rounded_box.png +0 -0
  112. benchmarks/sdf_showcase/primitives/3D/rounded_cone.png +0 -0
  113. benchmarks/sdf_showcase/primitives/3D/rounded_cylinder.png +0 -0
  114. benchmarks/sdf_showcase/primitives/3D/sphere.png +0 -0
  115. benchmarks/sdf_showcase/primitives/3D/tetrahedron.png +0 -0
  116. benchmarks/sdf_showcase/primitives/3D/torus.png +0 -0
  117. benchmarks/sdf_showcase/primitives/3D/wireframe_box.png +0 -0
  118. deepsdfstruct-1.7.2.dist-info/METADATA +44 -0
  119. deepsdfstruct-1.7.2.dist-info/RECORD +165 -0
  120. deepsdfstruct-1.7.2.dist-info/WHEEL +5 -0
  121. deepsdfstruct-1.7.2.dist-info/licenses/LICENSE +208 -0
  122. deepsdfstruct-1.7.2.dist-info/licenses/NOTICE +67 -0
  123. deepsdfstruct-1.7.2.dist-info/scm_file_list.json +178 -0
  124. deepsdfstruct-1.7.2.dist-info/scm_version.json +8 -0
  125. deepsdfstruct-1.7.2.dist-info/top_level.txt +4 -0
  126. docs/Makefile +20 -0
  127. docs/api_reference.rst +6 -0
  128. docs/conf.py +60 -0
  129. docs/index.rst +136 -0
  130. docs/make.bat +35 -0
  131. docs/qr_code_github.png +0 -0
  132. docs/qr_code_paper.png +0 -0
  133. docs/readme_images/example_output_01.png +0 -0
  134. docs/readme_images/example_output_02.png +0 -0
  135. docs/readme_images/example_output_03.png +0 -0
  136. docs/readme_images/example_output_04.png +0 -0
  137. tests/data/chairs/1005.obj +282 -0
  138. tests/data/chairs/1024.obj +222 -0
  139. tests/data/chairs/README.md +1 -0
  140. tests/data/circular_balls_test_case.stl +0 -0
  141. tests/data/cone.stl +1794 -0
  142. tests/data/example_disconnectd_mesh.inp +1784 -0
  143. tests/data/example_line_mesh.vtk +0 -0
  144. tests/data/stanford_bunny.stl +0 -0
  145. tests/data/sweep_test_case.stl +0 -0
  146. tests/test_DOE.py +49 -0
  147. tests/test_elongate_sdf.py +195 -0
  148. tests/test_flexisquares.py +187 -0
  149. tests/test_generate_dataset.py +76 -0
  150. tests/test_lattice_evaluation.py +137 -0
  151. tests/test_mesh_export.py +137 -0
  152. tests/test_mesh_functions.py +49 -0
  153. tests/test_networks.py +71 -0
  154. tests/test_pretrained_models.py +40 -0
  155. tests/test_reconstruction.py +101 -0
  156. tests/test_sdf_from_mesh.py +52 -0
  157. tests/test_sdf_functions.py +117 -0
  158. tests/test_sdf_primitives.py +668 -0
  159. tests/test_sdf_trimesh_comparison.py +482 -0
  160. tests/test_splinepy_unitcells.py +64 -0
  161. tests/test_structural_optimization.py +178 -0
  162. tests/test_torch_spline.py +216 -0
  163. tests/test_train_model.py +110 -0
  164. tests/test_training_data_ids.py +41 -0
  165. tests/tmp_outputs/temp_file.txt +0 -0
@@ -0,0 +1,66 @@
1
+ """
2
+ DeepSDFStruct - Differentiable Framework for 3D Microstructured Materials
3
+ ===========================================================================
4
+
5
+ DeepSDFStruct is a comprehensive library for creating, manipulating, and optimizing
6
+ 3D microstructured geometries using Signed Distance Functions (SDFs) and spline-based
7
+ lattices. The framework integrates classical geometric representations with deep
8
+ learning approaches, enabling differentiable design and optimization workflows.
9
+
10
+ Key Components
11
+ --------------
12
+
13
+ SDF Representations
14
+ - ``DeepSDFStruct.SDF``: Abstract base class and core SDF utilities
15
+ - ``DeepSDFStruct.sdf_primitives``: Geometric primitives (spheres, cylinders, etc.)
16
+ - ``DeepSDFStruct.lattice_structure``: Periodic lattice microstructures
17
+
18
+ Mesh Operations
19
+ - ``DeepSDFStruct.mesh``: Mesh generation, processing, and export
20
+ - ``DeepSDFStruct.flexicubes``: Advanced dual contouring (3D)
21
+ - ``DeepSDFStruct.flexisquares``: Dual contouring for 2D cross-sections
22
+
23
+ Deep Learning
24
+ - ``DeepSDFStruct.deep_sdf``: Neural network models and training
25
+ - ``DeepSDFStruct.sampling``: Data generation and sampling strategies
26
+
27
+ Optimization
28
+ - ``DeepSDFStruct.optimization``: MMA and gradient-based optimization
29
+ - ``DeepSDFStruct.parametrization``: Spatially-varying parameter functions
30
+
31
+ Utilities
32
+ - ``DeepSDFStruct.torch_spline``: Differentiable B-spline operations
33
+ - ``DeepSDFStruct.plotting``: Visualization tools
34
+ - ``DeepSDFStruct.utils``: General utility functions
35
+
36
+ Examples
37
+ --------
38
+ Create a simple sphere and generate a mesh::
39
+
40
+ from DeepSDFStruct.sdf_primitives import SphereSDF
41
+ from DeepSDFStruct.mesh import create_3D_mesh
42
+
43
+ sphere = SphereSDF(center=[0, 0, 0], radius=0.5)
44
+ mesh = create_3D_mesh(sphere, resolution=64)
45
+
46
+ Create a lattice structure::
47
+
48
+ from DeepSDFStruct.lattice_structure import LatticeSDFStruct
49
+ from DeepSDFStruct.torch_spline import TorchSpline
50
+
51
+ # Define deformation spline and unit cell
52
+ # ... (see documentation for details)
53
+
54
+ lattice = LatticeSDFStruct(
55
+ tiling=[3, 3, 3],
56
+ microtile=unit_cell
57
+ )
58
+
59
+ For comprehensive examples, see the example notebook in the repository.
60
+ """
61
+
62
+ import DeepSDFStruct.utils
63
+
64
+ DeepSDFStruct.utils.configure_logging()
65
+
66
+ __author__ = "Michael Kofler"
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env python3
2
+ # Copyright 2004-present Facebook. All Rights Reserved.
3
+
4
+ """
5
+ Deep Learning for Signed Distance Functions (DeepSDF)
6
+ ======================================================
7
+
8
+ This submodule implements the DeepSDF approach for learning implicit neural
9
+ representations of 3D geometry. It provides complete workflows for:
10
+
11
+ - Training neural networks to represent geometry as learned SDFs
12
+ - Generating datasets from explicit geometry
13
+ - Reconstructing shapes from latent codes
14
+ - Optimizing latent codes for shape fitting
15
+
16
+ The implementation is based on "DeepSDF: Learning Continuous Signed Distance
17
+ Functions for Shape Representation" (Park et al., CVPR 2019) with extensions
18
+ for microstructured materials and lattice geometries.
19
+
20
+ Key Components
21
+ --------------
22
+
23
+ models.py
24
+ DeepSDFModel class wrapping trained decoder networks and latent codes.
25
+
26
+ training.py
27
+ Complete training pipeline including loss functions, learning rate
28
+ schedules, and training loops.
29
+
30
+ data.py
31
+ Dataset classes for loading and batching SDF samples.
32
+
33
+ reconstruction.py
34
+ Methods for fitting latent codes to target geometries.
35
+
36
+ workspace.py
37
+ Utilities for managing experiments, checkpoints, and results.
38
+
39
+ networks/
40
+ Neural network architectures (decoders, hierarchical models).
41
+
42
+ Typical Workflow
43
+ ----------------
44
+
45
+ 1. Generate training data from explicit geometries::
46
+
47
+ from DeepSDFStruct.sampling import generate_dataset
48
+ generate_dataset(geometries, output_dir, n_samples=500000)
49
+
50
+ 2. Train a DeepSDF model::
51
+
52
+ from DeepSDFStruct.deep_sdf import training
53
+ training.main(specs) # specs define architecture, training params
54
+
55
+ 3. Use the trained model::
56
+
57
+ from DeepSDFStruct.SDF import SDFfromDeepSDF
58
+ from DeepSDFStruct.deep_sdf.models import DeepSDFModel
59
+
60
+ model = DeepSDFModel(decoder, latent_vectors, device)
61
+ sdf = SDFfromDeepSDF(model, latent_code=latent_vectors[0])
62
+
63
+ 4. Reconstruct new shapes::
64
+
65
+ from DeepSDFStruct.deep_sdf import reconstruction
66
+ latent_code = reconstruction.reconstruct(model, target_sdf)
67
+
68
+ For complete examples, see the example notebook and test files.
69
+ """
@@ -0,0 +1,65 @@
1
+ """
2
+ PLY File Screenshot Generator
3
+ =============================
4
+
5
+ This utility script generates screenshots and animated GIFs from PLY mesh files.
6
+ It uses Vedo for 3D rendering with a fixed camera configuration to ensure
7
+ consistent viewpoints across multiple meshes.
8
+
9
+ Usage
10
+ -----
11
+ From command line::
12
+
13
+ python create_screenshots_from_plyfiles.py /path/to/ply/files
14
+
15
+ The script will:
16
+ 1. Find all .ply files in the specified folder
17
+ 2. Generate a .png screenshot for each mesh
18
+ 3. Create an animated GIF combining all screenshots
19
+
20
+ This is useful for:
21
+ - Visualizing reconstruction sequences
22
+ - Creating animations of optimization progress
23
+ - Generating consistent documentation images
24
+ - Batch processing of mesh files
25
+ """
26
+
27
+ import vedo
28
+ import pathlib
29
+ from natsort import natsorted
30
+ import imageio.v3 as imageio
31
+
32
+ cam = dict(
33
+ position=(3.51484, 3.27242, 4.06787),
34
+ focal_point=(-3.98290e-3, -2.36815e-3, 1.02887e-3),
35
+ viewup=(-0.308731, 0.853375, -0.420043),
36
+ roll=2.37119,
37
+ distance=6.29647,
38
+ clipping_range=(3.00412, 10.4561),
39
+ )
40
+
41
+
42
+ def main(ply_file_folder):
43
+ images = []
44
+ plt = vedo.Plotter(interactive=False)
45
+ for ply_filename in natsorted(pathlib.Path(ply_file_folder).rglob("*.ply")):
46
+ print(f"Processing {ply_filename}")
47
+ mesh = vedo.load(str(ply_filename))
48
+ plt.show(mesh, interactive=False, camera=vedo.camera_from_dict(cam))
49
+ vedo.screenshot(ply_filename.with_suffix(".png").as_posix())
50
+ image = imageio.imread(str(ply_filename.with_suffix(".png")))
51
+ images.append(image)
52
+ plt.clear()
53
+
54
+ plt.close()
55
+ imageio.imwrite(ply_filename.parent / "reconstruction.gif", images, duration=300)
56
+
57
+
58
+ if __name__ == "__main__":
59
+ # argument parser
60
+ import argparse
61
+
62
+ parser = argparse.ArgumentParser(description="Create screenshots from ply files")
63
+ parser.add_argument("ply_file_folder", type=str, help="Folder containing ply files")
64
+ args = parser.parse_args()
65
+ main(args.ply_file_folder)
@@ -0,0 +1,258 @@
1
+ """
2
+ DeepSDF Data Loading and Preprocessing
3
+ ======================================
4
+
5
+ This module provides PyTorch Dataset classes and utilities for loading
6
+ and preprocessing SDF training data. It handles:
7
+
8
+ - Loading sampled SDF values from .npz files
9
+ - Splitting samples into positive and negative regions
10
+ - Batching and shuffling for training
11
+ - Data normalization and clamping
12
+ - Multi-class and multi-dataset support
13
+
14
+ The module is designed to work with the dataset format produced by the
15
+ sampling module, where each geometry is stored as a .npz file containing
16
+ positive and negative SDF samples.
17
+
18
+ Classes
19
+ -------
20
+ SDFSamples
21
+ PyTorch Dataset for loading SDF samples with lazy loading and caching.
22
+
23
+ NoMeshFileError
24
+ Exception raised when a mesh file cannot be found.
25
+
26
+ MultipleMeshFileError
27
+ Exception raised when multiple mesh files exist in a directory.
28
+
29
+ Functions
30
+ ---------
31
+ get_instance_filenames
32
+ Retrieve file paths for all instances in a dataset split.
33
+
34
+ unpack_sdf_samples
35
+ Load and preprocess SDF samples from a .npz file.
36
+
37
+ read_sdf_samples_into_ram
38
+ Load entire dataset into RAM for faster training.
39
+ """
40
+
41
+ #!/usr/bin/env python3
42
+ # Copyright 2004-present Facebook. All Rights Reserved.
43
+
44
+ import glob
45
+ import logging
46
+ import numpy as np
47
+ import os
48
+ import random
49
+ import torch
50
+ import torch.utils.data
51
+
52
+ import DeepSDFStruct.deep_sdf.workspace as ws
53
+
54
+ logger = logging.getLogger(__name__)
55
+
56
+
57
+ def get_instance_filenames(data_source, split):
58
+ npzfiles = []
59
+ for dataset in split:
60
+ for class_name in split[dataset]:
61
+ for instance_name in split[dataset][class_name]:
62
+ instance_filename = os.path.join(
63
+ dataset, class_name, instance_name + ".npz"
64
+ )
65
+ if not os.path.isfile(
66
+ os.path.join(data_source, ws.sdf_samples_subdir, instance_filename)
67
+ ):
68
+ # raise RuntimeError(
69
+ # 'Requested non-existent file "' + instance_filename + "'"
70
+ # )
71
+ logging.warning(
72
+ "Requested non-existent file '{}'".format(instance_filename)
73
+ )
74
+ npzfiles += [instance_filename]
75
+ return npzfiles
76
+
77
+
78
+ class NoMeshFileError(RuntimeError):
79
+ """Raised when a mesh file is not found in a shape directory"""
80
+
81
+ pass
82
+
83
+
84
+ class MultipleMeshFileError(RuntimeError):
85
+ """ "Raised when a there a multiple mesh files in a shape directory"""
86
+
87
+ pass
88
+
89
+
90
+ def find_mesh_in_directory(shape_dir):
91
+ mesh_filenames = list(glob.iglob(shape_dir + "/**/*.obj")) + list(
92
+ glob.iglob(shape_dir + "/*.obj")
93
+ )
94
+ if len(mesh_filenames) == 0:
95
+ raise NoMeshFileError()
96
+ elif len(mesh_filenames) > 1:
97
+ raise MultipleMeshFileError()
98
+ return mesh_filenames[0]
99
+
100
+
101
+ # and also convert to single precision
102
+ # usually one function should only contain one functionality, but here I think
103
+ # it makes sense, because the data is loaded at 3 different positions in the code
104
+ def remove_nans(tensor, geom_dimension):
105
+ tensor_nan = torch.isnan(tensor[:, geom_dimension])
106
+ return tensor[~tensor_nan, :].float()
107
+
108
+
109
+ def read_sdf_samples_into_ram(filename):
110
+ npz = np.load(filename)
111
+ pos_tensor = torch.from_numpy(npz["pos.npy"]).float()
112
+ neg_tensor = torch.from_numpy(npz["neg.npy"]).float()
113
+
114
+ return [pos_tensor, neg_tensor]
115
+
116
+
117
+ def unpack_sdf_samples(filename, geom_dimension, subsample=None):
118
+ npz = np.load(filename)
119
+
120
+ pos_tensor = remove_nans(torch.from_numpy(npz["pos.npy"]), geom_dimension)
121
+ neg_tensor = remove_nans(torch.from_numpy(npz["neg.npy"]), geom_dimension)
122
+
123
+ if subsample is None:
124
+ return torch.cat([pos_tensor, neg_tensor], 0)
125
+
126
+ half = int(subsample / 2)
127
+ pos_len = len(pos_tensor)
128
+ neg_len = len(neg_tensor)
129
+ if pos_len < half:
130
+ neg_len = 2 * half - pos_len
131
+ elif neg_len < half:
132
+ pos_len = 2 * half - neg_len
133
+ else:
134
+ pos_len = neg_len = half
135
+
136
+ use_randperm = True
137
+ if use_randperm:
138
+ random_pos = torch.randperm(len(pos_tensor))[:pos_len]
139
+ random_neg = torch.randperm(len(neg_tensor))[:neg_len]
140
+ else:
141
+ # split the sample into half
142
+ random_pos = (torch.rand(half) * pos_tensor.shape[0]).long()
143
+ random_neg = (torch.rand(half) * neg_tensor.shape[0]).long()
144
+
145
+ sample_pos = torch.index_select(pos_tensor, 0, random_pos)
146
+ sample_neg = torch.index_select(neg_tensor, 0, random_neg)
147
+
148
+ samples = torch.cat([sample_pos, sample_neg], 0)
149
+
150
+ # if len(samples) < 50:
151
+ # print("less than half")
152
+
153
+ return samples
154
+
155
+
156
+ def unpack_sdf_samples_from_ram(data, subsample=None):
157
+ if subsample is None:
158
+ return torch.cat(data, 0)
159
+ pos_tensor = data[0]
160
+ neg_tensor = data[1]
161
+
162
+ # split the sample into half
163
+ half = int(subsample / 2)
164
+
165
+ pos_size = pos_tensor.shape[0]
166
+ neg_size = neg_tensor.shape[0]
167
+
168
+ if pos_size <= half:
169
+ random_pos = (torch.rand(half) * pos_tensor.shape[0]).long()
170
+ sample_pos = torch.index_select(pos_tensor, 0, random_pos)
171
+ else:
172
+ pos_start_ind = random.randint(0, pos_size - half)
173
+ sample_pos = pos_tensor[pos_start_ind : (pos_start_ind + half)]
174
+
175
+ if neg_size <= half:
176
+ random_neg = (torch.rand(half) * neg_tensor.shape[0]).long()
177
+ sample_neg = torch.index_select(neg_tensor, 0, random_neg)
178
+ else:
179
+ neg_start_ind = random.randint(0, neg_size - half)
180
+ sample_neg = neg_tensor[neg_start_ind : (neg_start_ind + half)]
181
+
182
+ samples = torch.cat([sample_pos, sample_neg], 0)
183
+
184
+ return samples
185
+
186
+
187
+ class SDFSamples(torch.utils.data.Dataset):
188
+ def __init__(
189
+ self,
190
+ data_source,
191
+ split,
192
+ subsample,
193
+ geom_dimension,
194
+ load_ram=False,
195
+ print_filename=False,
196
+ num_files=1000000,
197
+ ):
198
+ self.subsample = subsample
199
+ self.geom_dimension = geom_dimension
200
+ self.data_source = data_source
201
+ self.npyfiles = get_instance_filenames(data_source, split)
202
+ self.filenames = []
203
+ logger.debug(
204
+ "using "
205
+ + str(len(self.npyfiles))
206
+ + " shapes from data source "
207
+ + data_source
208
+ )
209
+
210
+ self.load_ram = load_ram
211
+
212
+ self.loaded_mat_properties = []
213
+ if load_ram:
214
+ self.loaded_data = []
215
+ for f in self.npyfiles:
216
+ filename = os.path.join(self.data_source, ws.sdf_samples_subdir, f)
217
+ npz = np.load(filename)
218
+ pos_tensor = remove_nans(
219
+ torch.from_numpy(npz["pos.npy"]), self.geom_dimension
220
+ )
221
+ neg_tensor = remove_nans(
222
+ torch.from_numpy(npz["neg.npy"]), self.geom_dimension
223
+ )
224
+ self.loaded_data.append(
225
+ [
226
+ pos_tensor[torch.randperm(pos_tensor.shape[0])],
227
+ neg_tensor[torch.randperm(neg_tensor.shape[0])],
228
+ ]
229
+ )
230
+ if "C" in npz.keys():
231
+ self.loaded_mat_properties.append(
232
+ torch.from_numpy(npz["C.npy"]).to(torch.float32).reshape(-1)
233
+ )
234
+ self.filenames.append(filename)
235
+
236
+ def __len__(self):
237
+ return len(self.npyfiles)
238
+
239
+ def __getitem__(self, idx):
240
+ filename = os.path.join(
241
+ self.data_source, ws.sdf_samples_subdir, self.npyfiles[idx]
242
+ )
243
+ if len(self.loaded_mat_properties) != 0:
244
+ mat_prop = self.loaded_mat_properties[idx]
245
+ else:
246
+ mat_prop = torch.tensor(0)
247
+ if self.load_ram:
248
+ return (
249
+ unpack_sdf_samples_from_ram(self.loaded_data[idx], self.subsample),
250
+ mat_prop.unsqueeze(0).expand(self.subsample, -1),
251
+ idx,
252
+ )
253
+ else:
254
+ return (
255
+ unpack_sdf_samples(filename, self.geom_dimension, self.subsample),
256
+ mat_prop.unsqueeze(0).expand(self.subsample, -1),
257
+ idx,
258
+ )
@@ -0,0 +1,14 @@
1
+ """
2
+ Geometry Evaluation Metrics
3
+ ===========================
4
+
5
+ This module provides utilities for evaluating the quality of generated
6
+ geometries by comparing them to ground truth analytical SDFs.
7
+
8
+ Functions
9
+ ---------
10
+
11
+ mesh_to_analytical
12
+ Compute SDF values at mesh vertices using an analytical SDF function.
13
+ Useful for quantifying reconstruction accuracy.
14
+ """
@@ -0,0 +1,24 @@
1
+ """
2
+ Mesh-to-Analytical SDF Evaluation
3
+ =================================
4
+
5
+ This module provides functions for evaluating mesh quality by computing
6
+ SDF values at mesh vertices using ground truth analytical SDFs.
7
+
8
+ This is useful for:
9
+ - Quantifying reconstruction accuracy
10
+ - Measuring surface deviation
11
+ - Validating mesh generation algorithms
12
+ """
13
+
14
+ import trimesh
15
+ from DeepSDFStruct.mesh import torchSurfMesh
16
+ from DeepSDFStruct.SDF import SDFBase
17
+
18
+
19
+ def mesh_to_analytical(gt_sdf: SDFBase, gen_mesh: torchSurfMesh) -> float:
20
+ """
21
+ Calculates the SDF for every vertex on the mesh
22
+ """
23
+
24
+ return gt_sdf.forward(gen_mesh.vertices).abs().mean().item()
@@ -0,0 +1,197 @@
1
+ """
2
+ DeepSDF Model Classes
3
+ =====================
4
+
5
+ This module provides the DeepSDFModel class, which wraps a trained decoder
6
+ network and latent vectors for evaluating learned implicit representations.
7
+
8
+ The model can be used to:
9
+ - Decode latent codes to SDF values at query points
10
+ - Export models for deployment (TorchScript/LibTorch)
11
+ - Integrate with gradient-based optimization
12
+
13
+ Classes
14
+ -------
15
+ DeepSDFModel
16
+ Main interface for trained DeepSDF models, combining a decoder network
17
+ with learned latent vectors.
18
+ """
19
+
20
+ import torch
21
+
22
+
23
+ class DeepSDFModel:
24
+ """Wrapper for trained DeepSDF decoder and latent vectors.
25
+
26
+ This class provides a convenient interface for using trained DeepSDF models.
27
+ It combines the decoder network (which maps latent+position to SDF values)
28
+ with the trained latent vectors (which encode different shapes).
29
+
30
+ The model supports both constant latent codes (one code for all query points)
31
+ and spatially-varying codes (different codes per query point), enabling
32
+ flexible shape representation.
33
+
34
+ Parameters
35
+ ----------
36
+ decoder : torch.nn.Module
37
+ The trained decoder network. Should accept input of shape
38
+ (N, latent_dim + 3) and output SDF values of shape (N, 1).
39
+ trained_latent_vectors : torch.Tensor
40
+ Trained latent codes of shape (num_shapes, latent_dim).
41
+ Each row encodes one learned shape.
42
+ device : str or torch.device
43
+ Device for computation ('cpu' or 'cuda').
44
+
45
+ Attributes
46
+ ----------
47
+ _decoder : torch.nn.Module
48
+ The decoder network.
49
+ _trained_latent_vectors : torch.Tensor
50
+ The latent code library.
51
+ device : str or torch.device
52
+ Computation device.
53
+
54
+ Methods
55
+ -------
56
+ _decode_sdf(latent_vec, queries)
57
+ Decode SDF values from a latent code and spatial queries.
58
+ export_libtorch_executable(filename)
59
+ Export model to TorchScript for C++ deployment.
60
+
61
+ Examples
62
+ --------
63
+ >>> import torch
64
+ >>> from DeepSDFStruct.deep_sdf.models import DeepSDFModel
65
+ >>>
66
+ >>> # Assume we have a trained decoder and latent vectors
67
+ >>> # decoder = ...
68
+ >>> # latents = torch.randn(10, 256) # 10 shapes, 256-dim codes
69
+ >>>
70
+ >>> # Create model
71
+ >>> model = DeepSDFModel(decoder, latents, device='cuda')
72
+ >>>
73
+ >>> # Query first shape
74
+ >>> points = torch.rand(1000, 3, device='cuda')
75
+ >>> distances = model._decode_sdf(latents[0], points)
76
+ >>>
77
+ >>> # Use with SDFfromDeepSDF
78
+ >>> from DeepSDFStruct.SDF import SDFfromDeepSDF
79
+ >>> sdf = SDFfromDeepSDF(model, latent_code=latents[0])
80
+ >>> mesh = create_3D_mesh(sdf, N_base=64, mesh_type='surface')
81
+
82
+ Notes
83
+ -----
84
+ The decoder architecture typically consists of multiple fully-connected
85
+ layers with skip connections. See networks/ for architecture definitions.
86
+
87
+ References
88
+ ----------
89
+ .. [1] Park, J. J., Florence, P., Straub, J., Newcombe, R., & Lovegrove, S.
90
+ (2019). DeepSDF: Learning continuous signed distance functions for
91
+ shape representation. In CVPR.
92
+ """
93
+
94
+ def __init__(
95
+ self, decoder: torch.nn.Module, trained_latent_vectors: torch.Tensor, device
96
+ ):
97
+ self._decoder = decoder
98
+ self._trained_latent_vectors = trained_latent_vectors
99
+ self.device = device
100
+
101
+ def _decode_sdf(
102
+ self, latent_vec: torch.Tensor, queries: torch.Tensor
103
+ ) -> torch.Tensor:
104
+ """Decode SDF values from latent vector and xyz queries.
105
+
106
+ Combines latent codes with spatial coordinates and passes through
107
+ the decoder to obtain SDF values. Handles both per-query and
108
+ constant latent vectors.
109
+
110
+ Parameters
111
+ ----------
112
+ latent_vec : torch.Tensor
113
+ Latent code(s). Two formats supported:
114
+ - Shape (latent_dim,): Constant code used for all queries
115
+ - Shape (num_samples, latent_dim): Per-query codes
116
+ queries : torch.Tensor
117
+ Query point coordinates of shape (num_samples, 3).
118
+
119
+ Returns
120
+ -------
121
+ torch.Tensor
122
+ SDF values of shape (num_samples, 1).
123
+
124
+ Raises
125
+ ------
126
+ ValueError
127
+ If latent_vec shape doesn't match expected dimensions.
128
+
129
+ Notes
130
+ -----
131
+ If latent_vec is constant (1D), it's expanded to match the number
132
+ of query points. If latent_vec is per-query (2D), it must have
133
+ exactly one code per query point.
134
+ """
135
+ latent_dim = self._trained_latent_vectors[0].shape[0]
136
+ num_samples = queries.shape[0]
137
+ if latent_vec.ndim == 1:
138
+ if latent_vec.shape[0] != latent_dim:
139
+ raise ValueError(
140
+ f"Latent vector shape mismatch: {latent_vec.shape} does"
141
+ f"not align with latent dimension {latent_dim}."
142
+ )
143
+ latent_repeat = latent_vec.expand(-1, num_samples).T
144
+ elif latent_vec.ndim == 2:
145
+ if (latent_vec.shape[0] != num_samples) or (
146
+ latent_vec.shape[1] != latent_dim
147
+ ):
148
+ raise ValueError(
149
+ f"Latent vector shape mismatch: {latent_vec.shape} does"
150
+ f" not align with {num_samples} queries."
151
+ f" Must be of shape ({num_samples}, {latent_dim})"
152
+ )
153
+ latent_repeat = latent_vec
154
+
155
+ model_input = torch.cat([latent_repeat, queries], dim=1)
156
+ return self._decoder(model_input)
157
+
158
+ def export_libtorch_executable(self, filename: str, use_script: bool = True):
159
+ """
160
+ Export the trained decoder model to a TorchScript file for use with LibTorch (C++).
161
+
162
+ Args:
163
+ filename (str): Path where the TorchScript model will be saved (e.g. "decoder.pt").
164
+ use_script (bool): If True, use torch.jit.script (recommended for C++ compatibility).
165
+ If False, use torch.jit.trace (legacy mode, may have control flow issues).
166
+
167
+ Example:
168
+ >>> model.export_libtorch_executable("decoder.pt")
169
+ Example input: tensor([[...]])
170
+ Example Output: tensor([[...]])
171
+ # The file "decoder.pt" is now ready for loading in LibTorch.
172
+ """
173
+ assert isinstance(
174
+ self._trained_latent_vectors, torch.Tensor
175
+ ), "trained_latent_vectors must be a tensor"
176
+ assert (
177
+ self._trained_latent_vectors.shape[0] > 0
178
+ ), "trained_latent_vectors must contain at least one element"
179
+ latent = self._trained_latent_vectors
180
+ example_input = torch.cat(
181
+ [latent[0], torch.tensor([0.0, 0.0, 0.0], device=self.device)]
182
+ ).unsqueeze(0)
183
+
184
+ print("Example input: ", example_input)
185
+ print("Example Output: ", self._decoder(example_input))
186
+
187
+ self._decoder.eval()
188
+
189
+ if use_script:
190
+ print("Exporting with torch.jit.script (recommended)")
191
+ sm = torch.jit.script(self._decoder)
192
+ else:
193
+ print("Exporting with torch.jit.trace (legacy mode)")
194
+ sm = torch.jit.trace(self._decoder, example_input)
195
+
196
+ sm.save(filename)
197
+ print(f"Saved to {filename}")