【问题标题】:how to set "camera position" for 3d plots using python/matplotlib?如何使用 python/matplotlib 为 3d 绘图设置“相机位置”?
【发布时间】:2012-10-05 23:24:19
【问题描述】:

我正在学习如何使用 mplot3d 生成漂亮的 3d 数据图,到目前为止我很高兴。我现在想做的是一个旋转表面的小动画。为此,我需要为 3D 投影设置相机位置。我想这一定是可能的,因为在交互式使用 matplotlib 时可以使用鼠标旋转表面。但是我怎样才能从脚本中做到这一点? 我在 mpl_toolkits.mplot3d.proj3d 中发现了很多转换,但我不知道如何将它们用于我的目的,也没有找到任何我想要做的例子。

【问题讨论】:

  • 想知道如何在 jupyter notebook 中交互式旋转的旁注:你可以使用%matplotlib notebook
  • 在按住鼠标右键的同时拖动也会改变相机距离。
  • 对于这种可视化,我会试试 mayavi。

标签: python matplotlib mplot3d


【解决方案1】:

通过“相机位置”,听起来您想要调整用于查看 3D 绘图的仰角和方位角。您可以使用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)

【讨论】:

  • 打败我!附带说明一下,这些可用作 ax.elevax.azim 属性。你也可以写ax.azim = ii 甚至ax.azim += 1 来达到同样的效果。
  • 对不起,我打败了你,但周围都是公平的。这也只是我的一段代码摘录,在 for 循环中还有更多内容,而不仅仅是 view_init 和 savefig。 =)
  • 感谢 cosmosis 和乔,这正是我想要的。因为我现在知道要查找什么,所以我还找到了 ax.dist - 连同 ax.azim 和 ax.elev - 允许在极坐标中设置相机位置。
  • 这“添加”了一个新图到轴上。因此,如果您将“transparent=True”传递给 savefig,您将看到所有先前的视图重叠。这从文件大小也很明显。我仍在寻找一种无需重置轴即可更改视图的方法...
  • 也可以通过ax.dist=15(默认为10)设置相机与物点的距离
【解决方案2】:

改变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

变化azim

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

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

main_-60_10_30.png

main_0_10_30.png

main_60_10_30.png

变化dist

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

main_-60_10_30.png

main_-60_5_30.png

main_-60_20_-30.png

变化elev

由此我们了解到elev 是眼睛和xy 平面之间的角度。

main_-60_10_60.png

main_-60_10_30.png

main_-60_10_0.png

main_-60_10_-30.png

在 matpotlib==3.2.2 上测试。

【讨论】:

    【解决方案3】:

    尝试以下代码找到最佳相机位置

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

    使用 print 获取相机位置

    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()
    
    

    【讨论】:

      【解决方案4】:

      将相机位置应用于新绘图会很方便。 所以我绘图,然后用鼠标改变距离来移动绘图。然后尝试复制视图,包括另一个图上的距离。 我发现 axx.ax.get_axes() 为我提供了一个带有旧 .azim 和 .elev 的对象。

      在 Python 中...

      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
      

      后来的 3d 图...

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

      编辑 1... 好的,相机位置是关于 .dist 值的错误思考方式。它作为整个图形的一种 hackey 标量乘数,位于一切之上。

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

      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])     #...
      

      【讨论】:

      • +1 用于调用 hacky 标量乘法。如果您希望获得观点,那会很烦人。
      猜你喜欢
      • 2011-12-29
      • 1970-01-01
      • 1970-01-01
      • 2017-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-23
      相关资源
      最近更新 更多