8

我必须制作大量(〜90,000)人物的动画。就上下文而言,它是从 1700 年到 1950 年的每一天的地图图,并在相关日期标记了感兴趣的事件。我可以使用 来执行此操作matplotlib.animation.FuncAnimation,并且我的代码可以在一小段测试期内成功执行此操作。但是,对于完整的图形集,渲染需要花费不切实际的时间,并且会产生非常大的电影文件。我读过这显然moviepy提供了速度和文件大小的优势。但是,我无法让它工作——我相信我的问题是我不明白如何正确设置durationandfps参数。

我的代码的简化版本是:

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 的预期结果。我看不到我哪里出错了,任何帮助或指示将不胜感激。

最好的祝愿,卢克

4

2 回答 2

7

与 JuniorCompressor 相同的解决方案,在内存中只保留一帧以避免 RAM 问题。这个例子在我的机器上运行了 30 秒,并产生了一个 6000 帧的高质量 400 秒剪辑,重 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)

在旁注中,有一个专门的视频剪辑类称为 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) 
于 2015-05-01T08:39:13.180 回答
3

相同的观察:

  • 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)
于 2015-04-30T18:47:22.413 回答