1

我想创建由 2D 多边形组成的 3D 对象,每个对象都有一个由单个 jpeg 图像组成的纹理。我有多边形的 X、Y 和 Z 值的 3D 坐标以及 [0, 1] 间隔中的纹理坐标。我可以使用 matplotlib 在 matplotlib 中绘制 3D 对象Poly3DCollection,但到目前为止,matplotlib 不支持多边形的纹理映射。我找到了 PyVista,这似乎是纹理映射的不错选择,但我不明白如何从我的数据中创建与 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()

我将不胜感激在正确方向上的任何帮助!

4

1 回答 1

2

对于任何有类似问题的人:我发现了两个易于使用的包,并提供了纹理映射到 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 问题实现该功能的开发人员。总的来说,这两个包都很好地解决了这个问题,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)
于 2019-11-24T17:43:50.993 回答