带有 2D 投影的 3D 线框图:空间组织和投影频率

Posted

技术标签:

【中文标题】带有 2D 投影的 3D 线框图:空间组织和投影频率【英文标题】:3D wireframe plot with 2D projections: Spatial organiszation & frequency of projection 【发布时间】:2021-01-15 00:39:56 【问题描述】:

我正在处理由线框显示的 3D 图,其中 2D 图分别投影在 x、y 和 z 表面上。您可以在下面找到一个最小示例。

我有两个问题:

    使用 contourf,每个 x=10, x=20,... 或 y=10, y=20,... 的二维图都显示在图墙上。是否有可能分别定义显示等高线图的 x 或 y?例如,如果我只想将 y = 0.5 的 xz 等高线图镜像到墙上?

补充:为了显示“2D 图”的含义,我将代码中的“contourf”更改为“contour”,并将生成的图添加到这个问题中。在这里,您现在可以看到不同 y 值的 xz 线,都偏移到 y=90。如果我不想拥有所有行,而只想要其中两个用于定义的 y 值怎么办?

3D_plot_with_2D_contours

    正如您在最小示例中所见,2D 等高线图在光学上覆盖了线框 3D 图。随着 alpha=0.5 增加透明度,我可以增加 2D 轮廓的透明度以至少看到线框,但它仍然是光学错误的。是否可以正确排序对象?
import matplotlib.pyplot as plt,numpy as np
import pylab as pl

from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt,numpy as np

plt.clf()

fig = plt.figure(1,figsize=(35,17),dpi=600,facecolor='w',edgecolor='k')
fig.set_size_inches(10.5,8)
ax  = fig.gca(projection='3d')
X, Y, Z = axes3d.get_test_data(0.05)

Xnew = X + 50
Ynew = Y + 50

cset = ax.contourf(Xnew, Ynew, Z, zdir='z', offset=-100, cmap=plt.cm.coolwarm, alpha=0.5)
cset = ax.contourf(Xnew, Ynew, Z, zdir='x', offset=10, cmap=plt.cm.coolwarm, alpha=0.5) 
cset = ax.contourf(Xnew, Ynew, Z, zdir='y', offset=90, cmap=plt.cm.coolwarm, alpha = 0.5) 

ax.plot_wireframe(Xnew, Ynew, Z, rstride=5, cstride=5, color='black')

Z=Z-Z.min()
Z=Z/Z.max()

from scipy.ndimage.interpolation import zoom

Xall=zoom(Xnew,5)
Yall=zoom(Ynew,5)
Z=zoom(Z,5)

ax.set_xlim(10, 90)
ax.set_ylim(10, 90)
ax.set_zlim(-100, 100)

ax.tick_params(axis='z', which='major', pad=10)

ax.set_xlabel('X',labelpad=10)
ax.set_ylabel('Y',labelpad=10)
ax.set_zlabel('Z',labelpad=17)


ax.view_init(elev=35., azim=-70)

fig.tight_layout()

plt.show()

附加 2:这是我正在使用的实际代码。但是,原始数据隐藏在 csv 文件中,这些文件太大而无法包含在最小示例中。这就是为什么最初用测试数据替换它们的原因。不过,也许实际的代码还是有帮助的。

from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt,numpy as np
import pylab as pl
from matplotlib.markers import MarkerStyle

import csv
with open("X.csv", 'r') as f:
  X = list(csv.reader(f, delimiter=";"))
import numpy as np
X = np.array(X[1:], dtype=np.float)

import csv
with open("Z.csv", 'r') as f:
  Z = list(csv.reader(f, delimiter=";"))
import numpy as np
Z = np.array(Z[1:], dtype=np.float)

Y = [[7,7.1,7.2,7.3,7.4,7.5,7.6,7.7,7.8,7.9,8,8.1,8.2,8.3,8.4,8.5,8.6,8.7,8.8,8.9,9]]

Xall = np.repeat(X[:],21,axis=1)
Yall = np.repeat(Y[:],30,axis=0)

from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt,numpy as np


plt.clf()

fig = plt.figure(1,figsize=(35,17),dpi=600,facecolor='w',edgecolor='k')
fig.set_size_inches(10.5,8) 
ax  = fig.gca(projection='3d')

cset = ax.contourf(Xall, Yall, Z, 2, zdir='x', offset=0,  cmap=plt.cm.coolwarm, shade = False, edgecolor='none', alpha=0.5)
cset = ax.contourf(Xall, Yall, Z, 2, zdir='y', offset=9, cmap=plt.cm.coolwarm, shade = False, edgecolor='none', alpha=0.5)

ax.plot_wireframe(Xall, Yall, Z, rstride=1, cstride=1, color='black')

Z=Z-Z.min()
Z=Z/Z.max()

from scipy.ndimage.interpolation import zoom

Xall=zoom(Xall,5)
Yall=zoom(Yall,5)
Z=zoom(Z,5)

cset = ax.plot_surface(Xall, Yall, np.zeros_like(Z)-0,facecolors=plt.cm.coolwarm(Z),shade=False,alpha=0.5,linewidth=False)

ax.set_xlim(-0.5, 31)
ax.set_ylim(6.9, 9.1)
ax.set_zlim(0, 500)

labelsx = [item.get_text() for item in ax.get_xticklabels()]
empty_string_labelsx = ['']*len(labelsx)
ax.set_xticklabels(empty_string_labelsx)

labelsy = [item.get_text() for item in ax.get_yticklabels()]
empty_string_labelsy = ['']*len(labelsy)
ax.set_yticklabels(empty_string_labelsy)

labelsz = [item.get_text() for item in ax.get_zticklabels()]
empty_string_labelsz = ['']*len(labelsz)
ax.set_zticklabels(empty_string_labelsz)

import matplotlib.ticker as ticker
ax.xaxis.set_major_locator(ticker.MultipleLocator(5))
ax.xaxis.set_minor_locator(ticker.MultipleLocator(1))
ax.yaxis.set_major_locator(ticker.MultipleLocator(0.5))
ax.yaxis.set_minor_locator(ticker.MultipleLocator(0.25))
ax.zaxis.set_major_locator(ticker.MultipleLocator(100))
ax.zaxis.set_minor_locator(ticker.MultipleLocator(50))

ax.tick_params(axis='z', which='major', pad=10)

ax.set_xlabel('X',labelpad=5,fontsize=15)
ax.set_ylabel('Y',labelpad=5,fontsize=15)
ax.set_zlabel('Z',labelpad=5,fontsize=15)


ax.view_init(elev=35., azim=-70)

fig.tight_layout()

plt.show()

【问题讨论】:

【参考方案1】:

其他可能的答案。

这段代码演示

    曲面图及其对应的线框 在指定的 x 和 y 值处创建数据及其 3d 线图(叠加在 1 中的表面上) 3d 线(在 2 中)在框架墙上的投影
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from scipy import interpolate
import numpy as np

# use the test data for plotting
fig = plt.figure(1, figsize=(6,6), facecolor='w', edgecolor='gray')
ax  = fig.gca(projection='3d')
X, Y, Z = axes3d.get_test_data(0.1)  #get 3d data at appropriate density

# create an interpolating function
# can take a long time if data is too large
f1 = interpolate.interp2d(X, Y, Z, kind='linear')

# in general, one can use a set of other X,Y,Z that cover a surface
# preferably, (X,Y) are in grid arrangement

# make up a new set of 3d data to plot
# ranges of x1, and y1 will be inside (X,Y) of the data obtained above
# related grid, x1g,y1g,z1g will be obtained from meshgrid and the interpolated function
x1 = np.linspace(-15,15,10)
y1 = np.linspace(-15,15,10)
x1g, y1g = np.meshgrid(x1, y1)
z1g = f1(x1, y1)  #dont use (x1g, y1g)

# prep data for 3d line on the surface (X,Y,Z) at x=7.5
n = 12
x_pf = 7.5
x5 = x_pf*np.ones(n)
y5 = np.linspace(-15, 15, n)
z5 = f1(x_pf, y5)
# x5,y5,z5 can be used to plot 3d line on the surface (X,Y,Z)

# prep data for 3d line on the surface (X,Y,Z) at y=6
y_pf = 6
x6 = np.linspace(-15, 15, n)
y6 = x_pf*np.ones(n)
z6 = f1(x6, y_pf)
# x6,y6,z6 can be used to plot 3d line on the surface (X,Y,Z)

ax  = fig.gca(projection='3d')

ax.plot_surface(x1g, y1g, z1g, alpha=0.25)
ax.plot_wireframe(x1g, y1g, z1g, rstride=2, cstride=2, color='black', zorder=10, alpha=1, lw=0.8)

# 3D lines that follow the surface
ax.plot(x5,y5,z5.flatten(), color='red', lw=4)
ax.plot(x6,y6,z6.flatten(), color='green', lw=4)

# projections of 3d curves
# project red and green lines to the walls
ax.plot(-15*np.ones(len(y5)), y5, z5.flatten(), color='red', lw=4, linestyle=':', alpha=0.6)
ax.plot(x6, 15*np.ones(len(x6)), z6.flatten(), color='green', lw=4, linestyle=':', alpha=0.6)

# projections on other sides (become vertical lines)
# change to if True, to plot these
if False:
    ax.plot(x5, 15*np.ones(len(x5)), z5.flatten(), color='red', lw=4, alpha=0.3)
    ax.plot(-15*np.ones(len(x6)), y6, z6.flatten(), color='green', lw=4, alpha=0.3)

ax.set_title("Projections of 3D lines")

# set limits
ax.set_xlim(-15, 15.5)
ax.set_ylim(-15.5, 15)

plt.show();

【讨论】:

非常感谢,这正是我想要的!我刚刚将它应用到我的数据中,并且效果很好!只是一个小编辑:在准备 y=6 的数据时,它必须是 y6 = y_pf*np.ones(n) 而不是 ...x_pf... 。顺便说一句,这也解决了我的问题 2,即关于图中对象的光学排列 - 通过不使用轮廓线,线框始终位于前面并且没有被任何表面覆盖。非常聪明的解决方法!【参考方案2】:

(问题1的答案)要绘制曲面和指定平面(y=-20,y=20)之间的交点,需要找到Y[?]=-20和20。通过检查,我发现Y[100]=20,Y[20]=-20。

绘制相交线的相关代码:

# By inspection, Y[100]=20, Y[20]=-20
ax.plot3D(X[100], Y[100], Z[100], color='red', lw=6)  # line-1 at y=20
ax.plot3D(X[20], Y[20], Z[20], color='green', lw=6)   # line-2 at y=-20

# Project them on Z=-100 plane
ax.plot3D(X[100], Y[100], -100, color='red', lw=3)  # projection of Line-1
ax.plot3D(X[20], Y[20], -100, color='green', lw=3)  # projection of Line-2

输出图:

(对问题 2 的回答)从曲面图中突出线框以获得更好的图。曲面图必须是部分透明的,这通过设置选项alpha=0.6 来实现。相关代码如下。

Z1 = Z-Z.min()
Z1 = Z1/Z.max()
Xall = zoom(X,3)
Yall = zoom(Y,3)
Zz = zoom(Z1, 3)

surf = ax.plot_surface(Xall, Yall, Zz, rstride=10, cstride=10, 
                       facecolors = cm.jet(Zz/np.amax(Zz)),
                       linewidth=0, antialiased=True,
                       alpha= 0.6)

# Wireframe
ax.plot_wireframe(X, Y, Z, rstride=5, cstride=5, color='black', alpha=1, lw=0.8)

剧情是:

【讨论】:

感谢 swatchi 的评论。不幸的是,它现在正是我问题的答案。我稍微编辑了我的问题以更好地说明它。 @user14360796 (根据新编辑的问题)你能给我看一张'y = 0.5 镜像在墙上的 xz 等高线图'的草图吗? 原则上您已经在响应中添加的图中可以看到。 contourf 项导致 2D 绘图显示不同 y 值的 xz 线,全部偏移到 y=40 处的一个表面,即“绘图墙”。对于 x=-40 处的 yz 图也是如此。所以理论上我很好,但我希望轮廓中的步骤更少。我在上面的问题中添加了一个情节,可以更好地解释这个问题。 @user14360796 您能否提供生成您正在处理的真实 3D 线框的代码。我喜欢研究它而不是测试数据。 @swatschai 我现在将原始代码添加到问题中;但是,我正在读取 csv 文件中的数据,我很可能无法在此处上传(或者我可以上传吗?)并且太大而无法包含在代码本身中。这有帮助吗?

以上是关于带有 2D 投影的 3D 线框图:空间组织和投影频率的主要内容,如果未能解决你的问题,请参考以下文章

4D转3D透视投影

使用 R 进行 2D 投影的 3D 表面

在 C++ 中的 OpenGL 中将坐标从 3D 透视投影映射到 2D 正交投影

投影3D网格的2D轮廓算法

将 ARKit 面部跟踪 3D 网格投影到 2D 图像坐标

转载3D/2D中的D3DXMatrixPerspectiveFovLH和D3DXMatrixOrthoLH投影函数详解