File size: 3,215 Bytes
ecb7b71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
"""
CLI entry point for LightweightMR reconstruction.
Usage:
    python -m lightweightmr --input path/to/pointcloud.ply --output path/to/mesh.ply
"""
import argparse
import os
import sys
from .optimize import Runner


def main():
    parser = argparse.ArgumentParser(description="LightweightMR — pure-Python mesh reconstruction")
    parser.add_argument("--input", "-i", required=True, help="Input point cloud (.ply, .pcd, .xyz)")
    parser.add_argument("--output", "-o", required=True, help="Output mesh file (.ply or .obj)")
    parser.add_argument("--device", default="cpu", help="torch device: cpu or cuda")
    parser.add_argument("--out-dir", default="./output", help="Intermediate outputs directory")
    parser.add_argument("--sdf-iters", type=int, default=20_000, help="SDF training iterations")
    parser.add_argument("--vg-iters", type=int, default=8_000, help="Vertex generation iterations")
    parser.add_argument("--sdf-lr", type=float, default=1e-3, help="SDF learning rate")
    parser.add_argument("--vg-lr", type=float, default=1e-3, help="VG learning rate")
    parser.add_argument("--sdf-batch", type=int, default=5_000, help="SDF batch size")
    parser.add_argument("--vertices", type=int, default=3_400, help="Target number of vertices")
    parser.add_argument("--update-size", type=int, default=5, help="Curriculum update steps")
    parser.add_argument("--update-ratio", type=float, default=1.2, help="Curriculum growth ratio")
    parser.add_argument("--k-samples", type=int, default=21, help="Tetrahedron interior samples")
    parser.add_argument("--multires", type=int, default=8, help="Positional encoding frequencies")
    parser.add_argument("--queries-size", type=int, default=1_000_000, help="SDF query sample count")
    parser.add_argument("--surface-queries", type=int, default=200_000, help="Surface query count")
    parser.add_argument("--project-sdf-level", type=float, default=0.0, help="SDD projection level")
    parser.add_argument("--save-freq", type=int, default=2_000, help="Checkpoint save frequency")
    parser.add_argument("--resume-sdf", default=None, help="Resume from SDF checkpoint .pth")

    args = parser.parse_args()

    if not os.path.exists(args.input):
        print(f"ERROR: input file not found: {args.input}")
        sys.exit(1)

    runner = Runner(
        pointcloud_path=args.input,
        out_dir=args.out_dir,
        device=args.device,
        sdf_iters=args.sdf_iters,
        vg_iters=args.vg_iters,
        sdf_lr=args.sdf_lr,
        vg_lr=args.vg_lr,
        sdf_batch=args.sdf_batch,
        vertices_size=args.vertices,
        update_size=args.update_size,
        update_ratio=args.update_ratio,
        k_samples=args.k_samples,
        multires=args.multires,
        queries_size=args.queries_size,
        surface_queries=args.surface_queries,
        project_sdf_level=args.project_sdf_level,
        save_freq=args.save_freq,
    )

    if args.resume_sdf:
        print(f"Loading SDF checkpoint: {args.resume_sdf}")
        runner.load_sdf_checkpoint(args.resume_sdf)

    v, f = runner.run(mesh_path=args.output)
    print(f"\nDone! Mesh: {len(v)} vertices, {len(f)} faces")


if __name__ == "__main__":
    main()