在 Python 中绘制 3d 数组的最有效方法是什么?

新手上路,请多包涵

在 Python 中绘制 3d 数组的最有效方法是什么?

例如:

 volume = np.random.rand(512, 512, 512)

其中数组项表示每个像素的灰度颜色。


以下代码运行速度太慢:

 import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.gca(projection='3d')
volume = np.random.rand(20, 20, 20)
for x in range(len(volume[:, 0, 0])):
    for y in range(len(volume[0, :, 0])):
        for z in range(len(volume[0, 0, :])):
            ax.scatter(x, y, z, c = tuple([volume[x, y, z], volume[x, y, z], volume[x, y, z], 1]))
plt.show()

原文由 Dmitry 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 390
2 个回答

首先,512x512x512 点的密集网格太多数据无法绘制,这不是从技术角度来看,而是从观察绘图时能够从中看到任何有用的东西。您可能需要提取一些等值面、查看切片等。如果大多数点是不可见的,那么它可能没问题,但是您应该要求 ax.scatter 只显示非零点以使其更快。

也就是说,这是您可以更快地完成它的方法。技巧是消除所有 Python 循环,包括隐藏在库中的循环,例如 itertools

 import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt

# Make this bigger to generate a dense grid.
N = 8

# Create some random data.
volume = np.random.rand(N, N, N)

# Create the x, y, and z coordinate arrays.  We use
# numpy's broadcasting to do all the hard work for us.
# We could shorten this even more by using np.meshgrid.
x = np.arange(volume.shape[0])[:, None, None]
y = np.arange(volume.shape[1])[None, :, None]
z = np.arange(volume.shape[2])[None, None, :]
x, y, z = np.broadcast_arrays(x, y, z)

# Turn the volumetric data into an RGB array that's
# just grayscale.  There might be better ways to make
# ax.scatter happy.
c = np.tile(volume.ravel()[:, None], [1, 3])

# Do the plotting in a single call.
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.scatter(x.ravel(),
           y.ravel(),
           z.ravel(),
           c=c)

原文由 Mr Fooz 发布,翻译遵循 CC BY-SA 3.0 许可协议

为了获得更好的性能,请尽可能避免多次调用 ax.scatter 。 Instead, pack all the x , y , z coordinates and colors into 1D arrays (or lists), then call ax.scatter once:

 ax.scatter(x, y, z, c=volume.ravel())


问题(就 CPU 时间和内存而言)增长为 size**3 ,其中 size 是立方体的边长。

此外, ax.scatter 将尝试渲染所有 size**3 点,而不考虑这些点中的大多数被外壳上的点遮挡的事实。

这将有助于减少 volume 中的点数——可能通过以某种方式对其进行汇总或重新采样/插值——在渲染之前。

我们还可以将所需的 CPU 和内存从 O(size**3) 减少到 O(size**2) 通过仅绘制外壳:

 import functools
import itertools as IT
import numpy as np
import scipy.ndimage as ndimage
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def cartesian_product_broadcasted(*arrays):
    """
    http://stackoverflow.com/a/11146645/190597 (senderle)
    """
    broadcastable = np.ix_(*arrays)
    broadcasted = np.broadcast_arrays(*broadcastable)
    dtype = np.result_type(*arrays)
    rows, cols = functools.reduce(np.multiply, broadcasted[0].shape), len(broadcasted)
    out = np.empty(rows * cols, dtype=dtype)
    start, end = 0, rows
    for a in broadcasted:
        out[start:end] = a.reshape(-1)
        start, end = end, end + rows
    return out.reshape(cols, rows).T

# @profile  # used with `python -m memory_profiler script.py` to measure memory usage
def main():
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1, projection='3d')

    size = 512
    volume = np.random.rand(size, size, size)
    x, y, z = cartesian_product_broadcasted(*[np.arange(size, dtype='int16')]*3).T
    mask = ((x == 0) | (x == size-1)
            | (y == 0) | (y == size-1)
            | (z == 0) | (z == size-1))
    x = x[mask]
    y = y[mask]
    z = z[mask]
    volume = volume.ravel()[mask]

    ax.scatter(x, y, z, c=volume, cmap=plt.get_cmap('Greys'))
    plt.show()

if __name__ == '__main__':
    main()

在此处输入图像描述

但请注意,即使仅绘制外壳,要使用 size=512 绘制图,我们仍然需要大约 1.3 GiB 的内存。还要注意,即使您有足够的总内存,但由于缺少 RAM,程序使用交换空间,程序的整体速度也会显着降低。如果您发现自己处于这种情况,那么唯一的解决办法就是找到一种更聪明的方法来 使用更少的点 渲染可接受的图像,或者购买更多的 RAM。

在此处输入图像描述

原文由 unutbu 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题