197 votes

comment définir la "position de la caméra" pour les tracés 3d en utilisant python/matplotlib ?

J'apprends à utiliser mplot3d pour produire de beaux graphiques de données 3D et je suis assez satisfait jusqu'à présent. Ce que j'essaie de faire en ce moment est une petite animation d'une surface en rotation. Pour cela, j'ai besoin de définir une position de caméra pour la projection 3D. Je suppose que cela doit être possible puisqu'une surface peut être tournée à l'aide de la souris lorsque l'on utilise matplotlib de manière interactive. Mais comment puis-je faire cela à partir d'un script ? J'ai trouvé beaucoup de transformations dans mpl_toolkits.mplot3d.proj3d mais je n'ai pas pu trouver comment les utiliser pour mon but et je n'ai pas trouvé d'exemple pour ce que j'essaie de faire.

230voto

cosmosis Points 1281

Par "position de la caméra", il semble que vous souhaitiez régler l'angle d'élévation et l'angle d'azimut que vous utilisez pour visualiser le tracé 3D. Vous pouvez les définir avec ax.view_init . J'ai utilisé le script ci-dessous pour créer d'abord le tracé, puis j'ai déterminé une bonne élévation, ou elev d'où je peux voir ma parcelle. J'ai ensuite ajusté l'angle d'azimut, ou azim Je me suis servi de mon ordinateur pour faire varier les 360 degrés autour de ma parcelle, en sauvegardant la figure à chaque fois (et en notant l'angle d'azimut lorsque j'ai sauvegardé la parcelle). Pour un panoramique de caméra plus compliqué, vous pouvez ajuster à la fois l'élévation et l'angle pour obtenir l'effet désiré.

    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)

22voto

snaxxus Points 169

Ce qui serait pratique, c'est d'appliquer la position de la caméra à un nouveau tracé. Ainsi, je trace, puis je déplace le tracé avec la souris en modifiant la distance. J'essaie ensuite de reproduire la vue, y compris la distance, sur un autre graphique. Je trouve que axx.ax.get_axes() me donne un objet avec les anciens .azim et .elev.

EN 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

Plus tard, le graphique 3D...

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

EDIT 1... OK, la position de la caméra est la mauvaise façon de penser concernant la valeur .dist. Elle se superpose à tout comme une sorte de multiplicateur scalaire pour l'ensemble du graphique.

Cela fonctionne pour le grossissement/zoom de la vue :

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

Plus tard Graph...

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

15voto

Ciro Santilli Points 3341

Exemple minimal variant azim , dist y elev

Pour ajouter quelques exemples d'images simples à ce qui a été expliqué : https://stackoverflow.com/a/12905458/895245

Voici mon programme de test :

#!/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'
)

L'exécuter sans arguments donne les valeurs par défaut :

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

principal_-60_10_30.png

enter image description here

Varier azim

L'azimut est la rotation autour de l'axe z, par exemple :

  • 0 signifie "chercher à partir de +x"
  • 90 signifie "regarder depuis +y"

principal_-60_10_30.png

enter image description here

main_0_10_30.png

enter image description here

principal_60_10_30.png

enter image description here

Varier dist

dist semble être la distance du point central visible en coordonnées de données.

principal_-60_10_30.png

enter image description here

principal_-60_5_30.png

enter image description here

principal_-60_20_-30.png

enter image description here

Varier elev

A partir de là, nous comprenons que elev est l'angle entre l'œil et le plan xy.

main_-60_10_60.png

enter image description here

principal_-60_10_30.png

enter image description here

principal_-60_10_0.png

enter image description here

principal_-60_10_-30.png

enter image description here

Testé sur matpotlib==3.2.2.

3voto

Bharath C Points 27

Essayez le code suivant pour trouver la position optimale de la caméra

Déplacez l'angle de vue du tracé en utilisant les touches du clavier comme indiqué dans la clause if.

Utilisez imprimer pour obtenir les positions des caméras

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X