在matplotlib中制作旋转的3D图表动画

32

我已经设置并绘制了一个散点图,想要创建一个 .mp4 格式的视频,让图形在空间中旋转,就像我使用了 plt.show() 并拖动了观察点一样。

这个答案 几乎是我想要的,但是要保存电影,我需要手动调用 FFMpeg 并提供包含图像的文件夹。我更喜欢使用 Matplotlib 的内置动画支持,下面是代码:

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"%ii+".png")

非常相关:[如何使用Python制作旋转框的动画效果。](https://mas.to/@artilectzed/103986205750272190) - rugk
4个回答

40
如果你想了解更多关于matplotlib动画的内容,你应该真正地跟随这个教程。它详细地解释了如何创建动态图。 注意:创建动态图需要安装ffmpegmencoder
这是他的第一个示例版本,已经更改为适用于您的散点图。
# First import everthing you need
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
from mpl_toolkits.mplot3d import Axes3D

# Create some random data, I took this piece from here:
# http://matplotlib.org/mpl_examples/mplot3d/scatter3d_demo.py
def randrange(n, vmin, vmax):
    return (vmax - vmin) * np.random.rand(n) + vmin
n = 100
xx = randrange(n, 23, 32)
yy = randrange(n, 0, 100)
zz = randrange(n, -50, -25)

# Create a figure and a 3D Axes
fig = plt.figure()
ax = Axes3D(fig)

# Create an init function and the animate functions.
# Both are explained in the tutorial. Since we are changing
# the the elevation and azimuth and no objects are really
# changed on the plot we don't have to return anything from
# the init and animate function. (return value is explained
# in the tutorial.
def init():
    ax.scatter(xx, yy, zz, marker='o', s=20, c="goldenrod", alpha=0.6)
    return fig,

def animate(i):
    ax.view_init(elev=10., azim=i)
    return fig,

# Animate
anim = animation.FuncAnimation(fig, animate, init_func=init,
                               frames=360, interval=20, blit=True)
# Save
anim.save('basic_animation.mp4', fps=30, extra_args=['-vcodec', 'libx264'])

1
抱歉,希望你并没有真的生气。我不确定我是否同意你关于缺少返回值的观点。我认为blit需要返回值才能正常工作,但是这个库的那一部分对我来说仍然有些难以理解。(我已经给答案点了赞)。 - tacaswell
1
当然你没有! :D 我非常尊重你的评论,因为它们来自一位经验丰富的 matplotlib 开发者!我会尝试从中学习,并根据你的建议行事(比如昨天关于问题标记的建议)。所以请在任何时候都纠正我,如果你认为我做错了什么 :). 这符合 SO 社区和我的学习过程的利益。只是上一个问题我们有轻微的方法论分歧,所以我加了这个玩笑,因为我知道你会阅读所有的 matplotlib 问题 :) - Viktor Kerkez
关于返回值。在博客文章中Jake说:“这个函数必须返回线条对象,因为这样才能告诉动画制作者在每一帧之后需要更新哪些图形对象”。但是由于图形上没有任何对象发生改变,我认为不应该返回任何东西。如果这是错误的假设,我将编辑这篇文章。 - Viktor Kerkez
4
为了避免崩溃,initanimate 函数中都需要添加 return() - Eric
2
当我将这段代码复制粘贴到我的IDLE中时,会出现以下错误,有人有什么想法吗? File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/matplotlib/animation.py", line 1260, in <setcomp> axes = {a.axes for a in artists} TypeError: unhashable type: 'list' - user3015729
显示剩余4条评论

10

这个答案实际上很棒。顺便提一下,链接中的代码需要先安装pip install pillow。来源:https://github.com/ipython/ipython/issues/8052 - F.S.

3
Viktor Kerkez提供的解决方案对我无效(matplotlib版本为3.4.2)。我得到了user3015729报告的相同错误。以下是经过调整的版本,它能够产生相同的结果,并且可以在matplotlib 3.4.2中正常工作。
# First import everthing you need
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
from mpl_toolkits.mplot3d import Axes3D

# Create some random data, I took this piece from here:
# http://matplotlib.org/mpl_examples/mplot3d/scatter3d_demo.py


def randrange(n, vmin, vmax):
    return (vmax - vmin) * np.random.rand(n) + vmin


n = 100
xx = randrange(n, 23, 32)
yy = randrange(n, 0, 100)
zz = randrange(n, -50, -25)

# Create a figure and a 3D Axes
fig = plt.figure()
ax = Axes3D(fig)
scat = ax.scatter(xx, yy, zz, marker='o', s=20, c="goldenrod", alpha=0.6)

# Create an init function and the animate functions.
# Both are explained in the tutorial. Since we are changing
# the the elevation and azimuth and no objects are really
# changed on the plot we don't have to return anything from
# the init and animate function. (return value is explained
# in the tutorial.


def init():
    ax.view_init(elev=10., azim=0)
    return [scat]


def animate(i):
    ax.view_init(elev=10., azim=i)
    return [scat]


# Animate
anim = animation.FuncAnimation(fig, animate, init_func=init,
                               frames=360, interval=20, blit=True)
# Save
anim.save('basic_animation.mp4', fps=30, extra_args=['-vcodec', 'libx264'])

2

这种方法有点不正规,但如果您正在使用Jupyter笔记本电脑,您可以使用单元格魔法直接从笔记本电脑运行ffmpeg的命令行版本。在一个单元格中运行您的脚本以生成原始帧。

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)

现在,在一个新的笔记本单元格中,输入以下内容,然后运行该单元格。
%%bash 
ffmpeg -r 30 -i movie%d.png -c:v libx264 -vf fps=25 -pix_fmt yuv420p out.mp4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接