DataSetFilters.voxelize_binary_mask

DataSetFilters.voxelize_binary_mask#

DataSetFilters.voxelize_binary_mask(
*,
background_value: int | float = 0,
foreground_value: int | float = 1,
reference_volume: ImageData | None = None,
dimensions: VectorLikeInt | None = None,
spacing: float | VectorLikeFloat | None = None,
target_n_points: int | None = None,
max_n_points: int | None = None,
rounding_func: Callable[[VectorLikeFloat], VectorLikeInt] | None = None,
cell_length_percentile: float | None = None,
cell_length_sample_size: int | None = None,
progress_bar: bool = False,
) → ImageData[source]#

Voxelize mesh as a binary ImageData mask.

The binary mask is a point data array where points inside and outside of the input surface are labelled with foreground_value and background_value, respectively.

This filter implements vtkPolyDataToImageStencil. This algorithm operates as follows:

  • The algorithm iterates through the z-slice of the reference_volume.

  • For each slice, it cuts the input PolyData surface to create 2D polylines at that z position. It attempts to close any open polylines.

  • For each x position along the polylines, the corresponding y positions are determined.

  • For each slice, the grid points are labelled as foreground or background based on their xy coordinates.

The voxelization can be controlled in several ways:

  1. Specify the output geometry using a reference_volume.

  2. Specify the spacing explicitly.

  3. Specify the dimensions explicitly.

  4. Specify the target_n_points. The spacing is isotropic and estimated so the output has approximately this many points.

  5. Specify the cell_length_percentile. The spacing is estimated from the surface’s cells using the specified percentile.

Set max_n_points to cap the result of any of these. It differs from target_n_points, which is a resolution to aim for: a geometry specified explicitly raises if it exceeds the cap, while an estimated one is coarsened to fit.

Use reference_volume for full control of the output mask’s geometry. For all other options, the geometry is implicitly defined such that the generated mask fits the bounds of the input surface.

If no inputs are provided, cell_length_percentile=0.1 (tenth percentile) is used by default to estimate the spacing.

Added in version 0.45.0.

Note

The input must be a surface with faces or strips. Generate a surface from a point cloud with reconstruct_surface() before voxelizing it.

Note

For best results, ensure the input surface is a closed surface. The surface is considered closed if it has zero n_open_edges.

Note

This filter returns voxels represented as point data, not VOXEL cells. This differs from voxelize() and voxelize_rectilinear() which return meshes with voxel cells. See Image Data Representations for examples demonstrating the difference.

Note

This filter does not discard internal surfaces, due, for instance, to intersecting meshes. Instead, the intersection will be considered as background which may produce unexpected results. See Examples.

Parameters:
background_valueint, default: 0

Background value of the generated mask.

foreground_valueint, default: 1

Foreground value of the generated mask.

reference_volumeImageData, optional

Volume to use as a reference. The output will have the same dimensions, origin, spacing, offset, and direction_matrix as the reference.

dimensionsVectorLikeInt, optional

Dimensions of the generated mask image. Set this value to control the dimensions explicitly. If unset, the dimensions are defined implicitly through other parameter. See summary and examples for details.

spacingfloat | VectorLikeFloat, optional

Approximate spacing to use for the generated mask image. Set this value to control the spacing explicitly. If unset, the spacing is defined implicitly through other parameters. See summary and examples for details.

target_n_pointsint, optional

Approximate number of points to generate. The spacing is isotropic and chosen so the output holds about this many points, distributed between the axes in proportion to the input’s bounds. An axis with no extent holds a single point and takes no part in the count. Rounding to whole voxels means the count is approached, not matched exactly. Cannot be set with reference_volume, dimensions, spacing, or the cell length options.

Added in version 0.50.

max_n_pointsint, optional

Strict upper bound on the number of points generated. Unlike target_n_points, which is only approached, this limit is never exceeded. How it is enforced depends on how the geometry is defined:

  • Geometry set explicitly, with reference_volume, dimensions, spacing or a cell length option, raises if it exceeds the limit.

  • target_n_points must not exceed the limit, and the grid estimated from it is coarsened if rounding would take it above.

  • Geometry left to the defaults is coarsened to fit, without raising.

Added in version 0.50.

rounding_funcCallable[VectorLikeFloat, VectorLikeInt], optional

Control how the dimensions are rounded to integers based on the provided or calculated spacing. Should accept a length-3 vector containing the dimension values along the three directions and return a length-3 vector. numpy.round() is used by default.

Rounding the dimensions implies rounding the actual spacing.

Has no effect if reference_volume or dimensions are specified.

cell_length_percentilefloat, optional

Cell length percentage p to use for computing the default spacing. Default is 0.1 (tenth percentile) and must be between 0 and 1. The input’s surface is extracted first, and the p-th percentile is computed from the lengths of the edges of its cells. Up to cell_length_sample_size of those cells are used, drawn at random with a fixed seed, and degenerate edges with zero length are ignored.

Changed in version 0.50.0: The percentile is computed from every edge of the sampled cells instead of the distance between two random points of each triangulated cell, and the sample is drawn with a fixed seed. The estimate is now deterministic.

Has no effect if dimensions or reference_volume are specified.

cell_length_sample_sizeint, optional

Maximum number of cells to use when computing the cell_length_percentile. 100 000 cells are used by default.

progress_barbool, default: False

Display a progress bar to indicate progress.

Returns:
ImageData

Generated binary mask with a 'mask' point data array. The data array has dtype numpy.uint8 if the foreground and background values are unsigned and less than 256.

Examples#

Download Python source code | Download Jupyter notebook

Generate a binary mask from a coarse mesh.

>>> import numpy as np
>>> import pyvista as pv
>>> from pyvista import examples
>>> poly = examples.download_bunny_coarse()
>>> mask = poly.voxelize_binary_mask()

The mask is stored as ImageData with point data scalars (zeros for background, ones for foreground).

>>> mask
ImageData (...)
  N Cells:      6720
  N Points:     7854
  X Bounds:     -1.245e-01, 1.731e-01
  Y Bounds:     -1.131e-01, 1.804e-01
  Z Bounds:     -1.359e-01, 9.140e-02
  Dimensions:   22, 21, 17
  Spacing:      1.417e-02, 1.468e-02, 1.421e-02
  N Arrays:     1
>>> np.unique(mask.point_data['mask'])
pyvista_ndarray([0, 1], dtype=uint8)

To visualize it as voxel cells, use points_to_cells(), then use threshold() to extract the foreground.

We also plot the voxel cells in blue and the input poly data in green for comparison.

>>> def mask_and_polydata_plotter(mask, poly):
...     voxel_cells = mask.points_to_cells().threshold(0.5)
...
...     pl = pv.Plotter()
...     _ = pl.add_mesh(voxel_cells, color='blue')
...     _ = pl.add_mesh(poly, color='lime')
...     pl.camera_position = 'xy'
...     return pl
>>> pl = mask_and_polydata_plotter(mask, poly)
>>> pl.show()
../../../_images/pyvista-DataSetFilters-voxelize_binary_mask-9c3aed42d500a348_00_00.png

The spacing of the mask image is automatically adjusted to match the density of the input.

Repeat the previous example with a finer mesh.

>>> poly = examples.download_bunny()
>>> mask = poly.voxelize_binary_mask()
>>> pl = mask_and_polydata_plotter(mask, poly)
>>> pl.show()
../../../_images/pyvista-DataSetFilters-voxelize_binary_mask-9c3aed42d500a348_01_00.png

Control the spacing manually instead. Here, a very coarse spacing is used.

>>> mask = poly.voxelize_binary_mask(spacing=(0.01, 0.04, 0.02))
>>> pl = mask_and_polydata_plotter(mask, poly)
>>> pl.show()
../../../_images/pyvista-DataSetFilters-voxelize_binary_mask-9c3aed42d500a348_02_00.png

Note that the spacing is only approximate. Check the mask’s actual spacing.

>>> mask.spacing
(0.009731187485158443, 0.03858340159058571, 0.020112216472625732)

The actual values may be greater or less than the specified values. Use rounding_func=np.floor to force all values to be greater.

>>> mask = poly.voxelize_binary_mask(
...     spacing=(0.01, 0.04, 0.02), rounding_func=np.floor
... )
>>> mask.spacing
(0.01037993331750234, 0.05144453545411428, 0.020112216472625732)

Set the dimensions instead of the spacing.

>>> mask = poly.voxelize_binary_mask(dimensions=(10, 20, 30))
>>> pl = mask_and_polydata_plotter(mask, poly)
>>> pl.show()
../../../_images/pyvista-DataSetFilters-voxelize_binary_mask-9c3aed42d500a348_03_00.png
>>> mask.dimensions
(10, 20, 30)

Create a mask using a reference volume. First load a label map, crop it to the frog’s head, and generate polydata from it.

>>> volume = examples.load_frog_tissues()
>>> volume = volume.crop(extent=(300, 499, 110, 350, 0, 100))
>>> poly = volume.contour_labels()

Now create the mask from the polydata using the volume as a reference.

>>> mask = poly.voxelize_binary_mask(reference_volume=volume)
>>> pl = mask_and_polydata_plotter(mask, poly)
>>> pl.show()
../../../_images/pyvista-DataSetFilters-voxelize_binary_mask-9c3aed42d500a348_04_00.png

The mask is mottled where the surface is not closed. Smoothed surfaces from contour_labels() have gaps wherever the labels touch only along a voxel edge, and the cut contours are closed heuristically there.

Visualize the effect of internal surfaces.

>>> mesh = pv.Cylinder() + pv.Cylinder(center=(0, 0.75, 0))
>>> binary_mask = mesh.voxelize_binary_mask(
...     dimensions=(1, 100, 50)
... ).points_to_cells()
>>> pl = pv.Plotter()
>>> _ = pl.add_mesh(binary_mask)
>>> _ = pl.add_mesh(mesh.slice(), color='red')
>>> pl.show(cpos='yz')
../../../_images/pyvista-DataSetFilters-voxelize_binary_mask-9c3aed42d500a348_05_00.png

Note how the intersection is excluded from the mask. To include the voxels delimited by internal surfaces in the foreground, the internal surfaces should be removed, for instance by applying a boolean union. Note that this operation in unreliable in VTK but may be performed with external tools such as vtkbool.

Alternatively, the intersecting parts of the mesh can be processed sequentially.

>>> cylinder_1 = pv.Cylinder()
>>> cylinder_2 = pv.Cylinder(center=(0, 0.75, 0))
>>> reference_volume = pv.ImageData(
...     dimensions=(1, 100, 50),
...     spacing=(1, 0.0175, 0.02),
...     origin=(0, -0.5 + 0.0175 / 2, -0.5 + 0.02 / 2),
... )
>>> binary_mask_1['mask'] = binary_mask_1['mask'] | binary_mask_2['mask']
>>> pl = pv.Plotter()
>>> _ = pl.add_mesh(binary_mask_1)
>>> _ = pl.add_mesh(cylinder_1.slice(), color='red')
>>> _ = pl.add_mesh(cylinder_2.slice(), color='red')
>>> pl.show(cpos='yz')
../../../_images/pyvista-DataSetFilters-voxelize_binary_mask-9c3aed42d500a348_06_00.png

When multiple internal surfaces are nested, they are successively treated as interfaces between background and foreground.

>>> mesh = pv.Tube(radius=2) + pv.Tube(radius=3) + pv.Tube(radius=4)
>>> binary_mask = mesh.voxelize_binary_mask(
...     dimensions=(1, 50, 50)
... ).points_to_cells()
>>> pl = pv.Plotter()
>>> _ = pl.add_mesh(binary_mask)
>>> _ = pl.add_mesh(mesh.slice(), color='red')
>>> pl.show(cpos='yz')
../../../_images/pyvista-DataSetFilters-voxelize_binary_mask-9c3aed42d500a348_07_00.png

See Also#

voxelize

Similar function that returns a UnstructuredGrid of VOXEL cells.

voxelize_rectilinear

Similar function that returns a RectilinearGrid with cell data.

pyvista.DataObjectFilters.resample_to_image

Similar function which generates a ImageData of the same geometry. It resamples the input’s data arrays instead of generating a mask, and fills the voxels its cells or points reach rather than a closed surface’s interior.

pyvista.ImageDataFilters.contour_labels

Filter that generates surface contours from labeled image data. Can be loosely considered as an inverse of this filter.

pyvista.ImageDataFilters.points_to_cells

Convert voxels represented as points to VOXEL cells.

ImageData

Class used to build custom reference_volume.