|
| 1 | +#cython: language_level=3str |
| 2 | + |
| 3 | +cimport numpy as np |
| 4 | +import numpy as np |
| 5 | +from libcpp.vector cimport vector |
| 6 | +from libcpp.string cimport string |
| 7 | +from libc.stdlib cimport malloc, free |
| 8 | +from libcpp cimport bool |
| 9 | + |
| 10 | +np.import_array() |
| 11 | + |
| 12 | +cdef extern from "../Src/PoissonReconLib.h": |
| 13 | + cdef int PoissonReconLib(int argc, char* argv[]) |
| 14 | + cdef vector[double] double_data |
| 15 | + cdef vector[int] int_data |
| 16 | + cdef vector[double] mem_data |
| 17 | + |
| 18 | +def poisson_reconstruction(points, normals, |
| 19 | + depth=8, full_depth=5, scale=1.1, |
| 20 | + samples_per_node=1.0, cg_depth=0.0, |
| 21 | + enable_polygon_mesh=False, enable_density=False, |
| 22 | + nthreads=0, verbose=False): |
| 23 | + |
| 24 | + """ |
| 25 | + Python Binding of Poisson Surface Reconstruction |
| 26 | + Usage: |
| 27 | +
|
| 28 | + faces, vertices = poisson_reconstruction(points, normals, depth=10) |
| 29 | +
|
| 30 | +
|
| 31 | + Parameters |
| 32 | + ---------- |
| 33 | +
|
| 34 | + points: array-like |
| 35 | +
|
| 36 | + list of oriented vertices with the x-, y-, and z-coordinates of the positions |
| 37 | +
|
| 38 | + normals: array-like |
| 39 | +
|
| 40 | + list of the x-, y-, and z-coordinates of the normals |
| 41 | +
|
| 42 | + depth: Integer |
| 43 | +
|
| 44 | + This integer is the maximum depth of the tree that will be used for surface reconstruction. |
| 45 | + Running at depth d corresponds to solving on a voxel grid whose resolution is no larger than 2^d x 2^d x 2^d. |
| 46 | + Note that since the reconstructor adapts the octree to the sampling density, the specified reconstruction depth is only an upper bound. |
| 47 | + The default value for this parameter is 8. |
| 48 | +
|
| 49 | + full_depth: Integer |
| 50 | +
|
| 51 | + This integer specifies the depth beyond depth the octree will be adapted. |
| 52 | + At coarser depths, the octree will be complete, containing all 2^d x 2^d x 2^d nodes. |
| 53 | + The default value for this parameter is 5. |
| 54 | +
|
| 55 | + scale: float |
| 56 | +
|
| 57 | + This floating point value specifies the ratio between the diameter of the cube used for reconstruction and the diameter of the samples' bounding cube. |
| 58 | + The default value is 1.1. |
| 59 | +
|
| 60 | + samples_per_node: float |
| 61 | +
|
| 62 | + This floating point value specifies the minimum number of sample points that should fall within an octree node as the octree construction is adapted to sampling density. |
| 63 | + For noise-free samples, small values in the range [1.0 - 5.0] can be used. |
| 64 | + For more noisy samples, larger values in the range [15.0 - 20.0] may be needed to provide a smoother, noise-reduced, reconstruction. |
| 65 | + The default value is 1.0. |
| 66 | +
|
| 67 | + cg_depth: Integer |
| 68 | +
|
| 69 | + This integer is the depth up to which a conjugate-gradients solver will be used to solve the linear system. |
| 70 | + Beyond this depth Gauss-Seidel relaxation will be used. |
| 71 | + The default value for this parameter is 0. |
| 72 | +
|
| 73 | + enable_polygon_mesh: Bool |
| 74 | + Enabling this flag tells the reconstructor to output a polygon mesh (rather than triangulating the results of Marching Cubes). |
| 75 | + The default value for this parameter is False. |
| 76 | +
|
| 77 | + enable_density: Bool |
| 78 | + Enabling this flag tells the reconstructor to output the estimated depth values of the iso-surface vertices |
| 79 | + The default value for this parameter is False. |
| 80 | +
|
| 81 | + nthreads: int |
| 82 | + Number of omp threads to use. Default = 0 = all available threads. |
| 83 | +
|
| 84 | + verbose: Bool |
| 85 | + Enable verbose mode. |
| 86 | +
|
| 87 | +
|
| 88 | +
|
| 89 | + Returns |
| 90 | + ------- |
| 91 | +
|
| 92 | + faces: array-like |
| 93 | + faces of the reconstructed mesh |
| 94 | +
|
| 95 | + vertices: array-like |
| 96 | +
|
| 97 | + vertices of the reconstructed mesh |
| 98 | +
|
| 99 | +
|
| 100 | +
|
| 101 | +
|
| 102 | + """ |
| 103 | + |
| 104 | + return _poisson_reconstruction(np.ascontiguousarray(np.float64(points)), |
| 105 | + np.ascontiguousarray(np.float64(normals)), |
| 106 | + depth, full_depth, scale, samples_per_node, |
| 107 | + cg_depth, enable_polygon_mesh, enable_density, |
| 108 | + nthreads, verbose) |
| 109 | + |
| 110 | + |
| 111 | + |
| 112 | +cdef _poisson_reconstruction(np.float64_t[:, ::1] points, |
| 113 | + np.float64_t[:, ::1] normals, |
| 114 | + int depth=8, |
| 115 | + int full_depth=5, |
| 116 | + double scale=1.10, |
| 117 | + double samples_per_node=1.0, |
| 118 | + double cg_depth=0.0, |
| 119 | + bool enable_polygon_mesh=False, |
| 120 | + bool enable_density=False, |
| 121 | + int nthreads=0, |
| 122 | + bool verbose=False): |
| 123 | + |
| 124 | + cdef: |
| 125 | + char **c_argv |
| 126 | + string arg_depth = str(depth).encode() |
| 127 | + string arg_full_depth = str(full_depth).encode() |
| 128 | + string arg_scale = str(scale).encode() |
| 129 | + string arg_samples_per_node = str(samples_per_node).encode() |
| 130 | + string arg_cg_depth = str(cg_depth).encode() |
| 131 | + string arg_nthreads = str(nthreads).encode() |
| 132 | + |
| 133 | + int_data.clear() |
| 134 | + double_data.clear() |
| 135 | + mem_data.clear() |
| 136 | + |
| 137 | + point_nrows, point_ncols = np.shape(points) |
| 138 | + normal_nrows, normal_ncols = np.shape(normals) |
| 139 | + |
| 140 | + mem_data.resize(point_ncols * point_nrows + normal_ncols * normal_nrows) |
| 141 | + |
| 142 | + for i in range(point_nrows): |
| 143 | + for j in range(point_ncols): |
| 144 | + mem_data[j + i*(point_ncols + normal_ncols)] = points[i,j] |
| 145 | + mem_data[j + point_ncols + i *(point_ncols + normal_ncols)] = normals[i,j] |
| 146 | + |
| 147 | + |
| 148 | + args = [b"PoissonRecon", b"--in", b"none", b"--out", b"none", b"--depth", arg_depth.c_str(), |
| 149 | + b"--fullDepth", arg_full_depth.c_str(), b"--scale", arg_scale.c_str(), |
| 150 | + b"--samplesPerNode", arg_samples_per_node.c_str(), |
| 151 | + b"--cgDepth", arg_cg_depth.c_str()] |
| 152 | + |
| 153 | + if verbose == True: |
| 154 | + args += [b"--verbose"] |
| 155 | + |
| 156 | + if nthreads > 0: |
| 157 | + args += [b"--threads", arg_nthreads.c_str()] |
| 158 | + |
| 159 | + if enable_polygon_mesh: |
| 160 | + args += [b"--polygonMesh"] |
| 161 | + if enable_density: |
| 162 | + args += [b"--density"] |
| 163 | + |
| 164 | + c_argv = <char**> malloc(sizeof(char*) * len(args)) |
| 165 | + for idx, s in enumerate(args): |
| 166 | + c_argv[idx] = s |
| 167 | + |
| 168 | + try: |
| 169 | + PoissonReconLib(len(args), c_argv) |
| 170 | + finally: |
| 171 | + free(c_argv) |
| 172 | + |
| 173 | + |
| 174 | + face_cols, vertex_cols = 3, 3 |
| 175 | + face_rows = int_data.size() // face_cols |
| 176 | + vertex_rows = double_data.size() // vertex_cols |
| 177 | + |
| 178 | + cdef int *ptr_faces = &int_data[0] |
| 179 | + cdef double *ptr_vertices = &double_data[0] |
| 180 | + |
| 181 | + faces = np.zeros((face_rows*face_cols,), dtype=np.int32 ) |
| 182 | + vertices = np.zeros((vertex_rows*vertex_cols,), dtype=np.float64) |
| 183 | + |
| 184 | + for i in range(face_rows*face_cols): |
| 185 | + faces[i] = ptr_faces[i] |
| 186 | + |
| 187 | + for i in range(vertex_rows*vertex_cols): |
| 188 | + vertices[i] = ptr_vertices[i] |
| 189 | + |
| 190 | + int_data.clear() |
| 191 | + double_data.clear() |
| 192 | + |
| 193 | + return faces.reshape(face_rows,face_cols), vertices.reshape(vertex_rows,vertex_cols) |
0 commit comments