bdck commited on
Commit
d7bea5f
·
verified ·
1 Parent(s): 0b9d51d

add CLI entry point

Browse files
Files changed (1) hide show
  1. point2mesh/__main__.py +138 -0
point2mesh/__main__.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ CLI entry point for Point2Mesh.
3
+
4
+ Usage
5
+ -----
6
+ python -m point2mesh --input cloud.ply --output mesh.obj
7
+
8
+ # With custom parameters:
9
+ python -m point2mesh \
10
+ --input cloud.pcd \
11
+ --output mesh.ply \
12
+ --n-levels 5 \
13
+ --iters 1500 \
14
+ --init-faces 1500 \
15
+ --max-faces 30000 \
16
+ --device cuda
17
+
18
+ # Quick test (fewer iterations, smaller mesh):
19
+ python -m point2mesh \
20
+ --input cloud.ply \
21
+ --output mesh.obj \
22
+ --n-levels 2 \
23
+ --iters 200 \
24
+ --init-faces 500
25
+ """
26
+
27
+ import argparse
28
+ import logging
29
+ import sys
30
+
31
+ from .optimize import run_point2mesh, Point2MeshConfig
32
+
33
+
34
+ def main():
35
+ parser = argparse.ArgumentParser(
36
+ description="Point2Mesh: Shrink-wrap a mesh onto a point cloud.",
37
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
38
+ )
39
+
40
+ # Required
41
+ parser.add_argument(
42
+ "--input", "-i", required=True,
43
+ help="Input point cloud (.ply, .pcd, .xyz, .obj)",
44
+ )
45
+ parser.add_argument(
46
+ "--output", "-o", required=True,
47
+ help="Output mesh file (.obj, .ply, .stl)",
48
+ )
49
+
50
+ # Optimisation
51
+ parser.add_argument("--n-levels", type=int, default=4,
52
+ help="Number of coarse-to-fine levels")
53
+ parser.add_argument("--iters", type=int, default=1000,
54
+ help="Iterations per level")
55
+ parser.add_argument("--lr", type=float, default=2e-4,
56
+ help="Learning rate (Adam)")
57
+ parser.add_argument("--samples-start", type=int, default=15000,
58
+ help="Surface samples at iter 0")
59
+ parser.add_argument("--samples-end", type=int, default=50000,
60
+ help="Surface samples at final iter")
61
+
62
+ # Mesh resolution
63
+ parser.add_argument("--init-faces", type=int, default=2000,
64
+ help="Face count of the initial mesh")
65
+ parser.add_argument("--face-growth", type=float, default=1.5,
66
+ help="Face-count multiplier between levels")
67
+ parser.add_argument("--max-faces", type=int, default=20000,
68
+ help="Maximum face count (stop growing)")
69
+
70
+ # Loss weights
71
+ parser.add_argument("--lambda-beam", type=float, default=1.0,
72
+ help="Weight for beam-gap loss")
73
+ parser.add_argument("--lambda-normal", type=float, default=0.1,
74
+ help="Weight for normal-alignment loss")
75
+ parser.add_argument("--beam-epsilon", type=float, default=0.5,
76
+ help="Beam-gap cylinder radius")
77
+
78
+ # Network
79
+ parser.add_argument("--in-channels", type=int, default=6,
80
+ help="Input feature dimension (random noise)")
81
+ parser.add_argument("--enc-channels", type=int, nargs="+",
82
+ default=[64, 128, 256, 256],
83
+ help="Encoder channel widths")
84
+
85
+ # Parts
86
+ parser.add_argument("--part-threshold", type=int, default=10000,
87
+ help="Enable PartMesh when #faces > this")
88
+ parser.add_argument("--n-parts", type=int, default=2,
89
+ help="Spatial grid resolution for PartMesh (n^3 parts)")
90
+
91
+ # Device / output
92
+ parser.add_argument("--device", type=str, default=None,
93
+ help="torch device (auto-detect if omitted)")
94
+ parser.add_argument("--save-intermediates", action="store_true",
95
+ help="Save mesh after each level")
96
+ parser.add_argument("--output-dir", type=str, default=".",
97
+ help="Directory for intermediate outputs")
98
+ parser.add_argument("--log-every", type=int, default=50,
99
+ help="Log loss every N iterations")
100
+ parser.add_argument("--verbose", "-v", action="store_true")
101
+
102
+ args = parser.parse_args()
103
+
104
+ # Logging
105
+ logging.basicConfig(
106
+ level=logging.DEBUG if args.verbose else logging.INFO,
107
+ format="%(asctime)s | %(name)s | %(message)s",
108
+ datefmt="%H:%M:%S",
109
+ )
110
+
111
+ cfg = Point2MeshConfig(
112
+ n_levels=args.n_levels,
113
+ iters_per_level=args.iters,
114
+ init_faces=args.init_faces,
115
+ face_growth=args.face_growth,
116
+ max_faces=args.max_faces,
117
+ samples_start=args.samples_start,
118
+ samples_end=args.samples_end,
119
+ lambda_beam=args.lambda_beam,
120
+ lambda_normal=args.lambda_normal,
121
+ beam_epsilon=args.beam_epsilon,
122
+ in_channels=args.in_channels,
123
+ enc_channels=args.enc_channels,
124
+ lr=args.lr,
125
+ part_threshold=args.part_threshold,
126
+ n_parts=args.n_parts,
127
+ log_every=args.log_every,
128
+ save_intermediates=args.save_intermediates,
129
+ output_dir=args.output_dir,
130
+ )
131
+ if args.device:
132
+ cfg.device = args.device
133
+
134
+ run_point2mesh(args.input, args.output, cfg)
135
+
136
+
137
+ if __name__ == "__main__":
138
+ main()