Как отобразить трехмерный график изоповерхности трехмерного массива в matplotlib mplot3D или аналогичной? - PullRequest
41 голосов
/ 17 мая 2011

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

Часть mplot3D в matplotlib обеспечивает хорошую поддержку трехмерных графиков, но (насколько я вижу) в ее API нет ничего, что просто приняло бы трехмерный массив скалярных значений и отобразил бы изоповерхность. Тем не менее, он поддерживает отображение набора полигонов, так что, вероятно, я мог бы реализовать алгоритм марширующих кубов для генерации таких полигонов.

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

Ответы [ 3 ]

39 голосов
/ 17 мая 2011

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

Однако вместо этого вы можете использовать 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()

enter image description here

20 голосов
/ 18 февраля 2016

В дополнение к ответу @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()
12 голосов
/ 01 октября 2014

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

mpl_toolkits.mplot3d.art3d.Poly3DCollection

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

enter image description here

...