bdck commited on
Commit
5a90b18
·
verified ·
1 Parent(s): 3b9f516

Upload nksr_wrapper/io.py

Browse files
Files changed (1) hide show
  1. nksr_wrapper/io.py +101 -0
nksr_wrapper/io.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ I/O helpers for point clouds (PLY/PCD) and meshes (OBJ/PLY/GLB).
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from pathlib import Path
8
+ from typing import Optional, Tuple, Union
9
+
10
+ import numpy as np
11
+
12
+
13
+ def load_point_cloud(
14
+ path: Union[str, Path],
15
+ *,
16
+ estimate_normals: bool = False,
17
+ normal_knn: int = 30,
18
+ ) -> Tuple[np.ndarray, Optional[np.ndarray]]:
19
+ """
20
+ Load a point cloud from a ``.ply`` or ``.pcd`` file.
21
+
22
+ Parameters
23
+ ----------
24
+ path : str or Path
25
+ Path to the input file.
26
+ estimate_normals : bool, default False
27
+ If the file does not contain normals, estimate them with Open3D.
28
+ normal_knn : int, default 30
29
+ k-NN neighbourhood size for Open3D normal estimation.
30
+
31
+ Returns
32
+ -------
33
+ points : np.ndarray
34
+ (N, 3) float array of point positions.
35
+ normals : np.ndarray or None
36
+ (N, 3) float array of point normals, if available or estimated.
37
+ """
38
+ path = Path(path)
39
+ suffix = path.suffix.lower()
40
+
41
+ if suffix in (".ply", ".pcd"):
42
+ points, normals = _load_with_open3d(path, estimate_normals, normal_knn)
43
+ else:
44
+ raise ValueError(f"Unsupported point-cloud format: {suffix}")
45
+
46
+ return points, normals
47
+
48
+
49
+ def _load_with_open3d(
50
+ path: Path, estimate_normals: bool, normal_knn: int
51
+ ) -> Tuple[np.ndarray, Optional[np.ndarray]]:
52
+ try:
53
+ import open3d as o3d
54
+ except ImportError as exc:
55
+ raise ImportError(
56
+ "open3d is required to load PLY/PCD files. "
57
+ "Install it with: pip install open3d"
58
+ ) from exc
59
+
60
+ pcd = o3d.io.read_point_cloud(str(path))
61
+ points = np.asarray(pcd.points)
62
+
63
+ normals = None
64
+ if pcd.has_normals():
65
+ normals = np.asarray(pcd.normals)
66
+ elif estimate_normals and len(points) > 0:
67
+ pcd.estimate_normals(
68
+ search_param=o3d.geometry.KDTreeSearchParamKNN(knn=normal_knn)
69
+ )
70
+ pcd.orient_normals_consistent_tangent_plane(k=normal_knn)
71
+ normals = np.asarray(pcd.normals)
72
+
73
+ return points, normals
74
+
75
+
76
+ def save_mesh(
77
+ path: Union[str, Path],
78
+ vertices: np.ndarray,
79
+ faces: np.ndarray,
80
+ vertex_colors: Optional[np.ndarray] = None,
81
+ ) -> None:
82
+ """
83
+ Save a triangle mesh to a file.
84
+
85
+ Supported formats: ``.ply``, ``.obj``, ``.glb``, ``.stl``, ``.off``
86
+ (anything Trimesh supports).
87
+ """
88
+ import trimesh
89
+
90
+ path = Path(path)
91
+
92
+ # Ensure correct dtypes
93
+ vertices = np.asarray(vertices, dtype=np.float32)
94
+ faces = np.asarray(faces, dtype=np.int32)
95
+
96
+ mesh = trimesh.Trimesh(
97
+ vertices=vertices,
98
+ faces=faces,
99
+ vertex_colors=vertex_colors,
100
+ )
101
+ mesh.export(str(path))