Decimation#

Download Python source code | Download Jupyter notebook

Decimate a mesh.

import time

import numpy as np
import pyvista as pv
from pyvista import examples

mesh = examples.download_face()

# Define a camera position that shows this mesh properly
cpos = pv.CameraPosition(
    position=(0.4, -0.07, -0.31), focal_point=(0.05, -0.13, -0.06), viewup=(-0.1, 1, 0.08)
)
plot_kwargs = dict(show_edges=True, color=True)

# Preview the mesh
mesh.plot(cpos=cpos, **plot_kwargs)
decimate

Now let’s define a target reduction and compare the pyvista.PolyDataFilters.decimate() and pyvista.PolyDataFilters.decimate_pro() filters.

target_reduction = 0.7
print(f'Reducing {target_reduction * 100.0} percent out of the original mesh')
Reducing 70.0 percent out of the original mesh
decimate
decimate

Side by side comparison:

datasets = {
    'Input': mesh,
    'Decimated': decimated,
    'Pro Decimated': pro_decimated,
}

pv.plot_compare(
    datasets,
    label_size=24,
    cpos=cpos,
    **plot_kwargs,
)
decimate

Binned Decimation#

pyvista.PolyDataFilters.decimate_binned() splits the bounds of a mesh into a grid of bins and merges the points in each bin into one. It does not preserve topology, but it is orders of magnitude faster than the filters above, which makes it a good fit for previews of very large surfaces.

Decimate a larger mesh to roughly 5,000 points.

armadillo = examples.download_armadillo()
armadillo_cpos = pv.CameraPosition(
    position=(161.5, 82.1, -330.2), focal_point=(-4.3, 24.5, -1.6), viewup=(-0.1, 1, 0.12)
)
binned = armadillo.decimate_binned(target_points=5_000)
binned.plot(cpos=armadillo_cpos, show_edges=True)
decimate

Compare each point_generation option with uniform and per-axis divisions.

sphere = pv.Sphere(theta_resolution=200, phi_resolution=200)
point_generations = {}
for divisions in (8, (4, 8, 16)):
    for point_generation in ('bin_points', 'bin_centers', 'bin_averages'):
        label = f'{point_generation}\ndivisions={divisions}'
        point_generations[label] = sphere.decimate_binned(
            divisions=divisions, point_generation=point_generation
        )


pv.plot_compare(
    point_generations, shape=(2, 3), show_edges=True, cpos='iso', window_size=[1200, 800]
)
decimate

Time each of the three surface decimation filters on the same reduction.

reduction = 1 - 5_000 / armadillo.n_points
filters = {
    'decimate': lambda: armadillo.decimate(reduction),
    'decimate_pro': lambda: armadillo.decimate_pro(reduction),
    'decimate_binned': lambda: armadillo.decimate_binned(reduction),
}

results = {}
for name, run in filters.items():
    start = time.perf_counter()
    results[name] = run()
    print(f'{name}: {time.perf_counter() - start:.3f} s')
decimate: 2.625 s
decimate_pro: 2.004 s
decimate_binned: 0.018 s

Plot the results side by side.

pv.plot_compare(results, cpos=armadillo_cpos, window_size=[1200, 500], show_edges=True)
decimate

Decimate PolyLine Mesh#

Generate a fairly slow spiral polyline mesh.

Decimate using pyvista.PolyDataFilters.decimate_polyline() filter by targets of 50% and 80%. To avoid errors of quickly changing features, also use the maximum_error parameter. It is in units of fraction of the largest length of the bounding box. Note that it limits the level of reduction achieved.

polylines = {
    '50%': spiral.decimate_polyline(0.5),
    '80%': spiral.decimate_polyline(0.8),
    '80%, maximum_error=0.5': spiral.decimate_polyline(0.8, maximum_error=0.5),
}
print(f'Original # of points: {spiral.n_points}')
for name, decimated in polylines.items():
    print(f'{name}: {decimated.n_points}')
Original # of points: 100
50%: 50
80%: 20
80%, maximum_error=0.5: 45

Compare each result, in red, with the original spiral, in black. The decimation looks OK at 50%. At 80% the structure of the inner part of the spiral is completely lost, and maximum_error captures it adequately.

pv.plot_compare(
    polylines,
    reference_mesh=spiral,
    reference_kwargs=dict(color='k', line_width=2),
    cpos='xy',
    color='r',
    line_width=5,
    window_size=[1200, 400],
)
decimate

Tags: filter

Total running time of the script: (0 minutes 6.172 seconds)

Gallery generated by Sphinx-Gallery