Подтвердить что ты не робот

Как отобразить 3D-график 3D-массива isosurface в matplotlib mplot3D или аналогичный?

У меня есть трехмерный массив numpy. Я хотел бы отобразить (в matplotlib) хороший 3D-график изоповерхности этого массива (или, более строго, отобразить изоповерхность 3D-скалярного поля, определяемую интерполяцией между точками выборки).

matplotlib mplot3D обеспечивает приятную поддержку 3D-графики, но (насколько я вижу) его API не имеет ничего, что просто возьмет 3D-массив скалярных значений и отобразит изоповерхность. Однако он поддерживает отображение набора полигонов, поэтому, предположительно, я мог бы реализовать алгоритм маршевых кубов для создания таких полигонов.

Кажется вполне вероятным, что скользкие дружественные маршевые кубы уже были реализованы где-то и что я его не нашел, или что мне не хватает простого способа сделать это. В качестве альтернативы я бы приветствовал любые указатели на другие инструменты для визуализации данных 3D-массива, которые легко можно использовать в мире Python/numpy/scipy.

4b9b3361

Ответ 1

Чтобы подробно остановиться на моем комментарии выше, 3D-графика matplotlib действительно не предназначена для чего-то сложного, как изоповерхности. Это предназначалось для создания красивого векторного вывода для публикации на самом деле простых 3D-графиков. Он не может обрабатывать сложные 3D-полигоны, поэтому даже если они будут реализовывать маршевые кубы самостоятельно, чтобы создать изоповерхность, это не отразило бы его должным образом.

Однако вместо этого вы можете использовать mayavi (это mlab API немного удобнее, чем непосредственно с помощью майави), который использует VTK для обработки и визуализации многомерных данных.

В качестве быстрого примера (измененного из одного из примеров майави галереи):

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

Ответ 2

Дополняя ответ @DanHickstein, вы также можете использовать trisurf для визуализации полигонов, полученных на фазе маршевых кубов.

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

Обновление: 11 мая 2018 года

Как уже упоминалось @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)
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()

Ответ 3

Если вы хотите сохранить свои сюжеты в matplotlib (гораздо проще создавать изображения с качеством публикации, чем майави, на мой взгляд), то вы можете использовать функцию marching_cubes, реализованную в skimage, а затем построить результаты в matplotlib, используя

mpl_toolkits.mplot3d.art3d.Poly3DCollection

как показано в ссылке выше. Matplotlib делает довольно хорошую работу по визуализации изоповерхности. Вот пример, который я сделал из некоторых реальных томографических данных:

enter image description here