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)

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

pro_decimated = mesh.decimate_pro(target_reduction, preserve_topology=True)
pro_decimated.plot(cpos=cpos, **plot_kwargs)

Side by side comparison:
datasets = {
'Input': mesh,
'Decimated': decimated,
'Pro Decimated': pro_decimated,
}
pv.plot_compare(
datasets,
label_size=24,
cpos=cpos,
**plot_kwargs,
)

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)

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]
)

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 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],
)
Total running time of the script: (0 minutes 6.172 seconds)