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.