我正在用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
在我的计算机上执行需要
Execution: 51.32 s
,因此我正在寻找处理绘图的替代方法,无论是保留背景绘图还是使用额外的动画包。
没有任何花哨的位块传输(这可能会更加显着地加快速度),这几个简单的更改将我这边的执行时间缩短到不到 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" % ())