Skip to content

meshes

Search for available backends.

Source code in navis/meshes/operations.py
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
71
72
def available_backends(only_first=False):
    """Search for available backends."""
    backends = []

    try:
        import pyfqmr
        backends.append('pyfqmr')
    except ModuleNotFoundError:
        pass
    except BaseException:
        raise

    if only_first and len(backends):
        return backends

    try:
        import open3d
        backends.append('open3d')
    except ModuleNotFoundError:
        pass
    except BaseException:
        raise

    if only_first and len(backends):
        return backends

    try:
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            import pymeshlab
        backends.append('pymeshlab')
    except ModuleNotFoundError:
        pass
    except BaseException:
        raise

    if tm.interfaces.blender.exists:
        backends.append('blender')

    return backends

Simplify mesh using Blender.

PARAMETER DESCRIPTION
x
    Mesh object to simplify.

TYPE: MeshNeuron | Volume | Trimesh

F
    Ratio to which to reduce the mesh. For example, `F=0.5`
    should reduce number of vertices to half that of the original.

TYPE: float [0-1]

inplace
    If True, will perform simplication on `x`. If False, will
    simplify and return a copy.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
simp

Simplified mesh object.

Source code in navis/meshes/b3d.py
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
def simplify_mesh_blender(x, F, inplace=False):
    """Simplify mesh using Blender.

    Parameters
    ----------
    x :         MeshNeuron | Volume | Trimesh
                Mesh object to simplify.
    F :         float [0-1]
                Ratio to which to reduce the mesh. For example, `F=0.5`
                should reduce number of vertices to half that of the original.
    inplace :   bool
                If True, will perform simplication on `x`. If False, will
                simplify and return a copy.

    Returns
    -------
    simp
                Simplified mesh object.

    """
    if not tm.interfaces.blender.exists:
        raise ModuleNotFoundError('No Blender 3D unavailable (executable not found).')
    _blender_executable = tm.interfaces.blender._blender_executable

    if F > 1 or F < 0:
        raise ValueError(f'`F` must be between 0 and 1, got "{F}"')

    if isinstance(x, core.MeshNeuron):
        mesh = x.trimesh
    elif isinstance(x, core.Volume):
        mesh = tm.Trimesh(x.vertices, x.faces)
    elif isinstance(x, tm.Trimesh):
        mesh = x
    else:
        raise TypeError('Expected MeshNeuron, Volume or trimesh.Trimesh, '
                        f'got "{type(x)}"')

    assert isinstance(mesh, tm.Trimesh)

    # Load the template
    temp_name = 'blender_decimate.py.template'
    if temp_name in _cache:
        template = _cache[temp_name]
    else:
        with open(os.path.join(_pwd, 'templates', temp_name), 'r') as f:
            template = f.read()
        _cache[temp_name] = template

    # Replace placeholder with actual ratio
    script = template.replace('$RATIO', str(F))

    # Let trimesh's MeshScript take care of exectution and clean-up
    with tm.interfaces.generic.MeshScript(meshes=[mesh],
                                          script=script,
                                          debug=False) as blend:
        result = blend.run(_blender_executable
                           + ' --background --python $SCRIPT')

    # Blender apparently returns actively incorrect face normals
    result.face_normals = None

    if not inplace:
        x = x.copy()

    x.vertices = result.vertices
    x.faces = result.faces

    return x

Simplify mesh using pyfqmr.

PARAMETER DESCRIPTION
x
    Mesh object to simplify.

TYPE: MeshNeuron | Volume | Trimesh

F
    Target face count (integer).

TYPE: int

inplace
    If True, will perform simplication on `x`. If False, will
    simplify and return a copy.

TYPE: bool DEFAULT: False

**kwargs
    Keyword arguments are passed through to pyfqmr's
    `pyfqmr.Simplify.simplify_mesh`.

DEFAULT: {}

RETURNS DESCRIPTION
simp

Simplified mesh object.

Source code in navis/meshes/fqmr.py
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
def simplify_mesh_fqmr(x, F, inplace=False, **kwargs):
    """Simplify mesh using pyfqmr.

    Parameters
    ----------
    x :         MeshNeuron | Volume | Trimesh
                Mesh object to simplify.
    F :         int
                Target face count (integer).
    inplace :   bool
                If True, will perform simplication on `x`. If False, will
                simplify and return a copy.
    **kwargs
                Keyword arguments are passed through to pyfqmr's
                `pyfqmr.Simplify.simplify_mesh`.

    Returns
    -------
    simp
                Simplified mesh object.

    """
    if not utils.is_mesh(x):
        raise TypeError(f'Expected mesh-like, got "{type(x)}"')

    try:
        import pyfqmr
    except ModuleNotFoundError:
        raise ModuleNotFoundError('Please install pyfqmr: pip3 install pyfqmr')

    defaults = dict(aggressiveness=7, preserve_border=True, verbose=False)
    defaults.update(kwargs)

    mesh_simplifier = pyfqmr.Simplify()
    mesh_simplifier.setMesh(x.vertices, x.faces)
    mesh_simplifier.simplify_mesh(target_count=F, **defaults)
    vertices, faces, normals = mesh_simplifier.getMesh()

    if not inplace:
        x = x.copy()

    x.vertices = vertices
    x.faces = faces

    if hasattr(x, 'face_normals'):
        x.face_normals = normals

    return x

Simplify mesh using open3d.

PARAMETER DESCRIPTION
x
    Mesh object to simplify.

TYPE: MeshNeuron | Volume | Trimesh

F
    For method `quadric` this is the target face count (integer).
    For method `cluster` this is the size of the voxel within which
    vertices are pooled (larger t = coarser mesh).

TYPE: float | int

method
    Which method to use for simplification: either Quadric Error
    Metric Decimation (by Garland and Heckbert) or vertex clustering.
    Note that the intepretation of `F` depends on the method.

TYPE: "quadric" | "cluster" DEFAULT: 'quadric'

inplace
    If True, will perform simplication on `x`. If False, will
    simplify and return a copy.

TYPE: bool DEFAULT: False

**kwargs
    Keyword arguments are passed through to open3d's
    `simplify_quadric_decimation` and `simplify_vertex_clustering`,
    respectively.

DEFAULT: {}

RETURNS DESCRIPTION
simp

Simplified mesh object.

Source code in navis/meshes/o3d.py
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
def simplify_mesh_open3d(x, F, method='quadric', inplace=False, **kwargs):
    """Simplify mesh using open3d.

    Parameters
    ----------
    x :         MeshNeuron | Volume | Trimesh
                Mesh object to simplify.
    F :         float | int
                For method `quadric` this is the target face count (integer).
                For method `cluster` this is the size of the voxel within which
                vertices are pooled (larger t = coarser mesh).
    method :    "quadric" | "cluster"
                Which method to use for simplification: either Quadric Error
                Metric Decimation (by Garland and Heckbert) or vertex clustering.
                Note that the intepretation of `F` depends on the method.
    inplace :   bool
                If True, will perform simplication on `x`. If False, will
                simplify and return a copy.
    **kwargs
                Keyword arguments are passed through to open3d's
                `simplify_quadric_decimation` and `simplify_vertex_clustering`,
                respectively.

    Returns
    -------
    simp
                Simplified mesh object.

    """
    if not utils.is_mesh(x):
        raise TypeError(f'Expected mesh-like, got "{type(x)}"')

    mesh_o3d = make_o3d_mesh(x)

    if method == 'quadric':
        result = mesh_o3d.simplify_quadric_decimation(int(F), **kwargs)
    elif method == 'cluster':
        result = mesh_o3d.simplify_vertex_clustering(F,  **kwargs)
    else:
        raise ValueError(f'Unknown simplification scheme "{method}"')

    if not inplace:
        x = x.copy()

    x.vertices = np.asarray(result.vertices)
    x.faces = np.asarray(result.triangles)

    return x

Simplify mesh using pymeshlab.

PARAMETER DESCRIPTION
x
    Mesh object to simplify.

TYPE: MeshNeuron | Volume | Trimesh

F
    For method "quadric" this is the target number of faces as
    fraction of the original face count. For method "cluster" this
    is the size of the cells used for clustering: larger values =
    coarser mesh.

TYPE: float [0-1]

method
    Which method to use for simplification: quadratic mesh
    decimation or vertex clustering.

TYPE: "quadric" | "cluster" DEFAULT: 'quadric'

inplace
    If True, will perform simplication on `x`. If False, will
    simplify and return a copy.

TYPE: bool DEFAULT: False

**kwargs
    Passed to pymeshlab filter functions:
    `simplification_quadric_edge_collapse_decimation` or
    `simplification_clustering_decimation` depending on method.

DEFAULT: {}

RETURNS DESCRIPTION
simp

Simplified mesh-like object.

Source code in navis/meshes/pyml.py
 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
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
def simplify_mesh_pyml(x, F, method='quadric', inplace=False, **kwargs):
    """Simplify mesh using pymeshlab.

    Parameters
    ----------
    x :         MeshNeuron | Volume | Trimesh
                Mesh object to simplify.
    F :         float [0-1]
                For method "quadric" this is the target number of faces as
                fraction of the original face count. For method "cluster" this
                is the size of the cells used for clustering: larger values =
                coarser mesh.
    method :    "quadric" | "cluster"
                Which method to use for simplification: quadratic mesh
                decimation or vertex clustering.
    inplace :   bool
                If True, will perform simplication on `x`. If False, will
                simplify and return a copy.
    **kwargs
                Passed to pymeshlab filter functions:
                `simplification_quadric_edge_collapse_decimation` or
                `simplification_clustering_decimation` depending on method.

    Returns
    -------
    simp
                Simplified mesh-like object.

    """
    try:
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            import pymeshlab
    except ModuleNotFoundError:
        raise ModuleNotFoundError('Please install pymeshlab: pip3 install pymeshlab')
    except BaseException:
        raise

    utils.eval_param(method,
                     name='method',
                     allowed_values=('quadric', 'cluster'))

    if not isinstance(x, (core.MeshNeuron, tm.Trimesh, core.Volume)):
        raise TypeError(f'Expected MeshNeuron, Volume or Trimesh, got "{type(x)}"')

    if (F <= 0) or (F >= 1):
        raise ValueError(f'`t` must be between 0-1, got {F}')

    verts, faces = x.vertices, x.faces

    # Create mesh from vertices and faces
    m = pymeshlab.Mesh(verts, faces)

    # Create a new MeshSet
    ms = pymeshlab.MeshSet()

    # Add the mesh to the MeshSet
    ms.add_mesh(m, "mymesh")

    # Apply filter
    if method == 'quadric':
        defaults = {'targetperc': F}
        defaults.update(kwargs)
        ms.simplification_quadric_edge_collapse_decimation(**defaults)
    else:
        # Threshold is for some reason in percent, not fraction
        defaults = {'thresholds': F * 100}
        defaults.update(kwargs)
        ms.simplification_clustering_decimation(**defaults)

    # Get update mesh
    m2 = ms.current_mesh()

    # Get new vertices and faces
    new_verts = m2.vertex_matrix()
    new_faces = m2.face_matrix()

    # Make copy of the original mesh and assign new vertices + faces
    if not inplace:
        x = x.copy()

    x.vertices = new_verts
    x.faces = new_faces

    if isinstance(x, core.MeshNeuron):
        x._clear_temp_attr()

    return x