如何使用mplot3D或类似工具显示3D数组等值面的3D图。

57

我有一个三维的numpy数组。我想在matplotlib中显示这个数组的等值面(或更严格地说,显示由插值样本点定义的三维标量场的等值面)。

matplotlib的mplot3D部分提供了很好的三维图形支持,但是(就我所知)它的API没有任何东西可以简单地获取一个三维标量值数组并显示等值面。然而,它支持显示一组多边形,因此我可以实现Marching Cubes算法来生成这样的多边形。

似乎已经在某个地方实现了一个友好的scipy Marching Cubes算法,而我还没有找到它,或者我错过了一些简单的方法。另外,我欢迎任何指向从Python/numpy/scipy世界轻松使用的其他3D数组数据可视化工具的指针。


4
Matplotlib的三维绘图并不适用于像这样的情况。(它的目的是为了生成简单三维图形的矢量输出,而不是作为一个完整的三维绘图引擎。)如果你想要等值面,请使用mayavi/mlab。 - Joe Kington
4个回答

51

我之前的评论是想进一步解释,matplotlib的3D绘图并不适用于像等值面这样复杂的绘图。它旨在为简单的三维绘图生成漂亮的、出版质量的矢量输出。它无法处理复杂的三维多边形,因此即使你自己使用Marching Cubes创建等值面,它也无法正确地呈现。

但是,您可以使用Mayavi(它的mlab API比直接使用Mayavi更方便),它使用VTK来处理和可视化多维数据。

以下是一个快速示例(修改自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()

在此输入图片描述


5
太好了!执行apt-get install mayavi2,运行你的代码... 完美!正是我想要的。多年来我一直在想是否应该以某种方式利用VTK;从scipy世界进入这个领域看起来很不错。哦天啊,就像发现一个全新的星球一样... - timday
1
还有一个 mlab contour3d 函数可以让像上面那样的东西变得更简单:http://github.enthought.com/mayavi/mayavi/auto/mlab_helper_functions.html#contour3d - timday
4
取消那个操作,传入一个特定值列表在最新版本中似乎完美地起作用了,不管价值如何。 - Joe Kington
1
我刚刚看到它可以很好地处理一些512^3的数组。有趣的是,contour3d的峰值内存消耗似乎比上面的“管道”版本要低得多(大约2.5GB vs 8GB;幸运的是我在一个大型的64位系统上)。不过还没有尝试使用像np.array(...,dtype=np.int16)这样的东西(我认为np数组默认为double类型)。 - timday
2
在Ubuntu 15.04上,我需要做一点修改,如下所示:from mayavi import mlab - Jean-Pat
显示剩余3条评论

46

补充@DanHickstein的回答,您还可以使用trisurf来可视化在Marching Cubes阶段获得的多边形。

补充@DanHickstein的回答,您还可以使用trisurf来可视化在Marching Cubes阶段获得的多边形。

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)
iso_val=0.0
verts, faces = measure.marching_cubes(vol, iso_val, 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()

这里输入图片描述

更新:2018年5月11日

如@DrBwts所述,现在marching_cubes返回4个值。以下代码有效。

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)
iso_val=0.0
verts, faces, _, _ = measure.marching_cubes(vol, iso_val, 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()

更新:2020年2月2日

补充我的先前回答,我应该提到自那时以来已经发布了PyVista,它使这种任务变得更加轻松。

按照之前的例子进行操作。

from numpy import cos, pi, mgrid
import pyvista as pv

#%% Data
x, y, z = pi*mgrid[-1:1:31j, -1:1:31j, -1:1:31j]
vol = cos(x) + cos(y) + cos(z)
grid = pv.StructuredGrid(x, y, z)
grid["vol"] = vol.flatten()
contours = grid.contour([0])

#%% Visualization
pv.set_plot_theme('document')
p = pv.Plotter()
p.add_mesh(contours, scalars=contours.points[:, 2], show_scalar_bar=False)
p.show()

以下是结果

enter image description here

更新:2020年2月24日

如@HenriMenke所述,marching_cubes已更名为marching_cubes_lewiner。 "新"片段如下。

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

x, y, z = pi*np.mgrid[-1:1:31j, -1:1:31j, -1:1:31j]
vol = cos(x) + cos(y) + cos(z)
iso_val=0.0
verts, faces, _, _ = marching_cubes_lewiner(vol, iso_val, 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()

1
marching_cubes现在返回4个值,如果你改成verts, faces, sumit, sumitelse = measure.marching_cubes(vol, 0, spacing=(0.1, 0.1, 0.1)),上面的代码就可以工作了。 - DrBwts
1
@AndrasDeak,我认为这份文档非常好,并且有很多示例。话虽如此,使用起来有点不同,但并不太难。 - nicoguaro
1
好的,我仔细检查了PyVista文档。你说得对,它似乎非常灵活和强大,即使其API与我所习惯的有点不同,但文档确实充满了示例和有用的交叉链接。我一定会尝试一下的。 - Andras Deak -- Слава Україні
1
似乎marching_cubes已被重命名为marching_cubes_lewiner - Henri Menke
trisurf 的唯一问题是颜色映射:它将遵循 Z 轴 (verts[:,2]),它没有面颜色,这可能会对定量评估造成问题。即使 mayavi 被认为是复杂的,它可以绘制等值面,但您也可以将数据导出到 XML 并处理 Paraview 或其他 VTK 查看器。 - nsaura

17

如果您想保留matplotlib中的图形(在我看来比mayavi更容易生成出版品质量的图像),那么您可以使用在skimage中实现的marching_cubes函数,然后使用matplotlib绘制结果。

mpl_toolkits.mplot3d.art3d.Poly3DCollection

如上链接所示。Matplotlib在渲染等值面方面表现得非常好。以下是我根据一些真实层析成像数据制作的示例:

enter image description here


2
自从最初的帖子提交以来,Matplotlib已经提供了增强和额外的软件包。这使得Matplotlib成为创建许多类型的“复杂”3D图的可行方法。
使用Matplotlib的两个主要优点是轻松显示坐标轴和在同一图中使用多个轴(子图)。使用这两个功能可以增强一个概念。例如,等距表面的周期性特征在下图中被“展示”出来。

spherical harmonics

这个3D图形显示了“负”空间与“正”空间之间的π位移,通过使用不同颜色来区分前表面和后表面。这个图形是使用S3Dlibscikit-image这两个Matplotlib的第三方包生成的。Matplotlib完成了渲染的重要工作。代码如下:
import numpy as np
from skimage.measure import marching_cubes_lewiner
import matplotlib.pyplot as plt
import s3dlib.surface as s3d
import s3dlib.cmap_utilities as cmu

bcmap = cmu.binary_cmap('orange', 'yellowgreen')
# ...................................................................
fig = plt.figure(figsize=plt.figaspect(.5))
minmax = (-2*np.pi, 2*np.pi)
for i,[dmn,title] in enumerate ( [ \
        [2*np.pi, r'-2$\pi$ < surface < 2$\pi$'],
        [  np.pi,  r'-$\pi$ < central section < $\pi$']   ] ) :
    
    x, y, z = dmn*np.mgrid[-1:1:61j, -1:1:61j, -1:1:61j]
    vol = np.cos(x) + np.cos(y) + np.cos(z)  # Schwarz P 
    verts, faces, _, _ = marching_cubes_lewiner(vol, 0.001, (0.1, 0.1, 0.1))
    verts = dmn*( verts-(3,3,3) )/3
    surface = s3d.Surface3DCollection( verts, faces, cmap=bcmap)
    
    ax =fig.add_subplot(121+i, projection='3d')
    ax.set(xlabel='X',ylabel='Y',zlabel='Z',title=title,
        xlim=minmax,ylim=minmax,zlim=minmax )
    surface.map_cmap_from_normals(direction=ax)
    ax.add_collection3d(surface.shade(.3).hilite(0.7,focus=2))

fig.tight_layout()
plt.show()

使用子图还可以展示三维曲面几何的功能参数。例如,球面调和中的阶数和次数可以通过下图中的曲面表格进行可视化展示。

spherical harmonics

生成此图的代码在球谐函数示例中给出,并使用SciPy软件包进行球谐函数的计算。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接