Oberflächen erstellen#

In diesem Notebook erstellen wir eine Oberfläche (Mesh) aus einem 3D-Datensatz eines simulierten 3D-Binärbilddatensatzes.

import napari_process_points_and_surfaces as nppas
import pyclesperanto_prototype as cle
import vedo

from branchoid import branchoid
binary_image = branchoid()
binary_image
shape(100, 100, 100)
dtypeuint8
size976.6 kB
min0
max1

Oberflächen generieren#

Zuerst generieren wir eine Oberfläche aus dem Binärbild. In diesem Fall nehmen wir alle Pixel mit Nicht-Null-Werten und verwandeln sie in eine Oberfläche.

surface = nppas.all_labels_to_surface(binary_image)

Das resultierende Objekt wird in Jupyter Notebooks so visualisiert:

surface
nppas.SurfaceTuple
origin (z/y/x)[0. 0. 0.]
center of mass(z/y/x)50.000,46.575,42.589
scale(z/y/x)1.000,1.000,1.000
bounds (z/y/x)25.500...74.500
2.500...88.500
2.500...83.500
average size31.277
number of vertices19040
number of faces38076

Technisch gesehen handelt es sich um ein Tupel.

isinstance(surface, tuple)
True

Das Tupel enthält Vertices und Faces.

vertices, faces = surface

Vertices sind Listen von Listen von Z/Y/X-Koordinaten im 3D-Raum.

vertices
array([[25.5, 44. , 47. ],
       [26. , 43.5, 47. ],
       [26. , 44. , 46.5],
       ...,
       [74.5, 56. , 51. ],
       [74.5, 56. , 52. ],
       [74.5, 56. , 53. ]], dtype=float32)

Faces sind Listen von Listen von Indizes. Jedes Dreieck hat drei Punktkoordinaten, die wie folgt indiziert sind:

faces
array([[    2,     1,     0],
       [    4,     3,     0],
       [    4,     0,     1],
       ...,
       [19038, 18870, 18872],
       [19038, 18872, 19039],
       [19039, 18872, 18852]], dtype=int64)

Oberflächen aus einzelnen Labels#

Wenn wir ein Labelbild als Ausgangspunkt haben, können wir auch einzelne Objekte in Oberflächen umwandeln.

labels = cle.voronoi_otsu_labeling(binary_image, spot_sigma=6)
labels
cle._ image
shape(100, 100, 100)
dtypeuint32
size3.8 MB
min0.0
max2.0
nppas.largest_label_to_surface(labels)
nppas.SurfaceTuple
origin (z/y/x)[0. 0. 0.]
center of mass(z/y/x)50.000,53.374,48.714
scale(z/y/x)1.000,1.000,1.000
bounds (z/y/x)25.000...75.000
25.000...89.000
12.000...84.000
average size28.026
number of vertices8278
number of faces16552
nppas.label_to_surface(labels, label_id=1)
nppas.SurfaceTuple
origin (z/y/x)[0. 0. 0.]
center of mass(z/y/x)50.000,18.136,18.136
scale(z/y/x)1.000,1.000,1.000
bounds (z/y/x)36.000...64.000
2.000...38.000
2.000...38.000
average size15.361
number of vertices2472
number of faces4952
nppas.label_to_surface(labels, label_id=2)
nppas.SurfaceTuple
origin (z/y/x)[0. 0. 0.]
center of mass(z/y/x)50.000,53.374,48.714
scale(z/y/x)1.000,1.000,1.000
bounds (z/y/x)25.000...75.000
25.000...89.000
12.000...84.000
average size28.026
number of vertices8278
number of faces16552

Oberflächen mit vedo erstellen#

Vedo bietet auch Funktionen zum Erstellen von Oberflächen wie iso_surface().

volume = vedo.Volume(binary_image)

iso_surface = volume.isosurface()
iso_surface

vedo.mesh.Mesh
bounds
(x/y/z)
25.33 ... 74.67
2.333 ... 88.67
2.333 ... 83.67
center of mass (50.0, 46.6, 42.6)
average size 31.358
nr. points / faces 19040 / 38076
point data array input_scalars

Die resultierende Datenstruktur ist ein vedo Mesh. Sie können auch auf dessen Punkte und Flächen zugreifen.

iso_surface.points()
array([[49.       , 11.       ,  2.3333333],
       [50.       , 11.       ,  2.3333333],
       [51.       , 11.       ,  2.3333333],
       ...,
       [50.       , 55.       , 83.666664 ],
       [51.       , 55.       , 83.666664 ],
       [52.       , 55.       , 83.666664 ]], dtype=float32)
iso_surface.faces()[:10]
[[0, 92, 104],
 [0, 1, 93],
 [92, 0, 93],
 [1, 2, 94],
 [93, 1, 94],
 [94, 2, 105],
 [3, 106, 118],
 [3, 4, 107],
 [106, 3, 107],
 [104, 107, 4]]

Übung#

Laden Sie den skimage.data.cells3d Datensatz, extrahieren Sie den zweiten Kanal und erstellen Sie ein Oberflächennetz aus den Zellkernen.