如何使用PyVista从列表/数组创建自定义三维对象?

2024-04-19 09:32:48 发布

您现在位置:Python中文网/ 问答频道 /正文

我想创建由2D多边形组成的3D对象,每个多边形都有一个由单个jpeg图像组成的纹理。我有多边形的X、Y和Z值的三维坐标,以及[0,1]间隔内的纹理坐标。我可以使用Poly3DCollection在matplotlib中绘制三维对象,但正如我所读到的,matplotlib不支持多边形的纹理映射。我找到了PyVista,这似乎是texture mapping的一个好选择,但我不知道如何从我的数据中创建与PyVista兼容的数据集。下面是我当前正在使用的matplotlib示例:

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

# six polygons consisting of points with X, Y, and Z coordinates 
polygon_a = [
    [
        (371982, 5812893, 47),
        (371987, 5812889, 47),
        (371993, 5812896, 47),
        (371988, 5812900, 47),
        (371982, 5812893, 47),
    ]
]
polygon_b = [
    [
        (371987, 5812889, 44),
        (371987, 5812889, 47),
        (371982, 5812893, 47),
        (371982, 5812893, 44),
        (371987, 5812889, 44),
    ]
]
polygon_c = [
    [
        (371993, 5812896, 44),
        (371993, 5812896, 47),
        (371987, 5812889, 47),
        (371987, 5812889, 44),
        (371993, 5812896, 44),
    ]
]
polygon_d = [
    [
        (371982, 5812893, 44),
        (371982, 5812893, 47),
        (371988, 5812900, 47),
        (371988, 5812900, 44),
        (371982, 5812893, 44),
    ]
]
polygon_e = [
    [
        (371988, 5812900, 44),
        (371988, 5812900, 47),
        (371993, 5812896, 47),
        (371993, 5812896, 44),
        (371988, 5812900, 44),
    ]
]
polygon_f = [
    [
        (371987, 5812889, 44),
        (371982, 5812893, 44),
        (371988, 5812900, 44),
        (371993, 5812896, 44),
        (371987, 5812889, 44),
    ]
]

# texture coordinates of interval [0, 1]

texture_coords_a = [
    0.993515,
    0.590665,
    0.583403,
    0.995886,
    0.001318,
    0.409513,
    0.411194,
    0.00281,
    0.993515,
    0.590665,
]
texture_coords_b = [
    0.814495,
    0.004965,
    0.986562,
    0.175202,
    0.172649,
    0.994582,
    0.004011,
    0.820917,
    0.814495,
    0.004965,
]
texture_coords_c = [
    0.992976,
    0.869131,
    0.867654,
    0.99699,
    0.009377,
    0.134356,
    0.138307,
    0.010153,
    0.992976,
    0.869131,
]
texture_coords_d = [
    0.007693,
    0.148416,
    0.15451,
    0.00767,
    0.994519,
    0.86112,
    0.844256,
    0.998197,
    0.007693,
    0.148416,
]
texture_coords_e = [
    0.997322,
    0.660826,
    0.89938,
    0.990736,
    0.006374,
    0.337104,
    0.106732,
    0.00748,
    0.997322,
    0.660826,
]

# textures for some of the polygons as .jpg-files

img_a = "tex_2962910.jpg"
img_b = "tex_2962971.jpg"
img_c = "tex_2962990.jpg"
img_d = "tex_2962933.jpg"
img_e = "tex_2962915.jpg"

polygons = [polygon_a, polygon_b, polygon_c, polygon_d, polygon_e, polygon_f]

# 3D plot of the polygons using matplotlib, but without textures
fig = plt.figure()
ax = Axes3D(fig)
for polygon in polygons:
    ax.add_collection3d(Poly3DCollection(polygon, alpha=0.5))

ax.set_xlim3d(371980, 371995)
ax.set_ylim3d(5812889, 5812902)
ax.set_zlim3d(44, 48)

plt.show()

我将感谢任何帮助在正确的方向!你知道吗


Tags: ofimportimgmatplotlibpltcoordsax多边形
1条回答
网友
1楼 · 发布于 2024-04-19 09:32:48

对于有类似问题的人:我发现了两个易于使用的软件包,它们提供了到numpy多边形的纹理映射所需的功能。第一个是PyVista,工作代码如下:

import pyvista as pv
import numpy as np
from PIL import Image

# six polygons consisting of points with X, Y, and Z coordinates
polygon_a = [
    (371982, 5812893, 47),
    (371987, 5812889, 47),
    (371993, 5812896, 47),
    (371988, 5812900, 47),
    (371982, 5812893, 47),
]
polygon_b = [
    (371987, 5812889, 44),
    (371987, 5812889, 47),
    (371982, 5812893, 47),
    (371982, 5812893, 44),
    (371987, 5812889, 44),
]
polygon_c = [
    (371993, 5812896, 44),
    (371993, 5812896, 47),
    (371987, 5812889, 47),
    (371987, 5812889, 44),
    (371993, 5812896, 44),
]
polygon_d = [
    (371982, 5812893, 44),
    (371982, 5812893, 47),
    (371988, 5812900, 47),
    (371988, 5812900, 44),
    (371982, 5812893, 44),
]
polygon_e = [
    (371988, 5812900, 44),
    (371988, 5812900, 47),
    (371993, 5812896, 47),
    (371993, 5812896, 44),
    (371988, 5812900, 44),
]


texture_coords_a = np.array(
    [
        [0.993515, 0.590665],
        [0.583403, 0.995886],
        [0.001318, 0.409513],
        [0.411194, 0.00281],
        [0.993515, 0.590665],
    ]
)

texture_coords_b = np.array(
    [
        [0.814495, 0.004965],
        [0.986562, 0.175202],
        [0.172649, 0.994582],
        [0.004011, 0.820917],
        [0.814495, 0.004965],
    ]
)

texture_coords_c = np.array(
    [
        [0.992976, 0.869131],
        [0.867654, 0.99699],
        [0.009377, 0.134356],
        [0.138307, 0.010153],
        [0.992976, 0.869131],
    ]
)

texture_coords_d = np.array(
    [
        [0.007693, 0.148416],
        [0.15451, 0.00767],
        [0.994519, 0.86112],
        [0.844256, 0.998197],
        [0.007693, 0.148416],
    ]
)
texture_coords_e = np.array(
    [
        [0.997322, 0.660826],
        [0.89938, 0.990736],
        [0.006374, 0.337104],
        [0.106732, 0.00748],
        [0.997322, 0.660826],
    ]
)


# define polygon faces for each polygon
faces_a = np.hstack([[4, 0, 1, 2, 3]])
faces_b = np.hstack([[4, 0, 1, 2, 3]])
faces_c = np.hstack([[4, 0, 1, 2, 3]])
faces_d = np.hstack([[4, 0, 1, 2, 3]])
faces_e = np.hstack([[4, 0, 1, 2, 3]])

# textures for some of the polygons as .jpg-files

img_a = "tex_2962910.jpg"
img_b = "tex_2962971.jpg"
img_c = "tex_2962990.jpg"
img_d = "tex_2962933.jpg"
img_e = "tex_2962915.jpg"

polygons = [polygon_a, polygon_b, polygon_c, polygon_d, polygon_e]
faces = [faces_a, faces_b, faces_c, faces_d, faces_e]
textures = [img_a, img_b, img_c, img_d, img_e]
texture_coords = [
    texture_coords_a,
    texture_coords_b,
    texture_coords_c,
    texture_coords_d,
    texture_coords_e,
]


def create_meshes_with_textures(polygon, face, texture, texture_coords):
    """ Opens each image with PIL, converts them to np.arrays and converts those to 
    VTK textures. Each textur gets mapped to a polygon according to the coordinates.
    """
    img = Image.open(texture)
    img.load()
    img = np.asarray(img, dtype=np.uint8)
    img = pv.numpy_to_texture(img)

    polygon = np.array(polygon)
    mesh = pv.PolyData(polygon, face)
    mesh.t_coords = texture_coords
    return mesh, img


# create Plotter object from PyVista
p = pv.Plotter()

for polygon, face, texture, texture_coords in zip(
    polygons, faces, textures, texture_coords
    ):

    # map the textures to the polygons
    mesh, img = create_meshes_with_textures(polygon, face, texture, texture_coords)
    # add the resulting meshes to the Plotter object
    p.add_mesh(mesh, texture=img)

p.show()

第二种选择是使用vtkplotter,这要感谢通过这个GitHub Issue实现特性的开发人员。总的来说,这两个软件包都能很好地解决这个问题,vtkplotter需要较少的纹理数据转换步骤。你知道吗

from vtkplotter import *
import numpy as np

# six polygons consisting of points with X, Y, and Z coordinates
polygon_a = [
    [
        (371982, 5812893, 47),
        (371987, 5812889, 47),
        (371993, 5812896, 47),
        (371988, 5812900, 47),
        (371982, 5812893, 47),
    ],
    [[0, 1, 2, 3, 4]],
]
polygon_b = [
    [
        (371987, 5812889, 44),
        (371987, 5812889, 47),
        (371982, 5812893, 47),
        (371982, 5812893, 44),
        (371987, 5812889, 44),
    ],
    [[0, 1, 2, 3, 4]],
]

polygon_c = [
    [
        (371993, 5812896, 44),
        (371993, 5812896, 47),
        (371987, 5812889, 47),
        (371987, 5812889, 44),
        (371993, 5812896, 44),
    ],
    [[0, 1, 2, 3, 4]],
]
polygon_d = [
    [
        (371982, 5812893, 44),
        (371982, 5812893, 47),
        (371988, 5812900, 47),
        (371988, 5812900, 44),
        (371982, 5812893, 44),
    ],
    [[0, 1, 2, 3, 4]],
]
polygon_e = [
    [
        (371988, 5812900, 44),
        (371988, 5812900, 47),
        (371993, 5812896, 47),
        (371993, 5812896, 44),
        (371988, 5812900, 44),
    ],
    [[0, 1, 2, 3, 4]],
]
polygon_f = [
    [
        (371987, 5812889, 44),
        (371982, 5812893, 44),
        (371988, 5812900, 44),
        (371993, 5812896, 44),
        (371987, 5812889, 44),
    ],
    [[0, 1, 2, 3, 4]],
]

# texture coordinates of X, Y with interval [0, 1]
texture_coords_a = [
    0.993515,
    0.590665,
    0.583403,
    0.995886,
    0.001318,
    0.409513,
    0.411194,
    0.00281,
    0.993515,
    0.590665,
]
texture_coords_b = [
    0.814495,
    0.004965,
    0.986562,
    0.175202,
    0.172649,
    0.994582,
    0.004011,
    0.820917,
    0.814495,
    0.004965,
]
texture_coords_c = [
    0.992976,
    0.869131,
    0.867654,
    0.99699,
    0.009377,
    0.134356,
    0.138307,
    0.010153,
    0.992976,
    0.869131,
]
texture_coords_d = [
    0.007693,
    0.148416,
    0.15451,
    0.00767,
    0.994519,
    0.86112,
    0.844256,
    0.998197,
    0.007693,
    0.148416,
]
texture_coords_e = [
    0.997322,
    0.660826,
    0.89938,
    0.990736,
    0.006374,
    0.337104,
    0.106732,
    0.00748,
    0.997322,
    0.660826,
]

texture_coords = [
    texture_coords_a,
    texture_coords_b,
    texture_coords_c,
    texture_coords_d,
    texture_coords_e,
]

# textures for some of the polygons as .jpg-files
img_a = "tex_2962910"
img_b = "tex_2962971"
img_c = "tex_2962990"
img_d = "tex_2962933"
img_e = "tex_2962915"

polygons = [polygon_a, polygon_b, polygon_c, polygon_d, polygon_e]
textures = [img_a, img_b, img_c, img_d, img_e]

meshes = []

# loop through all polygons and their textures
for polygon, texture, texture_coord in zip(polygons, textures, texture_coords):
    # reformat texture coordinates as [(u,v), ...]
    texture_coord = np.split(np.array(texture_coord), 5)
    # create an Actor object for each polygon
    polygon = Actor(polygon)
    # map the textur with the according coordinates
    polygon.texture(texture, tcoords=texture_coord)
    meshes.append(polygon)

# assemble the objects
polygons = Assembly(meshes)

show(polygons, viewup="z", axes=8)

相关问题 更多 >