web-dev-qa-db-fra.com

Comment afficher un tracé 3D d'une isosurface de matrice 3D dans matplotlib mplot3D ou similaire?

J'ai un tableau numpy en 3 dimensions. Je voudrais afficher (dans matplotlib) un joli tracé 3D d'une isosurface de ce tableau (ou plus strictement, afficher une isosurface du champ scalaire 3D défini par interpolation entre les points d'échantillonnage).

la partie mplot3D de matplotlib fournit un support de tracé 3D agréable, mais (pour autant que je puisse voir) son API n'a rien qui prendra simplement un tableau 3D de valeurs scalaires et affichera une isosurface. Cependant, il prend en charge l'affichage d'une collection de polygones, donc je suppose que je pourrais implémenter l'algorithme des cubes de marche pour générer de tels polygones.

Il semble très probable qu'un cube de marche convivial pour les scipy ait déjà été implémenté quelque part et que je ne l'ai pas trouvé, ou que je manque un moyen facile de le faire. Sinon, j'accueillerais volontiers tous les pointeurs vers d'autres outils pour visualiser les données de tableaux 3D facilement utilisables depuis le monde Python/numpy/scipy.

42
timday

Juste pour développer mon commentaire ci-dessus, le tracé 3D de matplotlib n'est vraiment pas destiné à quelque chose d'aussi complexe que les isosurfaces. Il est destiné à produire une belle sortie vectorielle de qualité publication pour des tracés 3D très simples. Il ne peut pas gérer des polygones 3D complexes, donc même si vous implémentez vous-même des cubes de marche pour créer l'isosurface, il ne le rendra pas correctement.

Cependant, ce que vous pouvez faire à la place, c'est utiliser mayavi (c'est mlab API est un peu plus pratique que d'utiliser directement mayavi), qui utilise VTK pour traiter et visualiser des données multidimensionnelles.

À titre d'exemple rapide (modifié à partir d'un des exemples de la galerie mayavi):

import numpy as np
from enthought.mayavi import mlab

x, y, z = np.ogrid[-10:10:20j, -10:10:20j, -10:10:20j]
s = np.sin(x*y*z)/(x*y*z)

src = mlab.pipeline.scalar_field(s)
mlab.pipeline.iso_surface(src, contours=[s.min()+0.1*s.ptp(), ], opacity=0.3)
mlab.pipeline.iso_surface(src, contours=[s.max()-0.1*s.ptp(), ],)

mlab.show()

enter image description here

39
Joe Kington

En complément de la réponse de @DanHickstein, vous pouvez également utiliser trisurf pour visualiser les polygones obtenus dans la phase des cubes en marche.

import numpy as np
from numpy import sin, cos, pi
from skimage import measure
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D


def fun(x, y, z):
    return cos(x) + cos(y) + cos(z)

x, y, z = pi*np.mgrid[-1:1:31j, -1:1:31j, -1:1:31j]
vol = fun(x, y, z)
verts, faces = measure.marching_cubes(vol, 0, spacing=(0.1, 0.1, 0.1))

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(verts[:, 0], verts[:,1], faces, verts[:, 2],
                cmap='Spectral', lw=1)
plt.show()

enter image description here

Mise à jour: 11 mai 2018

Comme mentionné par @DrBwts, marching_cubes retourne désormais 4 valeurs. Le code suivant fonctionne.

import numpy as np
from numpy import sin, cos, pi
from skimage import measure
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D


def fun(x, y, z):
    return cos(x) + cos(y) + cos(z)

x, y, z = pi*np.mgrid[-1:1:31j, -1:1:31j, -1:1:31j]
vol = fun(x, y, z)
verts, faces, _, _ = measure.marching_cubes(vol, 0, spacing=(0.1, 0.1, 0.1))

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(verts[:, 0], verts[:,1], faces, verts[:, 2],
                cmap='Spectral', lw=1)
plt.show()
20
nicoguaro

Si vous souhaitez conserver vos tracés dans matplotlib (beaucoup plus facile à produire des images de qualité publication que mayavi à mon avis), vous pouvez utiliser la fonction marching_cubes implémentée dans skimage puis tracer les résultats dans matplotlib en utilisant

mpl_toolkits.mplot3d.art3d.Poly3DCollection

comme indiqué dans le lien ci-dessus. Matplotlib fait un très bon travail de rendu de l'isosurface. Voici un exemple que j'ai fait de vraies données tomographiques:

enter image description here

12
DanHickstein