I'm having issues with a slow animation in Matplotlib. I'm animating results from a simulation, which is easiest visualized with an array of rectangles that change color with time.
Following recommendations here, I'm using blitting to only draw the (small fraction) of rectangles that change in each frame. I also tried to implement this using FuncAnimation, but when using that with Blit=True, the script runs much slower.
I'm wondering if this is because I'm returning all of the rectangles to FuncAnimation, so it redraws all of them even if they haven't changed. Is there a way to pass different artists at each frame to FuncAnimation? I tried just passing a tuple of the ones that had changed (the commented out block in the "animate" function), but that led to seemingly random animation frames...
Use:
$ python2 [script].py blit
$ python2 [script].py anim
Thanks!
import sys
import numpy as np
import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
import matplotlib.animation as manim
def animate_data(plot_type):
"""
Use:
python2 plot_anim.py [option]
option = anim OR blit
"""
# dimension parameters
Nx = 30
Ny = 20
numtimes = 100
size = 0.5
if plot_type == "blit":
# "interactive mode on"
plt.ion()
# Prepare to do initial plot
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.set_aspect('equal', 'box')
ax.xaxis.set_major_locator(plt.NullLocator())
ax.yaxis.set_major_locator(plt.NullLocator())
# An array in which to store the rectangle artists
rects = np.empty((Nx, Ny), dtype=object)
# Generate initial figure of all green rectangles
for (i,j),k in np.ndenumerate(rects):
color = 'green'
rects[i, j] = plt.Rectangle([i - size / 2, j - size / 2],
size, size, facecolor=color, edgecolor=color)
ax.add_patch(rects[i, j])
ax.autoscale_view()
# "Old" method using fig.canvas.blit()
if plot_type == "blit":
plt.show()
fig.canvas.draw()
# Step through time updating the rectangles
for tind in range(1, numtimes):
updated_array = update_colors(rects)
for (i, j), val in np.ndenumerate(updated_array):
if val:
ax.draw_artist(rects[i, j])
fig.canvas.blit(ax.bbox)
# New method using FuncAnimate
elif plot_type == "anim":
def animate(tind):
updated_array = update_colors(rects)
# # Just pass the updated artists to FuncAnimation
# toupdate = []
# for (i, j), val in np.ndenumerate(updated_array):
# if val:
# toupdate.append(rects[i, j])
# return tuple(toupdate)
return tuple(rects.reshape(-1))
ani = manim.FuncAnimation(fig, animate, frames=numtimes,
interval=10, blit=True, repeat=False)
plt.show()
return
# A function to randomly update a few rectangles
def update_colors(rects):
updated_array = np.zeros(rects.shape)
for (i, j), c in np.ndenumerate(rects):
rand_val = np.random.rand()
if rand_val < 0.003:
rects[i, j].set_facecolor('red')
rects[i, j].set_edgecolor('red')
updated_array[i, j] = 1
return updated_array
if __name__ == "__main__":
if len(sys.argv) > 1:
plot_type = sys.argv[1]
else:
plot_type = "blit"
animate_data(plot_type)