在Python的3D numpy数组中绘制三角形。

3
假设我有一个3D数组(矩形立方体),形状为(48,32,64)。在这个立方体中,我有三个具有一些坐标的点。 x1 = (10, 20, 30) x2 = (21, 15, 34) x3 = (33, 1, 62)
我需要在这个由这些点限制的3D数组中绘制填充平面,例如在3D数组中绘制三角形。在2D情况下,我们可以使用OpenCV进行操作: OpenCV中的三角形填充
import numpy as np
a = np.zeros((48, 32, 64), dtype=np.uint8)
x1 = (10, 20, 30)
x2 = (21, 15, 34)
x3 = (33, 1, 62)
a = draw_3D_triangle(a, x1, x2, x3)

但在3D情况下最简单的方法是什么?
2个回答

3

(修订:我之前忘了包括full_triangle()的代码。)


假设你有一个绘制线条的算法,比如Bresenham算法,并且假设它可以推广到N维情况下使用。

幸运的是,raster-geometry 包提供了这样一个N维的Bresenham算法实现。

(免责声明:我是该软件包的主要作者。)

假设A、B、C为三角形ABC顶点的坐标。


如果您只需要绘制外部轮廓,则可以使用算法使用各种点的组合来形成线段:AB、BC、CA。

在代码中,简单地写作:

import numpy as np
import raster_geometry as rg


a, b, c = (1, 1), (3, 7), (6, 4)
coords = set(rg.bresenham_lines((a, b, c), closed=True))
print(coords)
# {(1, 2), (6, 4), (3, 2), (4, 6), (5, 5), (2, 2), (2, 3), (3, 6), (2, 4), (4, 3), (3, 7), (2, 5), (1, 1), (5, 3)}
arr = rg.render_at((10, 10), coords)
print(arr.astype(int))
# [[0 0 0 0 0 0 0 0 0 0]
#  [0 1 1 0 0 0 0 0 0 0]
#  [0 0 1 1 1 1 0 0 0 0]
#  [0 0 1 0 0 0 1 1 0 0]
#  [0 0 0 1 0 0 1 0 0 0]
#  [0 0 0 1 0 1 0 0 0 0]
#  [0 0 0 0 1 0 0 0 0 0]
#  [0 0 0 0 0 0 0 0 0 0]
#  [0 0 0 0 0 0 0 0 0 0]
#  [0 0 0 0 0 0 0 0 0 0]]

如果您需要绘制一个完整的三角形,可以按照以下步骤进行:

  • 从点A到点B绘制一条直线
  • 对于AB线上的每个点,从C点绘制一条线段到该点。

虽然这种方法可能不是最高效的,但它可以正常工作。由于顶点附近可能会有一些点被遗漏,因此有必要通过循环处理所有三个顶点来重复相同的过程。

在代码中,可以这样写:

import numpy as np
import raster_geometry as rg


def full_triangle(a, b, c):
    ab = rg.bresenham_line(a, b, endpoint=True)
    for x in set(ab):
        yield from rg.bresenham_line(c, x, endpoint=True)


a, b, c = (1, 1), (3, 7), (6, 4)
coords = set(full_triangle(a, b, c))
print(coords)
# {(1, 2), (6, 4), (5, 4), (3, 2), (3, 3), (5, 5), (4, 6), (4, 5), (4, 4), (1, 1), (2, 3), (4, 3), (2, 2), (3, 6), (3, 7), (2, 5), (5, 3), (3, 4), (2, 4), (3, 5)}
arr = rg.render_at((10, 10), coords)
print(arr.astype(int))
# [[0 0 0 0 0 0 0 0 0 0]
#  [0 1 1 0 0 0 0 0 0 0]
#  [0 0 1 1 1 1 0 0 0 0]
#  [0 0 1 1 1 1 1 1 0 0]
#  [0 0 0 1 1 1 1 0 0 0]
#  [0 0 0 1 1 1 0 0 0 0]
#  [0 0 0 0 1 0 0 0 0 0]
#  [0 0 0 0 0 0 0 0 0 0]
#  [0 0 0 0 0 0 0 0 0 0]
#  [0 0 0 0 0 0 0 0 0 0]]

请注意,即使示例是在二维中展示的,它们也适用于N维。例如,你可以使用以下代码生成所需的三维三角形:
x1 = (10, 20, 30)
x2 = (21, 15, 34)
x3 = (33, 1, 62)
coords = set(full_triangle(x1, x2, x3))
arr = rg.render_at((48, 32, 64), coords)

2
为了呈现您的3D三角形,您必须将其投影到2D平面上,可以使用一些现成的解决方案(例如mplot3d),或者手动将3D数据投影到2D平面上。最简单的投影方法是正交投影,只需忽略数据的z维度即可实现。对于透视投影,请将3D数据除以z值:
import numpy as np

p1 = (10, 20, 30)
p2 = (21, 15, 34)
p3 = (33, 1, 62)

tri3d = np.array([p1, p2, p3])
ortho2d = tri3d[:, :2]
proj2d = tri3d[:, :2] / tri3d[:, 2:]

print(tri3d)
print(ortho2d)
print(proj2d)

结果:

[[10 20 30]
 [21 15 34]
 [33  1 62]]
[[10 20]
 [21 15]
 [33  1]]
[[0.33333333 0.66666667]
 [0.61764706 0.44117647]
 [0.53225806 0.01612903]]

您可以使用您在问题中提到的相同的三角形填充方法。需要考虑的是您所处空间的边界以及任何投影三角形的比例尺。请注意,投影坐标都非常小。在这种情况下,您可以通过某个因子将它们放大(相当于相机校准矩阵中的焦距)。


我不确定你是否正确理解了问题。在输出中,我必须有一个相同形状的三维数组,其中标记为1的点(体素)是我需要绘制的三角形。 - ZFTurbo
1
啊,好的,可以将其渲染为由三角形定义的体素...要做到这一点,您需要找到三角形和体素的交点。这里有一个非常好的答案:https://dev59.com/K3zaa4cB1Zd3GeqPNkA1#21639350。Plotly有一种方法来显示体素:https://plotly.com/python/3d-volume-plots/。 - daveg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接