I'm trying to display the frames captured by a CCD camera in real-time using pyplot animation. I wrote a short python script just to test this out, and while it works, it does so erratically. It will quickly update a dozen or so animation frames, then pause for a second, then update again, then pause again, and so on. I'd like to just have it update the plot continuously and smoothly, but I'm not sure where I'm going wrong.
I know it isn't the part where it makes a call to the camera's frame buffer; I tested out just calling that in a loop and it never slowed down, so I think it's somewhere in the actual processing of the animation frames.
My code is below:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import Pixis100
import time
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = fig.add_subplot(111)
# Create ccd object
ccd = Pixis100.device()
ccd.snapshot()
ccd.focusStart()
# focusStart() tells the camera to start putting captured frames into the buffer
line, = ax.plot(ccd.getFrame()[:,2:].sum(axis=0)[::-1],'b-')
# getFrame() makes a call to the CCD frame buffer and retrieves the most recent frame
# animation function
def update(data):
line.set_ydata(data)
return line,
def data_gen():
while True: yield ccd.getFrame()[:,2:].sum(axis=0)[::-1]
# call the animator
anim = animation.FuncAnimation(fig, update, data_gen,interval=10,blit=True)
plt.show()
ccd.focusStop()
# focusStop() just tells the camera to stop capturing frames
For reference, ccd.getFrame()[:,2:].sum(axis=0)[::-1] returns a 1x1338 array of integers. I wouldn't think this would be too much for the animation to handle at one time.
The problem is not in animation, the following works just fine:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import time
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_xlim([0, 2 *np.pi])
ax.set_ylim([-1, 1])
th = np.linspace(0, 2 * np.pi, 1000)
line, = ax.plot([],[],'b-', animated=True)
line.set_xdata(th)
# getFrame() makes a call to the CCD frame buffer and retrieves the most recent frame
# animation function
def update(data):
line.set_ydata(data)
return line,
def data_gen():
t = 0
while True:
t +=1
yield np.sin(th + t * np.pi/100)
# call the animator
anim = animation.FuncAnimation(fig, update, data_gen, interval=10, blit=True)
plt.show()
The choppyness may come from either your frame grabber, the computation your are doing on it, or issues with the gui getting enough time on the main thread to re-draw. See time.sleep() required to keep QThread responsive?
Related
I am trying to automatically update a scatter plot.
The source of my X and Y values is external, and the data is pushed automatically into my code in a non-predicted time intervals (rounds).
I have only managed to plot all the data when the whole process ended, whereas I am trying to constantly add and plot data into my canvas.
What I DO get (at the end of the whole run) is this:
Whereas, what I am after is this:
A simplified version of my code:
import matplotlib.pyplot as plt
def read_data():
#This function gets the values of xAxis and yAxis
xAxis = [some values] #these valuers change in each run
yAxis = [other values] #these valuers change in each run
plt.scatter(xAxis,yAxis, label = 'myPlot', color = 'k', s=50)
plt.xlabel('x')
plt.ylabel('y')
plt.show()
There are several ways to animate a matplotlib plot. In the following let's look at two minimal examples using a scatter plot.
(a) use interactive mode plt.ion()
For an animation to take place we need an event loop. One way of getting the event loop is to use plt.ion() ("interactive on"). One then needs to first draw the figure and can then update the plot in a loop. Inside the loop, we need to draw the canvas and introduce a little pause for the window to process other events (like the mouse interactions etc.). Without this pause the window would freeze. Finally we call plt.waitforbuttonpress() to let the window stay open even after the animation has finished.
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
fig, ax = plt.subplots()
x, y = [],[]
sc = ax.scatter(x,y)
plt.xlim(0,10)
plt.ylim(0,10)
plt.draw()
for i in range(1000):
x.append(np.random.rand(1)*10)
y.append(np.random.rand(1)*10)
sc.set_offsets(np.c_[x,y])
fig.canvas.draw_idle()
plt.pause(0.1)
plt.waitforbuttonpress()
(b) using FuncAnimation
Much of the above can be automated using matplotlib.animation.FuncAnimation. The FuncAnimation will take care of the loop and the redrawing and will constantly call a function (in this case animate()) after a given time interval. The animation will only start once plt.show() is called, thereby automatically running in the plot window's event loop.
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
fig, ax = plt.subplots()
x, y = [],[]
sc = ax.scatter(x,y)
plt.xlim(0,10)
plt.ylim(0,10)
def animate(i):
x.append(np.random.rand(1)*10)
y.append(np.random.rand(1)*10)
sc.set_offsets(np.c_[x,y])
ani = matplotlib.animation.FuncAnimation(fig, animate,
frames=2, interval=100, repeat=True)
plt.show()
From what I understand, you want to update interactively your plot. If so, you can use plot instead of scatter plot and update the data of your plot like this.
import numpy
import matplotlib.pyplot as plt
fig = plt.figure()
axe = fig.add_subplot(111)
X,Y = [],[]
sp, = axe.plot([],[],label='toto',ms=10,color='k',marker='o',ls='')
fig.show()
for iter in range(5):
X.append(numpy.random.rand())
Y.append(numpy.random.rand())
sp.set_data(X,Y)
axe.set_xlim(min(X),max(X))
axe.set_ylim(min(Y),max(Y))
raw_input('...')
fig.canvas.draw()
If this is the behaviour your are looking for, you just need to create a function appending the data of sp, and get in that function the new points you want to plot (either with I/O management or whatever the communication process you're using).
I hope it helps.
This programme fills a figure with square patches. The y axis limit is set so that it will be seen that there is only one patch in one position. It plots this filling process. I want to record the filling as an animation and am trying to do so with 'matplotlib.animation'. I turn the plotting part of the programme into a function (def filler(b):) so that I can pass this function to the animation lines at the bottom. When I run the programme I get an error right at the end of the plotting saying Python has stopped working. Please could somebody explain why. Thanks.
Note that I don't know what the b in the function argument is meant to represent. I include it because without it the programme doesn't run, asking for a positional argument.
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.animation as animation
import numpy as np
startx = 0
endx = 10
blocks = 100
points = np.random.randint(startx,endx,size=blocks)
y = [-1]*int(endx-startx)
fig = plt.figure(figsize=(5,6))
ax = fig.add_subplot(111,aspect='equal')
ax.set_xlim(startx,endx)
ax.set_ylim(0,5)
def filler(b):
for i in range(blocks):
z = 5
a = patches.Rectangle((points[i],z),1,1,ec='k',fc=(1-i/blocks,i/(2*blocks),i/blocks))
ax.add_patch(a)
while z>y[int(points[i])]+1:
z=z-1
plt.pause(0.001)
a.set_y(z)
y[int(points[i])]=z
filler_ani = animation.FuncAnimation(fig, filler,interval=50, repeat=False, blit=True)
filler_ani.save('filler.mp4')
The code in the question mixes two different types of animations. Using a loop and plt.draw(), and a FuncAnimation. This will lead to chaos, as essentially the complete animation on screen is done during the first frame of the FuncAnimation, at the end of that first frame the animation fails.
So, one has to decide. Since it seems you want to do a FuncAnimation here, in order to be able to save it, one needs to get rid of the plt.draw. Then the problem is that there is a for loop and a while loop. This makes it hard to use a framenumber based animation.
Instead one may use a generator based animation.
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.animation as animation
import numpy as np
startx = 0
endx = 10
blocks = 101
points = np.random.randint(startx,endx,size=blocks)
y = [-1]*int(endx-startx)
fig = plt.figure(figsize=(5,6))
ax = fig.add_subplot(111,aspect='equal')
ax.set_xlim(startx,endx)
ax.set_ylim(0,5)
def filler():
yield None
for i in range(blocks):
z = 5
a = patches.Rectangle((points[i],z),1,1,ec='k',fc="r")
ax.add_patch(a)
while z>y[int(points[i])]+1:
z=z-1
a.set_y(z)
yield a
y[int(points[i])]=z
filler_ani = animation.FuncAnimation(fig, lambda x: None, frames=filler,
interval=50, blit=False, repeat=False)
plt.show()
This is kind of hacky, but stays most closely to your initial code.
I wrote a code that generates and shows a matplotlib animation. I am also able to save the animation as gif.
However when I do these two actions sequentially (first saving the gif, and then showing the animation, because plt.show() is a blocking call, the shown animation doesn't start from the beginning; it seems it's first used by the gif saving routine.
My idea is to show the animation in the window using plt.show(), and save the same animation as a gif to disk (before, or ideally during the animation). I've tried to solve this using Threads or creating new figure, but I wasn't able to pull it off - the shown animation was always modified by the saving routine. Also, putting plt.show into a thread didn't work (because it handles drawing to screen, I suppose, so it needs to be in a main thread).
Here's the code I'm using:
import matplotlib.pyplot as plt
from matplotlib import animation
from visualizer import visualize
import updater
def run(k, update_func=updater.uniform,
steps=1000, vis_steps=50,
alpha_init=0.3, alpha_const=100, alpha_speed=0.95,
diameter_init=3, diameter_const=1, diameter_speed=1,
xlim=(-1, 1), ylim=(-1, 1),
points_style='ro', lines_style='b-',
save_as_gif=False):
fig = plt.figure()
ax = fig.add_subplot(111, xlim=xlim, ylim=ylim)
global curr_alpha, curr_diameter, points, lines
points, = ax.plot([], [], points_style)
lines, = ax.plot([], [], lines_style)
curr_alpha = alpha_init
curr_diameter = diameter_init
def update_attributes(i):
global curr_alpha, curr_diameter
if i % alpha_const == 0:
curr_alpha *= alpha_speed
if i % diameter_const == 0:
curr_diameter *= diameter_speed
def init():
lines.set_data([], [])
points.set_data([], [])
def loop(i):
for j in range(vis_steps):
update_func(k, curr_alpha=curr_alpha, curr_diameter=curr_diameter)
update_attributes(i * vis_steps + j)
visualize(k, points, lines)
return points, lines
anim = animation.FuncAnimation(fig, loop, init_func=init, frames=steps // vis_steps, interval=1, repeat=False)
if save_as_gif:
anim.save('gifs/%s.gif' % save_as_gif, writer='imagemagick')
plt.show()
the code here below shows and saves an animation of random matrices in succession. My question is how can I adjust the duration of the animation that I save. The only parameters that I have here fps, and dpi control first how many seconds a frame remains and the second controls the quality of the image. What I want is to actually control the number of frames that are going to be saved in terms of the matrices the number of them that are actually stored.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
N = 5
A = np.random.rand(N,N)
im = plt.imshow(A)
def updatefig(*args):
im.set_array(np.random.rand(N,N))
return im,
ani = animation.FuncAnimation(fig, updatefig, interval=200, blit=True)
ani.save('try_animation.mp4', fps=10, dpi=80) #Frame per second controls speed, dpi controls the quality
plt.show()
I am wonderinf if I should add more parameters. I tried to look for the appropriate one in the class documentation in matplotlib but I was unsuccessful:
http://matplotlib.org/api/animation_api.html#module-matplotlib.animation
Years later I have built this is example that I come back to every time that I need to see how the parameters of the animation relate between themselves. I decided to share it here for whoever may find it useful.
tl/dr:
For the saved animation the duration is going to be frames * (1 / fps) (in seconds)
For the display animation the duration is going to be frames * interval / 1000 (in seconds)
The code bellow allows you to play with this setting in an environment that gives immediate visual feedback.
This code builds a clock that ticks according to the parameters:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure(figsize=(16, 12))
ax = fig.add_subplot(111)
# You can initialize this with whatever
im = ax.imshow(np.random.rand(6, 10), cmap='bone_r', interpolation='nearest')
def animate(i):
aux = np.zeros(60)
aux[i] = 1
image_clock = np.reshape(aux, (6, 10))
im.set_array(image_clock)
ani = animation.FuncAnimation(fig, animate, frames=60, interval=1000)
ani.save('clock.mp4', fps=1.0, dpi=200)
plt.show()
This will generate and save an animation that will look like this:
So the point is that the black square will move along the big white square as the time is passing. There are 60 white boxes so you can build a clock that goes over it in a minute.
Now, the important thing to note is that there are two parameters that determine how fast the black box would move: interval in the animation.FuncAnimation function and 'fps' in the ani.save function. The first controls the speed in the animation that you will display and the second in the animation that you will save.
As the code above stands you will generate 60 frames and they are displayed at 1 frame per second. That means that the clock ticks every second. If you want the saved animation clock to tick every two seconds then you should set fps=0.5. If you want the displayed animation clock to click every two seconds you should set interval=2000.
[I will edit the longer explanation as soon as I have time]
The documentation reveals that FuncAnimation accepts an argument frames, which controls the total number of frames played. Your code could thus read
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
N = 5
A = np.random.rand(N,N)
im = plt.imshow(A)
def updatefig(*args):
im.set_array(np.random.rand(N,N))
return im,
ani = animation.FuncAnimation(fig, updatefig, frames=10, interval=200, blit=True)
ani.save('try_animation.mp4', fps=10, dpi=80) #Frame per second controls speed, dpi controls the quality
plt.show()
to play 10 frames.
I am trying to use matplotlib.ArtistAnimation to animate two subplots. I want the x-axis to increase in value as the animation progresses, such that the total length of the animation is 100 but at any time the subplot is only presenting me with the time values from 0-24 and then iterates up to 100.
A great example is given here. The link uses FuncAnimation and updates the x-axis labels in a rolling fashion using plot().axes.set_xlim() and incrementing the x-values. The code is available via the link below the YouTube video in the link provided.
I have appended code below that shows my attempts to replicate these results but the x-limits seem to take on their final values instead of incrementing with time. I have also tried incrementing the solution (as opposed to the axis) by only plotting the values in the window that will be seen in the subplot, but that does not increment the x-axis values. I also tried to implement autoscaling but the x-axis still does not update.
I also found this question which is virtually the same problem, but the question was never answered.
Here is my code:
import matplotlib.pylab as plt
import matplotlib.animation as anim
import numpy as np
#create image with format (time,x,y)
image = np.random.rand(100,10,10)
#setup figure
fig = plt.figure()
ax1=fig.add_subplot(1,2,1)
ax2=fig.add_subplot(1,2,2)
#set up viewing window (in this case the 25 most recent values)
repeat_length = (np.shape(image)[0]+1)/4
ax2.set_xlim([0,repeat_length])
#ax2.autoscale_view()
ax2.set_ylim([np.amin(image[:,5,5]),np.amax(image[:,5,5])])
#set up list of images for animation
ims=[]
for time in xrange(np.shape(image)[0]):
im = ax1.imshow(image[time,:,:])
im2, = ax2.plot(image[0:time,5,5],color=(0,0,1))
if time>repeat_length:
lim = ax2.set_xlim(time-repeat_length,time)
ims.append([im, im2])
#run animation
ani = anim.ArtistAnimation(fig,ims, interval=50,blit=False)
plt.show()
I only want the second subplot (ax2) to update the x-axis values.
Any help would be much appreciated.
If you don't need blitting
import matplotlib.pylab as plt
import matplotlib.animation as animation
import numpy as np
#create image with format (time,x,y)
image = np.random.rand(100,10,10)
#setup figure
fig = plt.figure()
ax1 = fig.add_subplot(1,2,1)
ax2 = fig.add_subplot(1,2,2)
#set up viewing window (in this case the 25 most recent values)
repeat_length = (np.shape(image)[0]+1)/4
ax2.set_xlim([0,repeat_length])
#ax2.autoscale_view()
ax2.set_ylim([np.amin(image[:,5,5]),np.amax(image[:,5,5])])
#set up list of images for animation
im = ax1.imshow(image[0,:,:])
im2, = ax2.plot([], [], color=(0,0,1))
def func(n):
im.set_data(image[n,:,:])
im2.set_xdata(np.arange(n))
im2.set_ydata(image[0:n, 5, 5])
if n>repeat_length:
lim = ax2.set_xlim(n-repeat_length, n)
else:
# makes it look ok when the animation loops
lim = ax2.set_xlim(0, repeat_length)
return im, im2
ani = animation.FuncAnimation(fig, func, frames=image.shape[0], interval=30, blit=False)
plt.show()
will work.
If you need to run faster, you will need to play games with the bounding box used for blitting so that the axes labels are updated.
If you are using blitting, you can call pyplot.draw() to redraw the entire figure, each time you change y/x axis.
This updates whole figure, so is relatively slow, but it's acceptable if you don't call it many items.
This moves your axis, but is very slow.
import matplotlib.pylab as plt
import matplotlib.animation as anim
import numpy as np
image = np.random.rand(100,10,10)
repeat_length = (np.shape(image)[0]+1)/4
fig = plt.figure()
ax1 = ax1=fig.add_subplot(1,2,1)
im = ax1.imshow(image[0,:,:])
ax2 = plt.subplot(122)
ax2.set_xlim([0,repeat_length])
ax2.set_ylim([np.amin(image[:,5,5]),np.amax(image[:,5,5])])
im2, = ax2.plot(image[0:0,5,5],color=(0,0,1))
canvas = ax2.figure.canvas
def init():
im = ax1.imshow(image[0,:,:])
im2.set_data([], [])
return im,im2,
def animate(time):
time = time%len(image)
im = ax1.imshow(image[time,:,:])
im2, = ax2.plot(image[0:time,5,5],color=(0,0,1))
if time>repeat_length:
print time
im2.axes.set_xlim(time-repeat_length,time)
plt.draw()
return im,im2,
ax2.get_yaxis().set_animated(True)
# call the animator. blit=True means only re-draw the parts that have changed.
animate = anim.FuncAnimation(fig, animate, init_func=init,
interval=0, blit=True, repeat=True)
plt.show()