Difficulty animating a matplotlib graph with moviepy

后端 未结 2 932
礼貌的吻别
礼貌的吻别 2021-02-09 08:40

I have to make an animation of a large number (~90,000) figures. For context, it\'s a plot of a map for every day from 1700 - 1950, with events of interest marked on relevent da

相关标签:
2条回答
  • 2021-02-09 09:31

    Same solution as JuniorCompressor, with just one frame kept in memory to avoid RAM issues. This example runs in 30 seconds on my machine and produces a good quality 400-second clip of 6000 frames, weighing 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)
    

    On a sidenote, there is dedicated class of videoclips called DataVideoClip for precisely this purpose, which looks much more like matplotlib's animate. For the moment it's not really speed-efficient (I didn't include that little memoizing trick above). Here is how it works:

    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) 
    
    0 讨论(0)
  • 2021-02-09 09:33

    Same observations:

    • In animate a float number will be passed
    • One frame per second may cause playback problems in many players. It's better to use a bigger frame rate like 15 fps.
    • Using 15 fps will need many frames. It's better to use caching.

    So you can do the following:

    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)
    
    0 讨论(0)
提交回复
热议问题