在matplotlib动画的特定帧之间添加延迟

时间:2019-02-16 23:28:47

标签: python matplotlib

我想使用FuncAnimationmatplotlib中创建动画。动画包含各种“阶段”,我想通过向两个相应帧之间的间隔添加额外的延迟来分离(强调)这些阶段。考虑下面的示例代码,该代码绘制了五个圆,并且每两个连续圆的绘制应间隔1秒:

import time
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
import numpy as np

f, ax = plt.subplots()
ax.set_xlim([-5, 5])
ax.set_ylim([-5, 5])

radius = 1
dp = 2*np.pi / 50
circles = [[(radius, 0)]]
plots = ax.plot([radius], [0])

def update(frame):
    global radius

    if frame % 50 == 0:
        radius += 1
        circles.append([(radius, 0)])
        plots.extend(ax.plot([radius], [0]))
        # I want to add a delay here, i.e. before the drawing of a new circle starts.
        # This works for `plt.show()` however it doesn't when saving the animation.
        time.sleep(1)
    angle = (frame % 50) * dp
    circles[-1].append((radius * np.cos(angle), radius * np.sin(angle)))
    plots[-1].set_data(*zip(*circles[-1]))
    return plots[-1]

animation = FuncAnimation(f, update, frames=range(1, 251), interval=50, repeat=False)

### Uncomment one of the following options.
# animation.save('test.mp4', fps=20)
# with open('test.html', 'w') as fh:
#     fh.write(animation.to_html5_video())
# plt.show()

此功能在通过plt.show()播放动画时有效,但是在另存为.mp4或HTML5视频时无效。这是有道理的,因为根据文档,FPS确定mp4视频的帧延迟,并且interval参数用于HTML5视频。然后,只是一帧接一帧地播放(也忽略了任何计算时间)。

那么我该如何添加保存动画时保留的延迟?

2 个答案:

答案 0 :(得分:0)

您应该能够为frames参数使用生成函数。例如:

from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
import numpy as np

INTERVAL = 50  # ms
HOLD_MS  = 1000
HOLD_COUNT = HOLD_MS // INTERVAL

def frame_generator():
    for frame in range(1, 251):
        # Yield the frame first
        yield frame
        # If we should "sleep" here, yield None HOLD_COUNT times
        if frame % 50 == 0:
            for _ in range(HOLD_COUNT):
                yield None


f, ax = plt.subplots()
ax.set_xlim([-5, 5])
ax.set_ylim([-5, 5])

radius = 1
dp = 2*np.pi / 50
circles = [[(radius, 0)]]
plots = ax.plot([radius], [0])

def update(frame):
    global radius

    if frame is None: return   #--------------------------------- Added

    if frame % 50 == 0:
        radius += 1
        circles.append([(radius, 0)])
        plots.extend(ax.plot([radius], [0]))
        #-------------------------------------------------------- sleep removed

    angle = (frame % 50) * dp
    circles[-1].append((radius * np.cos(angle), radius * np.sin(angle)))
    plots[-1].set_data(*zip(*circles[-1]))
    return plots[-1]

# Slightly changed
animation = FuncAnimation(f, update, frames=frame_generator(), interval=INTERVAL, repeat=False)
plt.show()

应该工作。

print(list(frame_generator()))

可能有助于弄清正在发生的事情。

答案 1 :(得分:0)

您可以使用frame参数来引导动画。本质上,在帧n之后的暂停与重复显示帧编号n直到暂停结束相同。例如。如果您以每秒1帧的速度运行动画,并且在第二帧之后要暂停3秒,则可以提供

0, 1, 1, 1, 1, 2, 3, ....

作为框架,因此编号为1的框架显示了四次。

可以在您的代码中按以下步骤应用该概念。

from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt
import numpy as np

f, ax = plt.subplots()
ax.set_xlim([-5, 5])
ax.set_ylim([-5, 5])

radius = 0
bu = 50
dp = 2*np.pi / bu
circles = [[(radius, 0)]]
plots = ax.plot([radius], [0])

def update(frame):
    global radius

    if frame % bu == 0:
        radius += 1
        circles.append([(radius, 0)])
        plots.extend(ax.plot([radius], [0]))

    angle = (frame % bu) * dp
    circles[-1].append((radius * np.cos(angle), radius * np.sin(angle)))
    plots[-1].set_data(*zip(*circles[-1]))
    return plots[-1]


interval = 50 # milliseconds
pause = int(1 * 1000 / interval)
cycles = 4
frames = []
for c in range(cycles):
    frames.extend([np.arange(c*bu, (c+1)*bu), np.ones(pause)*((c+1)*bu)])
frames = np.concatenate(frames)

animation = FuncAnimation(f, update, frames=frames, interval=50, repeat=False)

### Uncomment one of the following options.
# animation.save('test.mp4', fps=20)
# with open('test.html', 'w') as fh:
#     fh.write(animation.to_html5_video())
plt.show()