как установить положение камеры для 3D-графиков с помощью python / matplotlib?

Я изучаю, как использовать mplot3d для создания хороших графиков трехмерных данных, и пока я очень доволен. Сейчас я пытаюсь сделать небольшую анимацию вращающейся поверхности. Для этого мне нужно установить положение камеры для 3D-проекции. Я думаю, это должно быть возможно, поскольку поверхность можно вращать с помощью мыши при интерактивном использовании matplotlib. Но как я могу сделать это из сценария? Я нашел много преобразований в mpl_toolkits.mplot3d.proj3d, но я не мог понять, как их использовать для своих целей, и не нашел ни одного примера того, что я пытаюсь сделать.


person Andreas Bleuler    schedule 15.10.2012    source источник
comment
Боковое примечание для тех, кто задается вопросом, как интерактивно вращаться в блокноте jupyter: вы можете использовать %matplotlib notebook   -  person YvesgereY    schedule 27.07.2017
comment
Также перетаскивание, удерживая правую кнопку мыши, изменяет расстояние до камеры.   -  person LoMaPh    schedule 04.12.2018
comment
Для такого рода визуализации я бы попробовал Mayavi.   -  person user2821    schedule 16.06.2019


Ответы (4)


Под «положением камеры» похоже, что вы хотите отрегулировать высоту и азимутальный угол, которые вы используете для просмотра трехмерного графика. Вы можете установить это с помощью 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)
person cosmosis    schedule 15.10.2012
comment
Обыграй меня! Кстати, они доступны как свойства ax.elev и ax.azim. Вы также могли бы просто написать ax.azim = ii или даже ax.azim += 1, чтобы добиться того же эффекта. - person Joe Kington; 16.10.2012
comment
Извини, что обыграл тебя, но все вокруг справедливые. Это тоже всего лишь мой отрывок из кода, в этом цикле for было больше, чем просто view_init и savefig. знак равно - person cosmosis; 16.10.2012
comment
Спасибо Cosmosis и Джо, это именно то, что я искал. Поскольку теперь я знал, что искать, я также нашел ax.dist, который - вместе с ax.azim и ax.elev - позволяет установить положение камеры в полярных координатах. - person Andreas Bleuler; 16.10.2012
comment
Это добавляет новый график к осям. Поэтому, если вы передадите transparent = True в savefig, вы увидите, что все предыдущие представления перекрываются. Это также видно по размерам файлов. Я все еще ищу способ изменить вид без сброса осей ... - person navidoo; 30.07.2013
comment
Вы также можете установить расстояние между камерой и точкой объекта с помощью ax.dist = 15 (по умолчанию 10). - person Tim; 03.07.2014
comment
Я искал способ получить текущий азим, высот (чего-то вроде ax.get_azim там нет), но ax.azim просто работал! - person otterb; 03.09.2014
comment
могу я таким образом сохранить вращающийся файл .gif ... @cosmosis - person diffracteD; 13.03.2015
comment
@diffracteD Да. Индекс ii поворачивает изображение на 360 градусов, создавая изображения для анимированного gif. Однако при этом изображения не будут скомпилированы для создания вращающегося gif. - person cosmosis; 14.03.2015
comment
Здравствуйте, кажется, view_init вращается из исходного положения. Чтобы получить некоторые сложные ориентации (например, изменить рулон), мне нужно было бы несколько раз менять вид камеры. Есть какие-нибудь сведения о том, как к этим ребятам? - person Rakshit Kothari; 20.05.2021
comment
Текущий код начинается с исходного положения на высоте 10 градусов (то есть вид камеры) и азимуте 0 градусов, а затем вращается вокруг азимута 360 градусов. Итак, если вы хотите сделать что-то более сложное, вам нужно будет настроить два соответствующих массива, чтобы показать, как высота и азимуталь изменяются вместе. - person cosmosis; 21.05.2021

Что было бы удобно, так это применить положение камеры к новому сюжету. Итак, я рисую, а затем перемещаю график с помощью мыши, изменяя расстояние. Затем попробуйте воспроизвести вид, включая расстояние, на другом участке. Я обнаружил, что axx.ax.get_axes () получает объект со старыми .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

Позже 3д график ...

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

РЕДАКТИРОВАТЬ 1 ... Хорошо, положение камеры - неправильный взгляд на значение .dist. Он действует поверх всего как своего рода скалярный множитель для всего графа.

Это работает для увеличения / увеличения вида:

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])     #...
person snaxxus    schedule 15.09.2015
comment
+1 за вызов хитрого скалярного умножения. Очень обидно, если вы надеетесь на перспективу. - person user5920660; 08.09.2017

Минимальный пример с различными azim, dist и elev

Чтобы добавить несколько простых образцов изображений к тому, что было объяснено на странице: 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_-60elev30.png

введите описание изображения здесь

Различное azim

Азимут - это вращение вокруг оси z, например:

  • 0 означает смотреть с + x
  • 90 означает смотреть с + y

main_-60elev30.png

введите описание изображения здесь

main_0elev30.png

введите описание изображения здесь

main_60elev30.png

введите описание изображения здесь

Различное dist

dist кажется расстоянием от центральной видимой точки в координатах данных.

main_-60elev30.png

введите описание изображения здесь

main_-60

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

введите описание изображения здесь

main_-60_20_-30.png

введите описание изображения здесь

Различное elev

Из этого мы понимаем, что elev - это угол между глазом и плоскостью xy.

main_-60elev60.png

введите описание изображения здесь

main_-60elev30.png

введите описание изображения здесь

main_-60elev0.png

введите описание изображения здесь

main_-60elev-30.png

введите описание изображения здесь

Проверено на matpotlib == 3.2.2.

person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 15.11.2020

Попробуйте следующий код, чтобы найти оптимальное положение камеры

Перемещайте угол обзора графика с помощью клавиш клавиатуры, как указано в предложении 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()

person Bharath C    schedule 19.08.2020
comment
Это работает очень хорошо, спасибо - person Azrael_DD; 08.06.2021