如何使用 python/matplotlib 设置3d 情节的“摄像机位置”?

我正在学习如何使用 mplot3d 来生成漂亮的3d 数据图,到目前为止我非常高兴。我现在要做的是一个旋转曲面的小动画。为此,我需要为3D 投影设置一个摄像机位置。我想这是可能的,因为当交互式使用 matplotlib 时,可以使用鼠标旋转曲面。但是我怎么能按照剧本来做呢? 我在 mpl _ toolkit 中发现了许多转换。Proj3d,但是我不知道如何使用它们来实现我的目的,而且我也没有找到任何示例来说明我正在尝试做的事情。

181303 次浏览

通过“摄像机位置”,它听起来像你想要调整高度和方位角,你用来查看三维情节。你可以用 ax.view_init设置它。我使用下面的脚本首先创建了一个图,然后确定了一个很好的高度,即 elev,从这里可以查看我的图。然后我调整了方位角,或者说 azim,以改变我的绘图周围的整个360度,保存每个实例的数字(并记录下我保存绘图时的方位角)。对于一个更复杂的相机平底锅,您可以调整高度和角度,以达到理想的效果。

    from mpl_toolkits.mplot3d import Axes3D
ax = Axes3D(fig)
ax.scatter(xx,yy,zz, marker='o', s=20, c="goldenrod", alpha=0.6)
for ii in xrange(0,360,1):
ax.view_init(elev=10., azim=ii)
savefig("movie%d.png" % ii)

将照相机的位置应用到一个新的情节将会很方便。 所以我画了一个图,然后用鼠标移动图的距离。然后尝试在另一个地块上复制包括距离在内的视图。 我发现 axx.ax.get _ ax ()给我一个带有旧的. azim 和. elev 的对象。

在巨蟒里..。

axx=ax1.get_axes()
azm=axx.azim
ele=axx.elev
dst=axx.dist       # ALWAYS GIVES 10
#dst=ax1.axes.dist # ALWAYS GIVES 10
#dst=ax1.dist      # ALWAYS GIVES 10

后来的三维图形..。

ax2.view_init(elev=ele, azim=azm) #Works!
ax2.dist=dst                       # works but always 10 from axx

编辑1..。 好吧,摄像机的位置是错误的思考方式。折现值。它在所有事物之上,就像是整个图形的一种粗糙的标量乘法器。

这适用于视图的放大/缩放:

xlm=ax1.get_xlim3d() #These are two tupples
ylm=ax1.get_ylim3d() #we use them in the next
zlm=ax1.get_zlim3d() #graph to reproduce the magnification from mousing
axx=ax1.get_axes()
azm=axx.azim
ele=axx.elev

稍后,图表..。

ax2.view_init(elev=ele, azim=azm) #Reproduce view
ax2.set_xlim3d(xlm[0],xlm[1])     #Reproduce magnification
ax2.set_ylim3d(ylm[0],ylm[1])     #...
ax2.set_zlim3d(zlm[0],zlm[1])     #...

尝试下面的代码来找到最佳的相机位置

使用 if 子句中提到的键盘键移动绘图的视角

使用 打印获取摄像机的位置

def move_view(event):
ax.autoscale(enable=False, axis='both')
koef = 8
zkoef = (ax.get_zbound()[0] - ax.get_zbound()[1]) / koef
xkoef = (ax.get_xbound()[0] - ax.get_xbound()[1]) / koef
ykoef = (ax.get_ybound()[0] - ax.get_ybound()[1]) / koef
## Map an motion to keyboard shortcuts
if event.key == "ctrl+down":
ax.set_ybound(ax.get_ybound()[0] + xkoef, ax.get_ybound()[1] + xkoef)
if event.key == "ctrl+up":
ax.set_ybound(ax.get_ybound()[0] - xkoef, ax.get_ybound()[1] - xkoef)
if event.key == "ctrl+right":
ax.set_xbound(ax.get_xbound()[0] + ykoef, ax.get_xbound()[1] + ykoef)
if event.key == "ctrl+left":
ax.set_xbound(ax.get_xbound()[0] - ykoef, ax.get_xbound()[1] - ykoef)
if event.key == "down":
ax.set_zbound(ax.get_zbound()[0] - zkoef, ax.get_zbound()[1] - zkoef)
if event.key == "up":
ax.set_zbound(ax.get_zbound()[0] + zkoef, ax.get_zbound()[1] + zkoef)
# zoom option
if event.key == "alt+up":
ax.set_xbound(ax.get_xbound()[0]*0.90, ax.get_xbound()[1]*0.90)
ax.set_ybound(ax.get_ybound()[0]*0.90, ax.get_ybound()[1]*0.90)
ax.set_zbound(ax.get_zbound()[0]*0.90, ax.get_zbound()[1]*0.90)
if event.key == "alt+down":
ax.set_xbound(ax.get_xbound()[0]*1.10, ax.get_xbound()[1]*1.10)
ax.set_ybound(ax.get_ybound()[0]*1.10, ax.get_ybound()[1]*1.10)
ax.set_zbound(ax.get_zbound()[0]*1.10, ax.get_zbound()[1]*1.10)
    

# Rotational movement
elev=ax.elev
azim=ax.azim
if event.key == "shift+up":
elev+=10
if event.key == "shift+down":
elev-=10
if event.key == "shift+right":
azim+=10
if event.key == "shift+left":
azim-=10


ax.view_init(elev= elev, azim = azim)


# print which ever variable you want


ax.figure.canvas.draw()


fig.canvas.mpl_connect("key_press_event", move_view)


plt.show()


最小的例子变化 azimdistelev

将一些简单的示例图像添加到在 https://stackoverflow.com/a/12905458/895245上解释的内容中

下面是我的测试程序:

#!/usr/bin/env python3


import sys


import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np


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


if len(sys.argv) > 1:
azim = int(sys.argv[1])
else:
azim = None
if len(sys.argv) > 2:
dist = int(sys.argv[2])
else:
dist = None
if len(sys.argv) > 3:
elev = int(sys.argv[3])
else:
elev = None


# Make data.
X = np.arange(-5, 6, 1)
Y = np.arange(-5, 6, 1)
X, Y = np.meshgrid(X, Y)
Z = X**2


# Plot the surface.
surf = ax.plot_surface(X, Y, Z, linewidth=0, antialiased=False)


# Labels.
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')


if azim is not None:
ax.azim = azim
if dist is not None:
ax.dist = dist
if elev is not None:
ax.elev = elev


print('ax.azim = {}'.format(ax.azim))
print('ax.dist = {}'.format(ax.dist))
print('ax.elev = {}'.format(ax.elev))


plt.savefig(
'main_{}_{}_{}.png'.format(ax.azim, ax.dist, ax.elev),
format='png',
bbox_inches='tight'
)


在没有参数的情况下运行它会得到默认值:

ax.azim = -60
ax.dist = 10
ax.elev = 30

Main _-60 _ 10 _ 30. png

enter image description here

变化 azim

方位角是围绕 z 轴的旋转,例如:

  • 0表示“从 + x 看”
  • 90表示“从 + y 看”

Main _-60 _ 10 _ 30. png

enter image description here

Main _ 0 _ 10 _ 30. png

enter image description here

Main _ 60 _ 10 _ 30. png

enter image description here

变化 dist

在数据坐标系中,dist似乎是到中心可见点的距离。

Main _-60 _ 10 _ 30. png

enter image description here

Main _-60 _ 5 _ 30. png

enter image description here

Main _-60 _ 20 _-30. png

enter image description here

变化 elev

由此我们知道 elev是眼睛和 xy 平面之间的角度。

Main _-60 _ 10 _ 60. png

enter image description here

Main _-60 _ 10 _ 30. png

enter image description here

Main _-60 _ 10 _ 0. png

enter image description here

Main _-60 _ 10 _-30. png

enter image description here

在 matpotlib 上测试 = = 3.2.2。

问: 如何在 matplotlib 中设置 view?

对于一个三维情节,你如何固定的视图?

答: 通过设置属性 ax.azimax.level

ax.elev = 0
ax.azim = 270  # xz view


ax.elev = 0
ax.azim = 0    # yz view


ax.elev = 0
ax.azim = -90  # xy view