很难用 moviepy 动画 matplotlib 图
Difficulty animating a matplotlib graph with moviepy
我必须制作大量 (~90,000) 个人物的动画。对于上下文,它是 1700 年至 1950 年每一天的地图图,并在相关日期标记了感兴趣的事件。
我可以使用 matplotlib.animation.FuncAnimation
来做到这一点,并且我有代码可以在一个小的测试期内成功地做到这一点。然而,对于完整的图形集,这将花费不切实际的时间来渲染并且会导致非常大的电影文件。
我读到显然 moviepy
提供速度和文件大小优势。但是,我无法让它正常工作——我相信我的问题是我不明白如何正确设置 duration
和 fps
参数。
我的代码的简化版本是:
import numpy as np
import matplotlib.pyplot as plt
from moviepy.video.io.bindings import mplfig_to_npimage
import moviepy.editor as mpy
fig = plt.figure()
ax = plt.axes()
x = np.random.randn(10,1)
y = np.random.randn(10,1)
p = plt.plot(x,y,'ko')
time = np.arange(2341973,2342373)
def animate(i):
xn = x+np.sin(2*np.pi*time[i]/10.0)
yn = y+np.cos(2*np.pi*time[i]/8.0)
p[0].set_data(xn,yn)
return mplfig_to_npimage(fig)
fps = 1
duration = len(time)
animation = mpy.VideoClip(animate, duration=duration)
animation.write_videofile("test.mp4", fps=fps)
但是,这不会产生为 time
的每个元素制作一帧电影并将其保存为 .mp4 的预期结果。我看不出哪里出了问题,任何帮助或指点将不胜感激。
最良好的祝愿,
卢克
相同的观察结果:
- 在
animate
中将传递一个浮点数
- 每秒一帧可能会导致许多播放器出现播放问题。最好使用更大的帧速率,例如 15 fps。
- 使用 15 fps 将需要很多帧。最好使用缓存。
因此您可以执行以下操作:
import numpy as np
import matplotlib.pyplot as plt
from moviepy.video.io.bindings import mplfig_to_npimage
import moviepy.editor as mpy
fig = plt.figure()
ax = plt.axes()
x = np.random.randn(10, 1)
y = np.random.randn(10, 1)
p = plt.plot(x, y, 'ko')
time = np.arange(2341973, 2342373)
cache = {}
def animate(t):
i = int(t)
if i in cache:
return cache[i]
xn = x + np.sin(2 * np.pi * time[i] / 10.0)
yn = y + np.cos(2 * np.pi * time[i] / 8.0)
p[0].set_data(xn, yn)
cache.clear()
cache[i] = mplfig_to_npimage(fig)
return cache[i]
duration = len(time)
fps = 15
animation = mpy.VideoClip(animate, duration=duration)
animation.write_videofile("test.mp4", fps=fps)
与 JuniorCompressor 相同的解决方案,只有一帧保留在内存中以避免 RAM 问题。这个例子在我的机器上运行了 30 秒,并产生了一个质量很好的 400 秒的 6000 帧剪辑,重 600k。
import numpy as np
import matplotlib.pyplot as plt
from moviepy.video.io.bindings import mplfig_to_npimage
import moviepy.editor as mpy
fig = plt.figure(facecolor="white") # <- ADDED FACECOLOR FOR WHITE BACKGROUND
ax = plt.axes()
x = np.random.randn(10, 1)
y = np.random.randn(10, 1)
p = plt.plot(x, y, 'ko')
time = np.arange(2341973, 2342373)
last_i = None
last_frame = None
def animate(t):
global last_i, last_frame
i = int(t)
if i == last_i:
return last_frame
xn = x + np.sin(2 * np.pi * time[i] / 10.0)
yn = y + np.cos(2 * np.pi * time[i] / 8.0)
p[0].set_data(xn, yn)
last_i = i
last_frame = mplfig_to_npimage(fig)
return last_frame
duration = len(time)
fps = 15
animation = mpy.VideoClip(animate, duration=duration)
animation.write_videofile("test.mp4", fps=fps)
在旁注中,有专门的 class 视频剪辑称为 DataVideoClip 正是出于这个目的,它看起来更像 matplotlib 的 animate
。目前它并不是真正的速度高效(我没有包括上面的那个小记忆技巧)。它是这样工作的:
from moviepy.video.VideoClip import DataVideoClip
def data_to_frame(time):
xn = x + np.sin(2 * np.pi * time / 10.0)
yn = y + np.cos(2 * np.pi * time / 8.0)
p[0].set_data(xn, yn)
return mplfig_to_npimage(fig)
times = np.arange(2341973, 2342373)
clip = DataVideoClip(times, data_to_frame, fps=1) # one plot per second
#final animation is 15 fps, but still displays 1 plot per second
animation.write_videofile("test2.mp4", fps=15)
我必须制作大量 (~90,000) 个人物的动画。对于上下文,它是 1700 年至 1950 年每一天的地图图,并在相关日期标记了感兴趣的事件。
我可以使用 matplotlib.animation.FuncAnimation
来做到这一点,并且我有代码可以在一个小的测试期内成功地做到这一点。然而,对于完整的图形集,这将花费不切实际的时间来渲染并且会导致非常大的电影文件。
我读到显然 moviepy
提供速度和文件大小优势。但是,我无法让它正常工作——我相信我的问题是我不明白如何正确设置 duration
和 fps
参数。
我的代码的简化版本是:
import numpy as np
import matplotlib.pyplot as plt
from moviepy.video.io.bindings import mplfig_to_npimage
import moviepy.editor as mpy
fig = plt.figure()
ax = plt.axes()
x = np.random.randn(10,1)
y = np.random.randn(10,1)
p = plt.plot(x,y,'ko')
time = np.arange(2341973,2342373)
def animate(i):
xn = x+np.sin(2*np.pi*time[i]/10.0)
yn = y+np.cos(2*np.pi*time[i]/8.0)
p[0].set_data(xn,yn)
return mplfig_to_npimage(fig)
fps = 1
duration = len(time)
animation = mpy.VideoClip(animate, duration=duration)
animation.write_videofile("test.mp4", fps=fps)
但是,这不会产生为 time
的每个元素制作一帧电影并将其保存为 .mp4 的预期结果。我看不出哪里出了问题,任何帮助或指点将不胜感激。
最良好的祝愿, 卢克
相同的观察结果:
- 在
animate
中将传递一个浮点数 - 每秒一帧可能会导致许多播放器出现播放问题。最好使用更大的帧速率,例如 15 fps。
- 使用 15 fps 将需要很多帧。最好使用缓存。
因此您可以执行以下操作:
import numpy as np
import matplotlib.pyplot as plt
from moviepy.video.io.bindings import mplfig_to_npimage
import moviepy.editor as mpy
fig = plt.figure()
ax = plt.axes()
x = np.random.randn(10, 1)
y = np.random.randn(10, 1)
p = plt.plot(x, y, 'ko')
time = np.arange(2341973, 2342373)
cache = {}
def animate(t):
i = int(t)
if i in cache:
return cache[i]
xn = x + np.sin(2 * np.pi * time[i] / 10.0)
yn = y + np.cos(2 * np.pi * time[i] / 8.0)
p[0].set_data(xn, yn)
cache.clear()
cache[i] = mplfig_to_npimage(fig)
return cache[i]
duration = len(time)
fps = 15
animation = mpy.VideoClip(animate, duration=duration)
animation.write_videofile("test.mp4", fps=fps)
与 JuniorCompressor 相同的解决方案,只有一帧保留在内存中以避免 RAM 问题。这个例子在我的机器上运行了 30 秒,并产生了一个质量很好的 400 秒的 6000 帧剪辑,重 600k。
import numpy as np
import matplotlib.pyplot as plt
from moviepy.video.io.bindings import mplfig_to_npimage
import moviepy.editor as mpy
fig = plt.figure(facecolor="white") # <- ADDED FACECOLOR FOR WHITE BACKGROUND
ax = plt.axes()
x = np.random.randn(10, 1)
y = np.random.randn(10, 1)
p = plt.plot(x, y, 'ko')
time = np.arange(2341973, 2342373)
last_i = None
last_frame = None
def animate(t):
global last_i, last_frame
i = int(t)
if i == last_i:
return last_frame
xn = x + np.sin(2 * np.pi * time[i] / 10.0)
yn = y + np.cos(2 * np.pi * time[i] / 8.0)
p[0].set_data(xn, yn)
last_i = i
last_frame = mplfig_to_npimage(fig)
return last_frame
duration = len(time)
fps = 15
animation = mpy.VideoClip(animate, duration=duration)
animation.write_videofile("test.mp4", fps=fps)
在旁注中,有专门的 class 视频剪辑称为 DataVideoClip 正是出于这个目的,它看起来更像 matplotlib 的 animate
。目前它并不是真正的速度高效(我没有包括上面的那个小记忆技巧)。它是这样工作的:
from moviepy.video.VideoClip import DataVideoClip
def data_to_frame(time):
xn = x + np.sin(2 * np.pi * time / 10.0)
yn = y + np.cos(2 * np.pi * time / 8.0)
p[0].set_data(xn, yn)
return mplfig_to_npimage(fig)
times = np.arange(2341973, 2342373)
clip = DataVideoClip(times, data_to_frame, fps=1) # one plot per second
#final animation is 15 fps, but still displays 1 plot per second
animation.write_videofile("test2.mp4", fps=15)