File: image_representations.py

package info (click to toggle)
python-pyvista 0.46.4-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 176,968 kB
  • sloc: python: 94,346; sh: 216; makefile: 70
file content (226 lines) | stat: -rw-r--r-- 7,492 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
"""
.. _image_representations_example:

Image Data Representations
~~~~~~~~~~~~~~~~~~~~~~~~~~
This example demonstrates how to use :meth:`~pyvista.ImageDataFilters.points_to_cells`
and :meth:`~pyvista.ImageDataFilters.cells_to_points` to re-mesh :class:`~pyvista.ImageData`.

These filters can be used to ensure that image data has an appropriate representation
when generating plots and/or when using either point- or cell-based filters such as
:meth:`ImageDataFilters.image_threshold <pyvista.ImageDataFilters.image_threshold>` (point-based)
and :meth:`DataSetFilters.threshold <pyvista.DataSetFilters.threshold>` (cell-based).

"""

# %%#
# Representations of 3D Volumes
# -----------------------------
# Create image data of a 3D volume with eight points and a discrete scalar data
# array.
from __future__ import annotations

import numpy as np

import pyvista as pv

# sphinx_gallery_thumbnail_number = 3


data_array = [8, 7, 6, 5, 4, 3, 2, 1]
points_volume = pv.ImageData(dimensions=(2, 2, 2))
points_volume.point_data['Data'] = data_array

# %%#
# If we plot the volume, it is represented as a single cell with eight points,
# and the point data is interpolated to color the cell.

points_volume.plot(show_edges=True)

# %%#
# However, in many applications (e.g. 3D medical imaging), the scalar data arrays
# represent discretized samples at the centers of voxels. As such, it may
# be more appropriate to represent the data as eight voxel cells instead of
# eight points. We can use :meth:`~pyvista.ImageDataFilters.points_to_cells` to
# generate a cell-based representation.

cells_volume = points_volume.points_to_cells()

# %%#
# Now, when we plot the volume, we have a more appropriate representation with
# eight voxel cells and the scalar data is no longer interpolated.

cells_volume.plot(show_edges=True)

# %%#
# Let's plot the two representations together for comparison.
#
# For visualization, we color the points volume (inner mesh) and only show the edges
# of the cells volume (outer mesh). We also plot the cell centers in red. Note
# how the centers of the image of the cells correspond to the points of the points image.

cell_centers = cells_volume.cell_centers()
cell_edges = cells_volume.extract_all_edges()

plot = pv.Plotter()
plot.add_mesh(points_volume, color=True, show_edges=True, opacity=0.7)
plot.add_mesh(cell_edges, color='black', line_width=2)
plot.add_points(
    cell_centers,
    render_points_as_spheres=True,
    color='red',
    point_size=20,
)
plot.camera.azimuth = -25
plot.camera.elevation = 25
plot.show()

# %%#
# As long as only one kind of scalar data is used (i.e. either point or cell
# data, but not both), it is possible to move between representations without
# loss of data.

array_before = points_volume.active_scalars
array_after = points_volume.points_to_cells().cells_to_points().active_scalars
np.array_equal(array_before, array_after)

# %%#
# Point Filters with Image Data
# -----------------------------
# Use a point representation of the image when working with point-based
# filters such as :meth:`~pyvista.ImageDataFilters.image_threshold`. If the
# image only has cell data, use :meth:`~pyvista.ImageDataFilters.cells_to_points`
# re-mesh the input first. Here, we reuse the point-based image defined earlier.
#
# For context, we first show the input data array.

points_volume.point_data['Data']

# %%#
# Now apply the filter and print the result.

points_ithresh = points_volume.image_threshold(2)
points_ithresh.point_data['Data']

# %%#
# The filter returns binary point data as expected. Values equal to or greater
# or than the threshold of ``2`` are ones and less than the threshold are zeros.
#
# However, in plotting it, the point values are linearly interpolated. For
# visualizing binary data, this interpolation is not desirable.

points_ithresh.plot(show_edges=True)

# %%#
# To better visualize the result, convert the image of the point returned by the
# filter to a cell representation with :meth:`~pyvista.ImageDataFilters.points_to_cells`
# before plotting.

points_ithresh_as_cells = points_ithresh.points_to_cells()
points_ithresh_as_cells.plot(show_edges=True)

# %%#
# The binary data is now correctly visualized as binary data.

# %%#
# Cell Filters with Image Data
# ----------------------------
# Use a cell representation of the image when working with cell-based filters
# such as :meth:`~pyvista.DataSetFilters.threshold`. If the image only has point
# data, use :meth:`~pyvista.ImageDataFilters.points_to_cells` to re-mesh the
# input first. Here, we reuse the cell-based image created earlier.
#
# For context, we first show the input data array.

cells_volume.cell_data['Data']

# %%#
# Now apply the filter and print the result.

cells_thresh = cells_volume.threshold(2)
cells_thresh.cell_data['Data']

# %%#
# When the input is cell data, this filter returns seven discrete values greater
# than or equal to the threshold value of ``2`` as expected.
#
# If we plot the result, the cells also produce the correct visualization.

cells_thresh.plot(show_edges=True)

# %%#
# However, if we apply the same filter to a point-based representation of the
# image, the filter does not produce the desired result.

points_thresh = points_volume.threshold(2)
points_thresh.point_data['Data']

# %%#
# In this case, since the image of the point only has a single cell, the filter has no
# effect on the data array's values. The thresholded values are the same as the
# input values.
#
# Plotting the result confirms this. The plot is identical to the initial plot
# of the point-based image shown at the beginning of this example.

points_thresh.plot(show_edges=True)

# %%#
# Representations of 2D Images
# ----------------------------
# The filters :meth:`~pyvista.ImageDataFilters.points_to_cells` and
# :meth:`~pyvista.ImageDataFilters.cells_to_points` can similarly be used
# with 2D images.
#
# For this example, we create a 4x4 2D grayscale image with 16 points to represent
# 16 pixels.

data_array = np.linspace(0, 255, 16, dtype=np.uint8)[::-1]
points_image = pv.ImageData(dimensions=(4, 4, 1))
points_image.point_data['Data'] = data_array

# %%#
# Plot the image. As before, the plot does not appear correct since the point
# data is interpolated, and nine cells are shown rather than the desired 16
# (one for each pixel).

plot_kwargs = dict(
    cpos='xy',
    zoom='tight',
    show_axes=False,
    cmap='gray',
    clim=[0, 255],
    show_edges=True,
)
points_image.plot(**plot_kwargs)

# %%#
# To visualize the image correctly, we first use :meth:`~pyvista.ImageDataFilters.points_to_cells`
# to get a cell-based representation of the image and plot the result. The plot
# now correctly shows 16-pixel cells with discrete values.

cells_image = points_image.points_to_cells()
cells_image.plot(**plot_kwargs)

# %%#
# Let's plot the two representations together for comparison.
#
# For visualization, we color the points image (inner mesh) and show the cells
# image (outer mesh) as a wireframe. We also plot the cell centers in red. Note
# how the centers of the image of the cells correspond to the points of the points image.

cell_centers = cells_image.cell_centers()

plot = pv.Plotter()
plot.add_mesh(points_image, color=True, opacity=0.7)
plot.add_mesh(cells_image, style='wireframe', color='black', line_width=2)
plot.add_points(
    cell_centers,
    render_points_as_spheres=True,
    color='red',
    point_size=20,
)
plot.view_xy()
plot.show()
# %%
# .. tags:: filter