加速 matplotlib 创建动画帧图像

问题描述 投票:0回答:1

我正在用Python模拟一些物理过程,并希望定期绘制系统的情况以制作动画。到目前为止,我一直在做的是定期绘制系统快照,将其保存到文件,最后使用

convert
ffmpeg
将图像转换为视频。然而,最近,绘图变得越来越复杂,现在只需要 2-3 秒才能生成一张图像,这对于我的需求来说太长了。

在我的系统中,我有一些固定且永远不会改变的背景元素,以及一些其他元素会根据时间发生移动。作为最小工作示例,请考虑以下代码:

import matplotlib.pyplot as plt
import numpy as np
import time


class MWE():
    def __init__(self):
        self.x = 0.0
        self.y = 0.0
        self.dt = 0.01
        self.imageCounter = 0
        self.elapsed_time = 0.0

    def evolve(self):
        theta = 2 * np.pi * self.elapsed_time
        self.x = np.cos(theta)
        self.y = np.sin(theta)
        self.elapsed_time += self.dt

    def draw(self):
        fig, ax = plt.subplots(figsize=(2, 2), layout='constrained')
        ax.set_aspect('equal')
        ax.axis('off')

        ax.set_xlim((-2, 2))
        ax.set_ylim((-2.2, 2))

        for center_x in np.linspace(-2,2, 21)[1::2]:
            for center_y in np.linspace(-2,2, 21)[1::2]:
                blue_circle = plt.Circle((center_x, center_y), 0.1, color='blue', fill=False)
                ax.add_patch(blue_circle)

        red_circle = plt.Circle((self.x, self.y), 0.1, color='red', fill=True)
        ax.add_patch(red_circle)
        self.imageCounter += 1

        ax.text(-1.9, -2.2, f'Time: {self.elapsed_time:.2f} s', color='black', size=8)

        fig.savefig(f'MWE_{str(self.imageCounter).zfill(3)}.png')
        plt.clf()
        plt.cla()
        plt.close()


mwe = MWE()

start_time = time.time()
while mwe.elapsed_time < 1.0:
    mwe.evolve()
    mwe.draw()
print(f"Execution: {time.time() - start_time:.2f} s" % ())
convert -resize 100% -delay 2 -loop 0 MWE_*.png loop.gif

Resulting GIF

在我的计算机上执行需要

Execution: 51.32 s
,因此我正在寻找处理绘图的替代方法,无论是保留背景绘图还是使用额外的动画包。

python matplotlib animation optimization
1个回答
0
投票

没有任何花哨的位块传输(这可能会更加显着地加快速度),这几个简单的更改将我这边的执行时间缩短到不到 4 秒:

  • 仅初始化图形一次
  • 仅绘制一次艺术家并更新位置
  • 切换到
    agg
    后端以避免图形用户界面

import matplotlib.pyplot as plt
import numpy as np
import time

plt.switch_backend("agg")

class MWE():
    def __init__(self):
        self.x = 0.0
        self.y = 0.0
        self.dt = 0.01
        self.imageCounter = 0
        self.elapsed_time = 0.0
        
        self.init_figure()

    def init_figure(self):
        self.fig, ax = plt.subplots(figsize=(2, 2), layout='constrained')
        ax.set_aspect('equal')
        ax.axis('off')

        ax.set_xlim((-2, 2))
        ax.set_ylim((-2.2, 2))

        for center_x in np.linspace(-2,2, 21)[1::2]:
            for center_y in np.linspace(-2,2, 21)[1::2]:
                blue_circle = plt.Circle((center_x, center_y), 0.1, color='blue', fill=False)
                ax.add_patch(blue_circle)

        self.red_circle = plt.Circle((self.x, self.y), 0.1, color='red', fill=True)
        ax.add_patch(self.red_circle)
        self.info_text = ax.text(-1.9, -2.2, f'Time: {self.elapsed_time:.2f} s', color='black', size=8)

    def evolve(self):
        theta = 2 * np.pi * self.elapsed_time
        self.x = np.cos(theta)
        self.y = np.sin(theta)
        self.elapsed_time += self.dt

    def draw(self):
        self.red_circle.set_center((self.x, self.y))
        self.info_text.set_text(f'Time: {self.elapsed_time:.2f} s')
        self.imageCounter += 1

        self.fig.savefig(f'MWE_{str(self.imageCounter).zfill(3)}.png')


mwe = MWE()
mwe.init_figure()

# %%
start_time = time.time()
while mwe.elapsed_time < 1.0:
    mwe.evolve()
    mwe.draw()
print(f"Execution: {time.time() - start_time:.2f} s" % ())

© www.soinside.com 2019 - 2024. All rights reserved.