¿Cómo mostrar una gráfica 3D de una matriz 3D isosuperficie en matplotlib mplot3D o similar?

Tengo una matriz numpy tridimensional. Me gustaría mostrar (en matplotlib) una bonita gráfica 3D de una isosuperficie de esta matriz (o, más estrictamente, visualizar una isosuperficie del campo escalar 3D definido mediante la interpolación entre los puntos de muestra).

La parte mplot3D de matplotlib proporciona un agradable soporte de gráficos 3D, pero (por lo que puedo ver) su API no tiene nada que simplemente tome una matriz 3D de valores escalares y muestre una superficie isosuperficial. Sin embargo, sí admite la visualización de una colección de polígonos, por lo que presumiblemente podría implementar el algoritmo de cubos de marcha para generar dichos polígonos.

Parece bastante probable que ya se hayan implementado unos cubos de marcha amigables a los scipy y que no lo encontré, o que me esté perdiendo una forma fácil de hacerlo. Alternativamente, agradecería cualquier puntero a otras herramientas para visualizar datos de matriz 3D fácilmente utilizables desde el mundo Python / numpy / scipy.

Solo para elaborar mi comentario anterior, el trazado 3D de matplotlib realmente no está diseñado para algo tan complejo como las isosuperficies. Está diseñado para producir una salida vectorial agradable y con calidad de publicación para gráficos 3D realmente simples. No puede manejar polígonos 3D complejos, por lo que incluso si implementas cubos de marcha para crear la superficie iso, no se procesaría correctamente.

Sin embargo, lo que puede hacer es usar mayavi (es mlab API es un poco más conveniente que usar directamente mayavi), que usa VTK para procesar y visualizar datos multidimensionales.

Como ejemplo rápido (modificado de uno de los ejemplos de la galería de 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() 

introduzca la descripción de la imagen aquí

Complementando la respuesta de @DanHickstein, también puede usar trisurf para visualizar los polígonos obtenidos en la fase de cubos en marcha.

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

introduzca la descripción de la imagen aquí

Actualización: 11 de mayo de 2018.

Como lo menciona @DrBwts, ahora marching_cubes devuelve 4 valores. El siguiente código funciona.

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

Si quiere mantener sus gráficos en matplotlib (en mi opinión, es mucho más fácil producir imágenes con calidad de publicación que mayavi), entonces puede usar la función marching_cubes implementada en skimage y luego trazar los resultados en matplotlib usando

 mpl_toolkits.mplot3d.art3d.Poly3DCollection 

como se muestra en el enlace de arriba. Matplotlib hace un buen trabajo al renderizar la isosuperficie. Aquí hay un ejemplo que hice de algunos datos de tomografía real:

introduzca la descripción de la imagen aquí