bdck commited on
Commit
7d4bd6d
Β·
verified Β·
1 Parent(s): 2c7d8e8

add comprehensive README

Browse files
Files changed (1) hide show
  1. README.md +183 -0
README.md ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Point2Mesh β€” A Self-Prior for Deformable Meshes
2
+
3
+ Pure Python/PyTorch reimplementation of **[Point2Mesh (SIGGRAPH 2020)](https://arxiv.org/abs/2005.11084)** by Hanocka et al.
4
+
5
+ **Input:** a point cloud (`.ply`, `.pcd`, `.xyz`, `.obj`)
6
+ **Output:** a shrink-wrapped triangle mesh (`.obj`, `.ply`, `.stl`)
7
+
8
+ No training data needed β€” the method optimises a single CNN per shape at inference time, exploiting the network's architectural bias toward self-similar structure as a shape prior.
9
+
10
+ ---
11
+
12
+ ## Quick Start
13
+
14
+ ```bash
15
+ # Install
16
+ pip install torch numpy scipy
17
+ git clone https://huggingface.co/bdck/point2mesh
18
+ cd point2mesh
19
+
20
+ # Run
21
+ python -m point2mesh --input my_cloud.ply --output mesh.obj
22
+
23
+ # Quick test (fast, lower quality)
24
+ python -m point2mesh -i cloud.ply -o mesh.obj --n-levels 2 --iters 200 --init-faces 500
25
+
26
+ # Full quality
27
+ python -m point2mesh -i cloud.ply -o mesh.obj --n-levels 5 --iters 1500 --max-faces 40000 --device cuda
28
+ ```
29
+
30
+ ## How It Works
31
+
32
+ ```
33
+ Point Cloud ──→ Convex Hull ──→ [ CNN optimisation ] ──→ Shrink-wrapped Mesh
34
+ (coarse) (coarse-to-fine) (detailed)
35
+ ```
36
+
37
+ 1. **Initialise** a coarse mesh from the convex hull of the input points
38
+ 2. **Optimise** a MeshCNN U-Net to deform the mesh surface toward the point cloud:
39
+ - The CNN input is fixed random noise (not the geometry)
40
+ - The CNN outputs per-vertex displacements
41
+ - Losses: bidirectional Chamfer distance + beam-gap loss + normal alignment
42
+ 3. **Remesh** (subdivide + decimate) and repeat at finer resolution
43
+ 4. **Export** the final mesh
44
+
45
+ The key insight is the **self-prior**: the CNN architecture itself acts as a regulariser, preferring coherent, self-similar deformations over noise. No external training data is used.
46
+
47
+ ## CLI Reference
48
+
49
+ ```
50
+ python -m point2mesh [OPTIONS]
51
+
52
+ Required:
53
+ --input, -i Input point cloud (.ply, .pcd, .xyz, .obj)
54
+ --output, -o Output mesh (.obj, .ply, .stl)
55
+
56
+ Optimisation:
57
+ --n-levels Coarse-to-fine levels (default: 4)
58
+ --iters Iterations per level (default: 1000)
59
+ --lr Learning rate (default: 0.0002)
60
+ --samples-start Surface samples at iter 0 (default: 15000)
61
+ --samples-end Surface samples at final iter (default: 50000)
62
+
63
+ Mesh resolution:
64
+ --init-faces Initial mesh face count (default: 2000)
65
+ --face-growth Face multiplier between levels (default: 1.5)
66
+ --max-faces Stop subdividing above this (default: 20000)
67
+
68
+ Loss weights:
69
+ --lambda-beam Beam-gap loss weight (default: 1.0)
70
+ --lambda-normal Normal alignment weight (default: 0.1)
71
+ --beam-epsilon Beam cylinder radius (default: 0.5)
72
+
73
+ Network:
74
+ --in-channels Random input features per edge (default: 6)
75
+ --enc-channels Encoder widths (default: 64 128 256 256)
76
+
77
+ Memory:
78
+ --part-threshold Use PartMesh above this face count (default: 10000)
79
+ --n-parts Spatial grid res for PartMesh (default: 2)
80
+
81
+ Output:
82
+ --device torch device (auto-detect if omitted)
83
+ --save-intermediates Save mesh after each level
84
+ --output-dir Directory for intermediates (default: .)
85
+ --log-every Print loss every N iters (default: 50)
86
+ --verbose, -v Debug logging
87
+ ```
88
+
89
+ ## Python API
90
+
91
+ ```python
92
+ from point2mesh.optimize import run_point2mesh, Point2MeshConfig
93
+
94
+ cfg = Point2MeshConfig(
95
+ n_levels=4,
96
+ iters_per_level=1000,
97
+ init_faces=2000,
98
+ max_faces=20000,
99
+ device="cuda",
100
+ )
101
+
102
+ run_point2mesh("cloud.ply", "mesh.obj", cfg)
103
+ ```
104
+
105
+ ### With progress callback
106
+
107
+ ```python
108
+ def on_progress(level, iteration, loss):
109
+ print(f"Level {level}, iter {iteration}: loss = {loss:.6f}")
110
+
111
+ run_point2mesh("cloud.ply", "mesh.obj", cfg, progress_callback=on_progress)
112
+ ```
113
+
114
+ ## Architecture
115
+
116
+ ```
117
+ point2mesh/
118
+ β”œβ”€β”€ __init__.py # Package root
119
+ β”œβ”€β”€ __main__.py # CLI entry point
120
+ β”œβ”€β”€ mesh.py # Mesh data structure + edge topology + PartMesh
121
+ β”œβ”€β”€ layers.py # MeshCNN conv / pool / unpool
122
+ β”œβ”€β”€ network.py # Point2Mesh U-Net (encoder-decoder)
123
+ β”œβ”€β”€ losses.py # Chamfer, beam-gap, normal alignment, surface sampling
124
+ β”œβ”€β”€ optimize.py # Main optimisation loop
125
+ └── io_utils.py # PCD/PLY/XYZ/OBJ loaders, mesh exporters, remeshing
126
+ ```
127
+
128
+ ### Module Details
129
+
130
+ | Module | Description |
131
+ |--------|-------------|
132
+ | `mesh.py` | Half-edge-style mesh with GEMM adjacency for MeshCNN. Builds edge→4-neighbor topology. `PartMesh` splits large meshes into spatial sub-grids. |
133
+ | `layers.py` | **MeshConv**: edge convolution with symmetric neighbor aggregation `[e, \|aβˆ’c\|, a+c, \|bβˆ’d\|, b+d]`. **MeshPool**: edge collapse by L2-norm priority. **MeshUnpool**: topology restoration from stored history. |
134
+ | `network.py` | U-Net encoder-decoder on edges. Input: fixed random noise. Output: per-edge vertex displacements `[N_e, 2, 3]`. Output head initialised to zero (no initial displacement). |
135
+ | `losses.py` | Bidirectional Chamfer distance (batched for large clouds). Beam-gap loss with Ξ΅-cylinder and mutual k-NN skip. Unoriented normal alignment `1 βˆ’ \|n₁·nβ‚‚\|`. Differentiable area-weighted surface sampling. |
136
+ | `optimize.py` | Full coarse-to-fine loop. Re-initialises network + noise each level. Linear sample-count ramp. Remeshing (subdivide β†’ smooth β†’ decimate) between levels. |
137
+ | `io_utils.py` | Zero-dependency PCD/PLY/XYZ/OBJ loaders (binary + ASCII). OBJ/PLY/STL mesh writers. Convex hull initialisation. PCA-based normal estimation. Midpoint subdivision, Laplacian smoothing, greedy edge-collapse decimation. |
138
+
139
+ ## Dependencies
140
+
141
+ Only **three** packages:
142
+
143
+ - `torch >= 2.0` β€” autograd, GPU acceleration
144
+ - `numpy >= 1.24` β€” array operations
145
+ - `scipy >= 1.10` β€” convex hull, KD-tree for normal estimation
146
+
147
+ No Open3D, no PyTorch3D, no trimesh, no pymeshlab.
148
+
149
+ ## Performance Tips
150
+
151
+ | Scenario | Recommendation |
152
+ |----------|---------------|
153
+ | Quick preview | `--n-levels 2 --iters 200 --init-faces 500` |
154
+ | Standard quality | Default settings (4 levels, 1000 iters) |
155
+ | High quality | `--n-levels 5 --iters 1500 --max-faces 40000` |
156
+ | Large point clouds (>100k pts) | Use GPU (`--device cuda`) |
157
+ | High-res meshes (>10k faces) | PartMesh auto-activates; tune `--n-parts 3` if OOM |
158
+ | CPU only | Works, but ~10Γ— slower than GPU |
159
+
160
+ ## Differences from Original Implementation
161
+
162
+ | Aspect | Original | This reimplementation |
163
+ |--------|----------|----------------------|
164
+ | Remeshing | RWM (Robust Watertight Manifold, external C++ binary) | Midpoint subdivision + Laplacian smooth + greedy decimation |
165
+ | Mesh pooling | Full half-edge data structure with manifold guards | Simplified edge collapse with adjacency redirect |
166
+ | Dependencies | PyTorch, Open3D, numpy, scipy, CUDA ops | PyTorch, numpy, scipy only |
167
+ | Initial mesh (genus > 0) | Alpha shape β†’ coarse RWM | Convex hull (genus-0 assumption) |
168
+
169
+ The main simplification is the remeshing step: the original uses the external [Manifold](https://github.com/hjwdzh/Manifold) binary for guaranteed watertight, non-self-intersecting output between levels. This reimplementation uses pure-Python subdivision + decimation which works well for most shapes but may produce self-intersections on complex topology.
170
+
171
+ ## Citation
172
+
173
+ ```bibtex
174
+ @article{hanocka2020point2mesh,
175
+ title = {Point2Mesh: A Self-Prior for Deformable Meshes},
176
+ author = {Hanocka, Rana and Metzer, Gal and Giryes, Raja and Cohen-Or, Daniel},
177
+ journal = {ACM Transactions on Graphics (TOG)},
178
+ volume = {39},
179
+ number = {4},
180
+ year = {2020},
181
+ publisher = {ACM}
182
+ }
183
+ ```