Delaunay Триангуляция точек с 2D-поверхности в 3D с помощью python?

У меня есть набор трехмерных точек. Эти точки отбираются на постоянных уровнях (z = 0,1,..., 7). Изображение должно прояснить:

point collection

Эти точки находятся в numpy ndarray формы (N, 3), называемой X. Вышеприведенный график создается с использованием:

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

X = load('points.npy')
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_wireframe(X[:,0], X[:,1], X[:,2])
ax.scatter(X[:,0], X[:,1], X[:,2])
plt.draw()

Я хотел бы вместо этого триангулировать только поверхность этого объекта и нарисовать поверхность. Я не хочу, чтобы выпуклая оболочка этого объекта, тем не менее, потому что это теряет тонкую информацию о форме, которую я хотел бы проверить.

Я пробовал ax.plot_trisurf(X[:,0], X[:,1], X[:,2]), но это приводит к следующему беспорядку:

mess

Любая помощь?

Примеры данных

Здесь приведен фрагмент для создания трехмерных данных, которые являются репрезентативными для проблемы:

import numpy as np
X = []
for i in range(8):
    t = np.linspace(0,2*np.pi,np.random.randint(30,50))
    for j in range(t.shape[0]):
        # random circular objects...
        X.append([
            (-0.05*(i-3.5)**2+1)*np.cos(t[j])+0.1*np.random.rand()-0.05,
            (-0.05*(i-3.5)**2+1)*np.sin(t[j])+0.1*np.random.rand()-0.05,
            i
        ])
X = np.array(X)

Пример данных из исходного изображения

Здесь пастебин к исходным данным:

http://pastebin.com/YBZhJcsV

Вот срезы вдоль постоянной z:

enter no image description here

Ответы

Ответ 1

обновление 2

Теперь я сделаю это следующим образом:

  • Я использую тот факт, что пути в каждом z-сегменте замкнуты и просты и используют matplotlib.path для определения точек внутри и снаружи контура. Используя эту идею, я конвертирую контуры в каждом фрагменте в булевозначное изображение, которое объединяется в булевозначный том.
  • Затем я использую метод skimage marching_cubes для получения триангуляции поверхности для визуализации.

Вот пример метода. Я думаю, что данные немного разные, но вы можете определенно увидеть, что результаты намного чище и могут обрабатывать поверхности, которые отключены или имеют отверстия.

обновление 1 (все еще плохо)

Я должен обновить это для будущих людей, которые сталкиваются с этим. Хотя вышеупомянутый метод работает большую часть времени, он принимает (через сферическое преобразование координат), что две точки не лежат вдоль одного и того же луча. Если вы заметили артефакт в середине слева от изображения выше, вот почему.

Лучшим подходом является "операция" на поверхности. Думая о поверхности, как апельсиновая корка, вы обрезаете ее с одной стороны, а затем раскройте ее, растянув ее. Затем у вас есть 2D-плоскость, которую вы можете триангулировать и интерполировать. Вам просто нужно следить за тем, как вернуться в соответствующее место в 3D. Для реализации этой идеи требуется довольно много работы, и реализация также требует особого ухода, уникального для представления моих данных.

В любом случае, это просто указание того, как можно подойти к этому более надежно.

Оригинальный ответ

Хорошо, вот решение, которое я придумал. Это зависит в значительной степени от того, что мои данные грубо сферические и сэмплированные равномерно в z, я думаю. Некоторые другие комментарии предоставляют больше информации о более надежных решениях. Поскольку мои данные примерно сферические, я триангулирую азимутальные и зенитные углы от сферического преобразования координат моих точек данных.

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

X = np.load('./mydatars.npy')
# My data points are strictly positive. This doesn't work if I don't center about the origin.
X -= X.mean(axis=0)

rad = np.linalg.norm(X, axis=1)
zen = np.arccos(X[:,-1] / rad)
azi = np.arctan2(X[:,1], X[:,0])

tris = mtri.Triangulation(zen, azi)

fig = plt.figure()
ax  = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(X[:,0], X[:,1], X[:,2], triangles=tris.triangles, cmap=plt.cm.bone)
plt.show()

Используя данные образца из пастебина выше, это дает:

no thanks


Ответ 2

Я понимаю, что вы упомянули в своем вопросе, что не хотите использовать выпуклый корпус, потому что вы можете потерять информацию о форме. У меня есть простое решение, которое очень хорошо работает для ваших данных "дрожания сферических" примеров, хотя оно использует scipy.spatial.ConvexHull. Я думал, что все равно буду делиться им здесь, на всякий случай это полезно для других:

from matplotlib.tri import triangulation
from scipy.spatial import ConvexHull

# compute the convex hull of the points
cvx = ConvexHull(X)

x, y, z = X.T

# cvx.simplices contains an (nfacets, 3) array specifying the indices of
# the vertices for each simplical facet
tri = Triangulation(x, y, triangles=cvx.simplices)

fig = plt.figure()
ax = fig.gca(projection='3d')
ax.hold(True)
ax.plot_trisurf(tri, z)
ax.plot_wireframe(x, y, z, color='r')
ax.scatter(x, y, z, color='r')

plt.draw()

enter image description here

В этом случае это очень хорошо, так как ваши данные примера попадают на более или менее выпуклую поверхность. Возможно, вы могли бы сделать несколько более сложных примеров данных? Тороидальная поверхность была бы хорошим тестовым случаем, который, очевидно, мог бы выпустить метод выпуклой оболочки.

Отображение произвольной трехмерной поверхности из облака точек - действительно сложная проблема. Здесь связанный вопрос, содержащий некоторые ссылки, которые могут быть полезны.