Matplotlib animation.FuncAnimation() 动画错过第一帧?
Posted
技术标签:
【中文标题】Matplotlib animation.FuncAnimation() 动画错过第一帧?【英文标题】:Matplotlib animation.FuncAnimation() animation miss first frame? 【发布时间】:2022-01-12 02:06:03 【问题描述】:我是 Python 和 Matplotlib 的新手。我有这段代码可以在 3d Matplotlib 子图中绘制轨迹:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from itertools import islice
import sympy as sym
from sympy.parsing.sympy_parser import parse_expr
from sympy import Eq, solve
class FunZ:
def __init__(self, f):
self.x, self.y = sym.symbols('x y')
self.f = parse_expr(f)
# print('f : ', self.f)
def evalu(self, xx, yy):
return float(self.f.subs(self.x: xx, self.y: yy).evalf())
def derX(self, xx, yy):
self.dx = sym.diff(self.f, self.x)
# print('dx : ', self.dx)
return float(self.dx.subs(self.x: xx, self.y: yy).evalf())
def derY(self, xx, yy):
self.dy = sym.diff(self.f, self.y)
# print('dy :', self.dy)
return float(self.dy.subs(self.x: xx, self.y: yy).evalf())
def derXY(self, xx, yy):
return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
def minim(self):
self.dx = sym.diff(self.f, self.x)
self.dy = sym.diff(self.f, self.y)
print('dx : ', self.dx)
print('dy : ', self.dy)
eq1 = Eq(self.dx ,0)
eq2 = Eq(self.dy ,0)
solu = solve((eq1,eq2), (self.x, self.y), dict= False)
print(solu, type(solu))
return solu
XX = np.linspace(-4, 4, 100)
YY = np.linspace(-4, 4, 100)
funz = FunZ('x**2 + y**2 + 2*y + 2')
ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]
arr = np.array(ij, dtype=float)
# print(arr, arr.size, arr.shape, arr.dtype)
der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY]
derX = np.array(der_x)
# print(derX, derX.size, derX.shape, derX.dtype)
der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY]
derY = np.array(der_y)
# print(derY, derY.size, derY.shape, derY.dtype)
x = arr[:, 0]
y = arr[:, 1]
data = arr[:, 2]
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)
### devo duplicare primo elemento della lista altrimenti skippa a visualizzare i primi due sul video
# trajectory = [(2.5, 3.5, 27.5),(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]
trajectory = [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]
print('len trajectory : ', len(trajectory))
i = islice(trajectory, 0 , len(trajectory), 1)
pippo = None
def animate(j): #funziona senza stop iteration with animation.save
global pippo
try:
coord = next(i)
except:
print('coord empty')
return
if pippo:
pippo.remove()
print(coord)
x = coord[0]
y = coord[1]
z = coord[2]
pippo = ax.scatter(x,y,z)
return pippo
ani = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory), repeat=False) # e' necessario
plt.show()
plt.close('all')
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)
print('#####################')
i = islice(trajectory, 0 , len(trajectory), 1)
ani2 = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory), repeat=False)
# writervideo = animation.FFMpegWriter(fps=1, codec="h264")
# ani2.save('video_steeper.mp4', writer=writervideo)
ani2.save("movie.gif", writer=animation.PillowWriter(fps=1))
我很难理解为什么要有一个正确的动画和保存的动画我需要复制我的trajectory
列表的第一个元素 ((2.5, 3.5, 27.5))。
如果我不这样做,动画和我的 gif 只显示 9 个点而不是 10 个点,请查看结果输出:
10 元素列表中的电影trajectory
:
来自 11 元素列表 trajectory
的电影(第一个重复):
我已经尝试了很多来弄清楚发生了什么但无法理解,奇怪的是我在 animate
函数中的 print
语句为两个不同的列表打印了 11 和 10 分,但两者都是他们错过了动画和文件中的一帧。有什么线索吗?
【问题讨论】:
【参考方案1】:在我看来,您的问题来自您正在创建的可迭代 i
和函数 next
的使用。我不确定为什么会发生这种情况,但如果不是创建可迭代的i
,而是直接索引你的轨迹,那么你的动画会按预期工作。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from itertools import islice
import sympy as sym
from sympy.parsing.sympy_parser import parse_expr
from sympy import Eq, solve
class FunZ:
def __init__(self, f):
self.x, self.y = sym.symbols('x y')
self.f = parse_expr(f)
def evalu(self, xx, yy):
return float(self.f.subs(self.x: xx, self.y: yy).evalf())
def derX(self, xx, yy):
self.dx = sym.diff(self.f, self.x)
return float(self.dx.subs(self.x: xx, self.y: yy).evalf())
def derY(self, xx, yy):
self.dy = sym.diff(self.f, self.y)
return float(self.dy.subs(self.x: xx, self.y: yy).evalf())
def derXY(self, xx, yy):
return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
def minim(self):
self.dx = sym.diff(self.f, self.x)
self.dy = sym.diff(self.f, self.y)
print('dx : ', self.dx)
print('dy : ', self.dy)
eq1 = Eq(self.dx ,0)
eq2 = Eq(self.dy ,0)
solu = solve((eq1,eq2), (self.x, self.y), dict= False)
print(solu, type(solu))
return solu
XX = np.linspace(-4, 4, 100)
YY = np.linspace(-4, 4, 100)
funz = FunZ('x**2 + y**2 + 2*y + 2')
ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]
arr = np.array(ij, dtype=float)
der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY]
derX = np.array(der_x)
der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY]
derY = np.array(der_y)
x = arr[:, 0]
y = arr[:, 1]
data = arr[:, 2]
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)
trajectory = [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]
print('len trajectory : ', len(trajectory))
pippo = None
def animate(j): #funziona senza stop iteration with animation.save
global pippo
x = trajectory[j][0]
y = trajectory[j][1]
z = trajectory[j][2]
if pippo is not None:
pippo.remove()
pippo = ax.scatter(x,y,z)
return pippo
ani = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory), repeat=False) # e' necessario
plt.show()
输出给出:
【讨论】:
可能是 print(coord) 也来自迭代器,它实际上打印了所有 10 或 11 个点 我怀疑在全局范围内声明的迭代器i
会在动画中搞砸。
可能与:ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128), init_func=init, blit=True) --> init_funct有关=init 参见示例:matplotlib.org/stable/api/animation_api.html
不是真的,但明白了,确实感谢您的时间,我应该开始尝试阅读文档(行话对我来说仍然是很大的陡峭墙,而不是寻找教程
好收获!感谢分享!【参考方案2】:
用FuncAnimation
加上init_func
稍微摆弄了一下:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from itertools import islice
import sympy as sym
from sympy.parsing.sympy_parser import parse_expr
from sympy import Eq, solve
class FunZ:
def __init__(self, f):
self.x, self.y = sym.symbols('x y')
self.f = parse_expr(f)
# print('f : ', self.f)
def evalu(self, xx, yy):
return float(self.f.subs(self.x: xx, self.y: yy).evalf())
def derX(self, xx, yy):
self.dx = sym.diff(self.f, self.x)
# print('dx : ', self.dx)
return float(self.dx.subs(self.x: xx, self.y: yy).evalf())
def derY(self, xx, yy):
self.dy = sym.diff(self.f, self.y)
# print('dy :', self.dy)
return float(self.dy.subs(self.x: xx, self.y: yy).evalf())
def derXY(self, xx, yy):
return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
def minim(self):
self.dx = sym.diff(self.f, self.x)
self.dy = sym.diff(self.f, self.y)
print('dx : ', self.dx)
print('dy : ', self.dy)
eq1 = Eq(self.dx ,0)
eq2 = Eq(self.dy ,0)
solu = solve((eq1,eq2), (self.x, self.y), dict= False)
print(solu, type(solu))
return solu
XX = np.linspace(-4, 4, 100)
YY = np.linspace(-4, 4, 100)
funz = FunZ('x**2 + y**2 + 2*y + 2')
ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]
arr = np.array(ij, dtype=float)
# print(arr, arr.size, arr.shape, arr.dtype)
der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY]
derX = np.array(der_x)
# print(derX, derX.size, derX.shape, derX.dtype)
der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY]
derY = np.array(der_y)
# print(derY, derY.size, derY.shape, derY.dtype)
x = arr[:, 0]
y = arr[:, 1]
data = arr[:, 2]
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)
trajectory = [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]
print('len trajectory : ', len(trajectory))
i = islice(trajectory, 0 , len(trajectory), 1)
def init_animate():
ax.plot_trisurf(x, y, data, color="green", alpha=0.5)
pippo = None
def animate(j): #funziona senza stop iteration with animation.save
global pippo
try:
coord = next(i)
except:
print('coord empty')
return
if pippo:
pippo.remove()
print(coord)
x = coord[0]
y = coord[1]
z = coord[2]
pippo = ax.scatter(x,y,z)
return pippo
ani = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory),init_func=init_animate, repeat=False)
plt.show()
plt.close('all')
print('#####################')
能够在 gif 上移动所有 10 个点:
也能够删除 frames
参数,但只是为了可视化而不是保存我的动画,请参阅:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from itertools import islice
import sympy as sym
from sympy.parsing.sympy_parser import parse_expr
from sympy import Eq, solve
class FunZ:
def __init__(self, f):
self.x, self.y = sym.symbols('x y')
self.f = parse_expr(f)
# print('f : ', self.f)
def evalu(self, xx, yy):
return float(self.f.subs(self.x: xx, self.y: yy).evalf())
def derX(self, xx, yy):
self.dx = sym.diff(self.f, self.x)
# print('dx : ', self.dx)
return float(self.dx.subs(self.x: xx, self.y: yy).evalf())
def derY(self, xx, yy):
self.dy = sym.diff(self.f, self.y)
# print('dy :', self.dy)
return float(self.dy.subs(self.x: xx, self.y: yy).evalf())
def derXY(self, xx, yy):
return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
def minim(self):
self.dx = sym.diff(self.f, self.x)
self.dy = sym.diff(self.f, self.y)
print('dx : ', self.dx)
print('dy : ', self.dy)
eq1 = Eq(self.dx ,0)
eq2 = Eq(self.dy ,0)
solu = solve((eq1,eq2), (self.x, self.y), dict= False)
print(solu, type(solu))
return solu
XX = np.linspace(-4, 4, 100)
YY = np.linspace(-4, 4, 100)
funz = FunZ('x**2 + y**2 + 2*y + 2')
ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]
arr = np.array(ij, dtype=float)
# print(arr, arr.size, arr.shape, arr.dtype)
der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY]
derX = np.array(der_x)
# print(derX, derX.size, derX.shape, derX.dtype)
der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY]
derY = np.array(der_y)
# print(derY, derY.size, derY.shape, derY.dtype)
x = arr[:, 0]
y = arr[:, 1]
data = arr[:, 2]
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)
### devo duplicare primo elemento della lista altrimenti skippa a visualizzare i primi due sul video
trajectory = [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]
# trajectory = [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]
print('len trajectory : ', len(trajectory))
i = islice(trajectory, 0 , len(trajectory), 1)
def init_animate():
ax.plot_trisurf(x, y, data, color="blue", alpha=0.5)
pippo = None
def animate(j): #funziona senza stop iteration with animation.save
global pippo
try:
coord = next(i)
print(coord)
except:
print('coord empty')
global ani
global ani2
ani = None
ani2 = None
return
if pippo:
pippo.remove()
# print(coord)
x = coord[0]
y = coord[1]
z = coord[2]
pippo = ax.scatter(x,y,z)
return pippo
# ani = animation.FuncAnimation(fig, animate ,frames=len(trajectory),init_func=init_animate, interval=1000, repeat=False)
ani = animation.FuncAnimation(fig, animate, init_func=init_animate, interval=1000, repeat=False)
plt.show()
print('test to see if everything is fine')
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)
print('#####################')
i = islice(trajectory, 0 , len(trajectory), 1)
ani2 = animation.FuncAnimation(fig, animate, init_func=init_animate, interval=1000, repeat=False).save("movie_init_no-frames.gif", writer=animation.PillowWriter(fps=1))
不知道发生了什么,但算法一直在运行,输出:
len trajectory : 10
(2.5, 3.5, 27.5)
(2.0, 3.0, 21.0)
(1.5, 2.5, 15.5)
(1.0, 2.0, 11.0)
(0.5, 1.5, 7.5)
(0.0, 1.0, 5.0)
(0.0, 0.5, 3.25)
(0.0, 0.0, 2.0)
(0.0, -0.5, 1.25)
(0.0, -1.0, 1.0)
coord empty
test to see if everything is fine
#####################
(2.5, 3.5, 27.5)
(2.0, 3.0, 21.0)
(1.5, 2.5, 15.5)
(1.0, 2.0, 11.0)
(0.5, 1.5, 7.5)
(0.0, 1.0, 5.0)
(0.0, 0.5, 3.25)
(0.0, 0.0, 2.0)
(0.0, -0.5, 1.25)
(0.0, -1.0, 1.0)
coord empty
coord empty
coord empty
^Z
[9]+ Stopped
我想它与 FuncAnimation
的内部有关。
尽管如此,我应该花时间尝试理解j
文档
根据help(matplotlib.animation)
......
class FuncAnimation(TimedAnimation)
| FuncAnimation(fig, func, frames=None, init_func=None, fargs=None, save_count=None, *, cache_frame_data=True, **kwargs)
|
| Makes an animation by repeatedly calling a function *func*.
|
| Parameters
| ----------
| fig : `~matplotlib.figure.Figure`
| The figure object used to get needed events, such as draw or resize.
|
| func : callable
| The function to call at each frame. The first argument will
| be the next value in *frames*. Any additional positional
| arguments can be supplied via the *fargs* parameter.
|
| The required signature is::
|
| def func(frame, *fargs) -> iterable_of_artists
|
| If ``blit == True``, *func* must return an iterable of all artists
| that were modified or created. This information is used by the blitting
| algorithm to determine which parts of the figure have to be updated.
| The return value is unused if ``blit == False`` and may be omitted in
| that case.
......
【讨论】:
以上是关于Matplotlib animation.FuncAnimation() 动画错过第一帧?的主要内容,如果未能解决你的问题,请参考以下文章