elf.mesh.mesh
1from typing import Optional, Tuple 2 3import nifty 4import numpy as np 5from skimage.measure import marching_cubes as marching_cubes_impl 6 7 8def marching_cubes( 9 obj: np.ndarray, 10 smoothing_iterations: int = 0, 11 resolution: Optional[Tuple[float, float, float]] = None, 12) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: 13 """Compute mesh via marching cubes. 14 15 This is a wrapper around the skimage marching cubes implementation that provides 16 additional mesh smoothing. 17 18 Args: 19 obj: Volume containing the object to be meshed. 20 smoothing_iterations: Number of mesh smoothing iterations. 21 resolution: Resolution of the data. 22 23 Returns: 24 The vertices of the mesh. 25 The faces of the mesh. 26 The normals of the mesh. 27 """ 28 resolution = (1.0, 1.0, 1.0) if resolution is None else resolution 29 if len(resolution) != 3: 30 raise ValueError(f"Invalid resolution argument: {resolution}") 31 resolution = tuple(resolution) 32 33 verts, faces, normals, _ = marching_cubes_impl(obj, spacing=resolution) 34 if smoothing_iterations > 0: 35 verts, normals = smooth_mesh(verts, normals, faces, smoothing_iterations) 36 37 return verts, faces, normals 38 39 40def smooth_mesh( 41 verts: np.ndarray, normals: np.ndarray, faces: np.ndarray, iterations: int 42) -> Tuple[np.ndarray, np.ndarray]: 43 """Smooth mesh surface via laplacian smoothing. 44 45 Args: 46 verts: The mesh vertices. 47 normals: The mesh normals. 48 faces: The mesh faces. 49 iterations: The number of smoothing iterations. 50 51 Returns: 52 The vertices after smoothing. 53 The normals after smoothing. 54 """ 55 n_verts = len(verts) 56 g = nifty.graph.undirectedGraph(n_verts) 57 58 edges = np.concatenate([faces[:, :2], faces[:, 1:], faces[:, ::2]], axis=0) 59 g.insertEdges(edges) 60 61 current_verts = verts 62 current_normals = normals 63 new_verts = np.zeros_like(verts, dtype=verts.dtype) 64 new_normals = np.zeros_like(normals, dtype=normals.dtype) 65 66 # Implement this directly in nifty for speed up? 67 for it in range(iterations): 68 for vert in range(n_verts): 69 nbrs = np.array([vert] + [nbr[0] for nbr in g.nodeAdjacency(vert)], dtype="int") 70 new_verts[vert] = np.mean(current_verts[nbrs], axis=0) 71 new_normals[vert] = np.mean(current_normals[nbrs], axis=0) 72 current_verts = new_verts 73 current_normals = new_normals 74 75 return new_verts, new_normals
def
marching_cubes( obj: numpy.ndarray, smoothing_iterations: int = 0, resolution: Optional[Tuple[float, float, float]] = None) -> Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]:
9def marching_cubes( 10 obj: np.ndarray, 11 smoothing_iterations: int = 0, 12 resolution: Optional[Tuple[float, float, float]] = None, 13) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: 14 """Compute mesh via marching cubes. 15 16 This is a wrapper around the skimage marching cubes implementation that provides 17 additional mesh smoothing. 18 19 Args: 20 obj: Volume containing the object to be meshed. 21 smoothing_iterations: Number of mesh smoothing iterations. 22 resolution: Resolution of the data. 23 24 Returns: 25 The vertices of the mesh. 26 The faces of the mesh. 27 The normals of the mesh. 28 """ 29 resolution = (1.0, 1.0, 1.0) if resolution is None else resolution 30 if len(resolution) != 3: 31 raise ValueError(f"Invalid resolution argument: {resolution}") 32 resolution = tuple(resolution) 33 34 verts, faces, normals, _ = marching_cubes_impl(obj, spacing=resolution) 35 if smoothing_iterations > 0: 36 verts, normals = smooth_mesh(verts, normals, faces, smoothing_iterations) 37 38 return verts, faces, normals
Compute mesh via marching cubes.
This is a wrapper around the skimage marching cubes implementation that provides additional mesh smoothing.
Arguments:
- obj: Volume containing the object to be meshed.
- smoothing_iterations: Number of mesh smoothing iterations.
- resolution: Resolution of the data.
Returns:
The vertices of the mesh. The faces of the mesh. The normals of the mesh.
def
smooth_mesh( verts: numpy.ndarray, normals: numpy.ndarray, faces: numpy.ndarray, iterations: int) -> Tuple[numpy.ndarray, numpy.ndarray]:
41def smooth_mesh( 42 verts: np.ndarray, normals: np.ndarray, faces: np.ndarray, iterations: int 43) -> Tuple[np.ndarray, np.ndarray]: 44 """Smooth mesh surface via laplacian smoothing. 45 46 Args: 47 verts: The mesh vertices. 48 normals: The mesh normals. 49 faces: The mesh faces. 50 iterations: The number of smoothing iterations. 51 52 Returns: 53 The vertices after smoothing. 54 The normals after smoothing. 55 """ 56 n_verts = len(verts) 57 g = nifty.graph.undirectedGraph(n_verts) 58 59 edges = np.concatenate([faces[:, :2], faces[:, 1:], faces[:, ::2]], axis=0) 60 g.insertEdges(edges) 61 62 current_verts = verts 63 current_normals = normals 64 new_verts = np.zeros_like(verts, dtype=verts.dtype) 65 new_normals = np.zeros_like(normals, dtype=normals.dtype) 66 67 # Implement this directly in nifty for speed up? 68 for it in range(iterations): 69 for vert in range(n_verts): 70 nbrs = np.array([vert] + [nbr[0] for nbr in g.nodeAdjacency(vert)], dtype="int") 71 new_verts[vert] = np.mean(current_verts[nbrs], axis=0) 72 new_normals[vert] = np.mean(current_normals[nbrs], axis=0) 73 current_verts = new_verts 74 current_normals = new_normals 75 76 return new_verts, new_normals
Smooth mesh surface via laplacian smoothing.
Arguments:
- verts: The mesh vertices.
- normals: The mesh normals.
- faces: The mesh faces.
- iterations: The number of smoothing iterations.
Returns:
The vertices after smoothing. The normals after smoothing.