I'm working on some computer vision algorithm and I'd like to show how a numpy array changes in each step.
What works now is that if I have a simple imshow( array ) at the end of my code, the window displays and shows the final image.
However what I'd like to do is to update and display the imshow window as the image changes in each iteration.
So for example I'd like to do:
import numpy as np
import matplotlib.pyplot as plt
import time
array = np.zeros( (100, 100), np.uint8 )
for i in xrange( 0, 100 ):
for j in xrange( 0, 50 ):
array[j, i] = 1
#_show_updated_window_briefly_
plt.imshow( array )
time.sleep(0.1)
The problem is that this way, the Matplotlib window doesn't get activated, only once the whole computation is finished.
I've tried both native matplotlib and pyplot, but the results are the same. For plotting commands I found an .ion() switch, but here it doesn't seem to work.
Q1. What is the best way to continuously display updates to a numpy array (actually a uint8 greyscale image)?
Q2. Is it possible to do this with an animation function, like in the dynamic image example? I'd like to call a function inside a loop, thus I don't know how to achieve this with an animation function.
You don't need to call imshow all the time. It is much faster to use the object's set_data method:
myobj = imshow(first_image)
for pixel in pixels:
addpixel(pixel)
myobj.set_data(segmentedimg)
draw()
The draw() should make sure that the backend updates the image.
UPDATE: your question was significantly modified. In such cases it is better to ask another question. Here is a way to deal with your second question:
Matplotlib's animation only deals with one increasing dimension (time), so your double loop won't do. You need to convert your indices to a single index. Here is an example:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
nx = 150
ny = 50
fig = plt.figure()
data = np.zeros((nx, ny))
im = plt.imshow(data, cmap='gist_gray_r', vmin=0, vmax=1)
def init():
im.set_data(np.zeros((nx, ny)))
def animate(i):
xi = i // ny
yi = i % ny
data[xi, yi] = 1
im.set_data(data)
return im
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=nx * ny,
interval=50)
I struggled to make it work because many post talk about this problem, but no one seems to care about providing a working example. In this case however, the reasons were different :
I couldn't use Tiago's or Bily's answers because they are not in the
same paradigm as the question. In the question, the refresh is
scheduled by the algorithm itself, while with funcanimation or
videofig, we are in an event driven paradigm. Event driven
programming is unavoidable for modern user interface programming, but
when you start from a complex algorithm, it might be difficult to
convert it to an event driven scheme - and I wanted to be able to do
it in the classic procedural paradigm too.
Bub Espinja reply suffered another problem : I didn't try it in the
context of jupyter notebooks, but repeating imshow is wrong since it
recreates new data structures each time which causes an important
memory leak and slows down the whole display process.
Also Tiago mentioned calling draw(), but without specifying where to get it from - and by the way, you don't need it. the function you really need to call is flush_event(). sometime it works without, but it's because it has been triggered from somewhere else. You can't count on it. The real tricky point is that if you call imshow() on an empty table, you need to specify vmin and vmax or it will fail to initialize it's color map and set_data will fail too.
Here is a working solution :
IMAGE_SIZE = 500
import numpy as np
import matplotlib.pyplot as plt
plt.ion()
fig1, ax1 = plt.subplots()
fig2, ax2 = plt.subplots()
fig3, ax3 = plt.subplots()
# this example doesn't work because array only contains zeroes
array = np.zeros(shape=(IMAGE_SIZE, IMAGE_SIZE), dtype=np.uint8)
axim1 = ax1.imshow(array)
# In order to solve this, one needs to set the color scale with vmin/vman
# I found this, thanks to #jettero's comment.
array = np.zeros(shape=(IMAGE_SIZE, IMAGE_SIZE), dtype=np.uint8)
axim2 = ax2.imshow(array, vmin=0, vmax=99)
# alternatively this process can be automated from the data
array[0, 0] = 99 # this value allow imshow to initialise it's color scale
axim3 = ax3.imshow(array)
del array
for _ in range(50):
print(".", end="")
matrix = np.random.randint(0, 100, size=(IMAGE_SIZE, IMAGE_SIZE), dtype=np.uint8)
axim1.set_data(matrix)
fig1.canvas.flush_events()
axim2.set_data(matrix)
fig1.canvas.flush_events()
axim3.set_data(matrix)
fig1.canvas.flush_events()
print()
UPDATE : I added the vmin/vmax solution based on #Jettero's comment (I missed it at first).
If you are using Jupyter, maybe this answer interests you.
I read in this site that the emmbebed function of clear_output can make the trick:
%matplotlib inline
from matplotlib import pyplot as plt
from IPython.display import clear_output
plt.figure()
for i in range(len(list_of_frames)):
plt.imshow(list_of_frames[i])
plt.title('Frame %d' % i)
plt.show()
clear_output(wait=True)
It is true that this method is quite slow, but it can be used for testing purposes.
I implemented a handy script that just suits your needs. Try it out here
An example that shows images in a custom directory is like this:
import os
import glob
from scipy.misc import imread
img_dir = 'YOUR-IMAGE-DIRECTORY'
img_files = glob.glob(os.path.join(video_dir, '*.jpg'))
def redraw_fn(f, axes):
img_file = img_files[f]
img = imread(img_file)
if not redraw_fn.initialized:
redraw_fn.im = axes.imshow(img, animated=True)
redraw_fn.initialized = True
else:
redraw_fn.im.set_array(img)
redraw_fn.initialized = False
videofig(len(img_files), redraw_fn, play_fps=30)
I had a similar problem - want to update image, don't want to repeatedly replace the axes, but plt.imshow() (nor ax.imshow()) was not updating the figure displayed.
I finally discovered that some form of draw() was required. But fig.canvas.draw(), ax.draw() ... all did not work. I finally found the solution here:
%matplotlib notebook #If using Jupyter Notebook
import matplotlib.pyplot as plt
import numpy as np
imData = np.array([[1,3],[3,1]])
# Setup and plot image
fig = plt.figure()
ax = plt.subplot(111)
im = ax.imshow(imData)
# Change image contents
newImData = np.array([[2,2],[2,2]])
im.set_data( newImData )
im.draw()
import numpy as np
import matplotlib.pyplot as plt
k = 10
plt.ion()
array = np.zeros((k, k))
for i in range(k):
for j in range(k):
array[i, j] = 1
plt.imshow(array)
plt.show()
plt.pause(0.001)
plt.clf()
Related
I want to plot a 3D tensor plane by plane using matplotlib in a loop.
However, in this example, matplotlib keeps on adding colorbars to the figure:
data = np.random.rand(100,100,10)
for i in range(10):
plt.imshow(np.squeeze(data[:, :, i]))
plt.colorbar()
plt.pause(2)
print(i)
Caveat: I've seen some complicated answers to this simple question, which didn't work. The problem may sound simple, but I'm thinking there might be an easy (short) solution.
The easy solution
Clear the figure in each loop run.
import numpy as np
import matplotlib.pyplot as plt
data = np.random.rand(100,100,10) * np.linspace(1,7,10)
fig = plt.figure()
for i in range(10):
plt.clf()
plt.imshow(np.squeeze(data[:, :, i]))
plt.colorbar()
plt.pause(2)
plt.show()
The efficient solution
Use the same image and just update the data. Also use a FuncAnimation instead of a loop to run everything within the GUI event loop.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
data = np.random.rand(100,100,10) * np.linspace(1,7,10)
fig, ax = plt.subplots()
im = ax.imshow(np.squeeze(data[:, :, 0]))
cbar = fig.colorbar(im, ax=ax)
def update(i):
im.set_data(data[:, :, i])
im.autoscale()
ani = FuncAnimation(fig, update, frames=data.shape[2], interval=2000)
plt.show()
So here is a solution. Unfortunately it is not short at all. If someone knows how to make this less complicated, feel free to post another answer.
This is slightly modified version of this answer
import matplotlib.pyplot as plt
import numpy as np
def visualize_tensor(data, delay=0.5):
""" data must be 3 dimensional array and
have format:
[height x width x channels]"""
assert(np.ndim(data) == 3)
# Get number of channels from last dimension
num_channels = np.shape(data)[-1]
# Plot data of first channel
fig = plt.figure()
ax = fig.add_subplot(111)
data_first_channel = data[:, :, 0]
plot = ax.imshow(data_first_channel)
# Create colorbar
cbar = plt.colorbar(plot)
plt.show(block=False)
# Iterate over all channels
for i in range(num_channels):
print(f"channel = {i}")
data_nth_channel = np.squeeze(data[:, :, i])
plot.set_data(data_nth_channel)
plot.autoscale()
vmin = np.min(data_nth_channel.view()) # get minimum of nth channel
vmax = np.max(data_nth_channel.view()) # get maximum of nth channel
cbar.set_clim(vmin=vmin, vmax=vmax)
cbar_ticks = np.linspace(vmin, vmax, num=11, endpoint=True)
cbar.set_ticks(cbar_ticks)
cbar.draw_all()
plt.draw()
plt.pause(delay)
Example execution:
data = np.random.rand(20,20,10)
visualize_tensor(data)
Update:
Using plot.autoscale() forces the colorbar to adapt dynamically, see this answer
This question intrigued me as hacking at matplotlib is somewhat my hobby. Next to the solution posed by #mcExchange one could use this
from matplotlib.pyplot import subplots
import numpy as np
%matplotlib notebook
d = np.random.rand(10, 10)
fig, ax = subplots(figsize = (2,2))
# create mappable
h = ax.imshow(d)
# create colorbar
cb = fig.colorbar(h)
# show non-blocking
fig.show(0)
for i in range(100):
# generate new data
h.set_data(np.random.randn(*d.shape) + 1)
h.autoscale()
# flush events update time
ax.set_title(f't = {i}')
fig.canvas.draw(); fig.canvas.flush_events();
How did I get this solution?
The docs state that colorbar.update_normal only updates if the norm on the mappable is different than before. Setting the data doesn't change this. As such manually function have to be called to register this update.
Behind the scene the following happens:
# rescale data for cb trigger
h.norm.autoscale(h._A) #h._A is the representation of the data
# update mappable
h.colorbar.update_normal(h.colorbar.mappable)
I'm trying to plot a 2D grid of data and map them to colors. Then I want to update the values and have the graph update with the new values. Currently the graph only shows the final result, not all the middle phases the graph should go through.
MY CODE::
import matplotlib.pyplot as pyplot
import matplotlib as mpl
import numpy as np
import time
import matplotlib.animation as animation
thing=0
NUM_COL=10
NUM_ROW=10
zvals=np.full((NUM_ROW,NUM_COL),-5.0)
def update_graph(zvals):
zvals+=1
pyplot.clf()
img = pyplot.imshow(zvals,interpolation='nearest',
cmap = cmap,norm=norm)
time.sleep(1)
pyplot.draw()
# make a color map of fixed colors
cmap = mpl.colors.ListedColormap(['blue','black','red'])
bounds=[-6,-2,2,6]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
# tell imshow about color map so that only set colors are used
img = pyplot.imshow(zvals,interpolation='nearest',
cmap = cmap,norm=norm)
# make a color bar
pyplot.colorbar(img,cmap=cmap,norm=norm,boundaries=bounds,ticks=[-5,0,5])
pyplot.draw()
for i in range(5):
update_graph(zvals)
pyplot.show()
pyplot does not generally show anything until pyplot.show() is called, unless matplotlib runs in 'interactive' mode. The interactive mode is entered by calling pyplot.ion() and can exited again by calling pyplot.ioff().
Thus it should be possible for you to see all your updates by calling pyplot.ion() somewhere before doing anything you want to be directly updated and then end your program with pyplot.ioff() to get back to the standard pyplot way when done.
However, it may not look very smooth, depending on your system and what updates you are doing.
So I'm not sure if this a good answer or not, I have only used updating plots once before. But this is a way to achieve what you want.
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
NUM_COL = 10
NUM_ROW = 10
zvals = np.full((NUM_ROW,NUM_COL),-5.0)
cmap = mpl.colors.ListedColormap(['blue','black','red'])
bounds = [-6,-2,2,6]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
fig = plt.figure() # Create the figure
img = plt.imshow(zvals,interpolation='nearest', cmap=cmap,norm=norm) # display the first image
plt.colorbar(img,cmap=cmap,norm=norm,boundaries=bounds,ticks=[-5,0,5]) # create your colour bar
# If we dont have this, then animation.FuncAnimation will call update_graph upon initialization
def init():
pass
# animation.FuncAnimation will use this function to update the plot. This is where we update what we want displayed
def update_graph(frame):
global zvals # zvals is a global variable
zvals+=1
img.set_data(zvals) # This sets the data to the new, updated values
print("Frame Update {}".format(frame)) # this is for debugging to help you see whats going on
return img
# This is what will run the animations
anim = animation.FuncAnimation(fig, update_graph, init_func = init,
interval = 1000, # update every 1000ms
frames = 8, # Update 8 times
repeat=False) # After 8 times, don't repeat the animation
plt.show() # show our plot
I want to show a jpg in a window which updates multiple times per second.
I have coded a very very compact program with just 100 lines of code (a neural network which creates the image) and don't want to put in another 100 lines of code to just show the image.
Is there anything I can do to solve this problem?
Many thx, jj
As it was stated in the comments that IO is not an issue, we shall go straight to the available standard image plot tools used in matplotlib, since it is the defacto standard plotting library for python. While not knowing the dimensions of typical images originating in neural networks, a quick comparison of the average time it would take to call e.g. imshow, pcolormesh and matshow for different image dimensions cannot hurt (pcolor is significantly slower, so it is omitted).
import matplotlib.pyplot as plt
import numpy as np
import timeit
n = 13
repeats = 20
timetable = np.zeros((4, n-1))
labellist = ['imshow', 'matshow', 'pcolormesh']
for i in range(1, n):
image = np.random.rand(2**i, 2**i)
print('image size:', 2**i)
timetable[0, i - 1] = 2**i
timetable[1, i - 1] = timeit.timeit("plt.imshow(image)", setup="from __main__ import plt, image", number=repeats)/repeats
plt.close('all')
timetable[2, i - 1] = timeit.timeit("plt.matshow(image)", setup="from __main__ import plt, image", number=repeats)/repeats
plt.close('all')
timetable[3, i - 1] = timeit.timeit("plt.pcolormesh(image)", setup="from __main__ import plt, image", number=repeats)/repeats
plt.close('all')
for i in range(1, 4):
plt.semilogy(timetable[0, :], timetable[i, :], label=labellist[i - 1])
plt.legend()
plt.xlabel('image size')
plt.ylabel('avg. exec. time [s]')
plt.ylim(1e-3, 1)
plt.show()
So, imshow it is. An elegant way to update or animate a plot in matplotlib is the animation framework it offers. That way one does not have to bother with many lines of code, as it was asked for. Here is a simple example:
import matplotlib.pyplot as plt
import numpy as np
import time
from matplotlib import animation
data = np.random.rand(128, 128)
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
im = ax.imshow(data, animated=True)
def update_image(i):
data = np.random.rand(128, 128)
im.set_array(data)
# time.sleep(.5)
# plt.pause(0.5)
ani = animation.FuncAnimation(fig, update_image, interval=0)
plt.show()
In this example the neural network would be called out of the update function. The update behaviour under heavy computational work can be emulated by time.sleep. If your application is multi-threaded plt.pause might come in handy to give the other threads time to do their work. interval=0 basically makes the plot update as often as possible.
I hope this points you in the general direction and is helpful. If you do not want to utilize animations, canvas clearing and/or blitting need to be taken care of manually.
I've been struggling with this for a while. I have a set of images, I perform some math on the X, Y coordinates of these images and then plot the new images using pcolormesh. All the calculations I've already done, all I do is load the new X's and new Y's and use the colors from the image in pcolormesh.
The images are 2048x2448 pixels (say approx 5mp), first image goes pretty fast and every image after that the script gets slower and eats more memory. I have tried some garbage collection but it doesn't work.
My script:
import numpy as np
from PIL import Image
import cPickle as pickle
import matplotlib.pyplot as plt
import os
# TRY forced garbage collection!
import gc
def rectify(cam_files, cam_rec_files, rec_file_location):
''' cam_files is a dictionary that contains the filenames with the camera-names as index
example: {'KDXX04C' : C:\Users\Yorian\Desktop\TU\Stage Shore\python_files\Rectify, metadata and upload\v3\archive\KDXXXXX\original\snap\1381383000\{filename}.jpg }
cam_rec_files_dir contains a dictionary, cameraName : fileLocation
example: {'KDXX04C' : C:\Users\Yorian\Desktop\TU\Stage Shore\python_files\Rectify, metadata and upload\v3\camdata\KDXXXXX\KDXX04C.pkl }
rec_file_location is a string that shows where the new rectification needs to be saved '''
fig, ax = plt.subplots(1, 1, figsize=(60,90))
for camname in cam_files:
img = Image.open(cam_files[camname])
img = np.asarray(img, dtype=np.uint8)
height, width, channels = img.shape
# load plot data
fh = open(cam_rec_files[camname], 'rb')
X = pickle.load(fh)
Y = pickle.load(fh)
masks = [X<=0, X>1500, Y>4000, Y<-4000]
total_mask = masks[0] | masks[1] | masks[2] | masks[3]
first_false = np.argwhere(total_mask == 0)
start = int(first_false[0]/width)
rgb = img.reshape((-1,3))/255.0
rgba = np.concatenate((rgb, np.ones((rgb.shape[0],1), dtype=np.uint8)), axis=1)
rgba[total_mask,3] = 0
rgba = rgba.reshape((height,width,4))[:,:-1,:]
rgba = rgba.reshape((-1,4))
plotimg = ax.pcolormesh(X.reshape((height, width))[start:,:], Y.reshape((height, width))[start:,:], img.mean(-1)[start:,:], cmap='Greys') # img.mean(-1)
plotimg.set_array(None)
plotimg.set_edgecolor('none')
plotimg.set_facecolor(rgba[(start*(width-1)):,:])
fh.close()
plt.savefig(rec_file_location)
gc.collect()
It works until six images, but when I try eight for example I have insufficient memory (I use python 64bit and have 12gb of memory on my computer which I imagined to be enough).
Does anybody have an idea on how to solve this problem?
In a nutshell, call plt.close(fig) when you're through with it if you're using the pyplot interface and want to generate lots of figures without displaying them.
Each time you call your rectify function, you're making a new (very large!!) figure and then keeping it in memory. pyplot keeps a reference to the figure so it can be displayed when you call plt.show(). Either call plt.close(fig) or create the figures without using the pyplot state machine. (fig.clf() will also work, but will keep references to a blank figures around.)
Also, given that you're reading in image files, your values are presumably on a regular x and y grid. If so, use imshow instead of pcolormesh. It's much faster and more memory efficient.
As an example of the first issue, your rectify function basically does something like this, and you're presumably calling it repeatedly (as the loop below does):
import numpy as np
import matplotlib.pyplot as plt
def rectify():
fig, ax = plt.subplots()
ax.pcolormesh(np.random.random((10,10)))
fig.savefig('blah.png')
for _ in range(10):
rectify()
plt.show()
Notice that we'll get 10 figures popping up. pyplot holds on to a reference to the figure so that it can be displayed with show.
If you want to remove the figure from the pyplot state machine, call plt.close(fig).
For example, no figures will be displayed if you do this: (each figure will be garbage collected as you'd expect after you remove the figure from pyplot's figure manager by calling plt.close(fig).)
import numpy as np
import matplotlib.pyplot as plt
def rectify():
fig, ax = plt.subplots()
ax.pcolormesh(np.random.random((10,10)))
fig.savefig('blah.png')
plt.close(fig)
for _ in range(10):
rectify()
plt.show()
Alternately, you can bypass pyplot and make the figure and canvas directly. Pyplot's figure manager won't be involved, and the figure instance will be garbage collected as you'd expect. However, this method is rather verbose, and assumes you know a bit more about how matplotlib works behind-the-scenes:
import numpy as np
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
# Don't need this, but just to demonstrate that `show()` has no effect...
import matplotlib.pyplot as plt
def rectify():
fig = Figure()
FigureCanvas(fig)
ax = fig.add_subplot(1,1,1)
ax.pcolormesh(np.random.random((10,10)))
fig.savefig('blah.png')
for _ in range(10):
rectify()
plt.show()
You can reduce the needed memory if you clear the stored data in ax.
Add ax.clear() at the end of Joe Kington's rectify function. Then the program only need the memory of one pcolormesh.
I want to plot a sequence of .png images in matplotlib. The goal is to plot them rapidly to simulate the effect of a movie, but I have additional reasons for wanting to avoid actually creating an .avi file or saving matplotlib figures and then viewing them in sequence outside of Python.
I'm specifically trying to view the image files in sequence inside a for-loop in Python. Assuming I have imported matplotlib correctly, and I have my own functions 'new_image()' and 'new_rect()', here's some example code that fails to work because of the blocking effect of the show() function's call to the GUI mainloop:
for index in index_list:
img = new_image(index)
rect = new_rect(index)
plt.imshow(img)
plt.gca().add_patch(rect)
plt.show()
#I also tried pausing briefly and then closing, but this doesn't
#get executed due to the GUI mainloop from show()
time.sleep(0.25)
plt.close()
The above code works to show only the first image, but then the program just hangs and waits for me to manually close the resultant figure window. Once I do close it, the program then just hangs and doesn't re-plot with the new image data. What should I be doing? Also note that I have tried replacing the plt.show() command with a plt.draw() command, and then adding the plt.show() outside of the for-loop. This doesn't display anything and just hangs.
Based on http://matplotlib.sourceforge.net/examples/animation/simple_anim_tkagg.html:
import time
import numpy as np
import matplotlib
matplotlib.use('TkAgg') # do this before importing pylab
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
def animate():
tstart = time.time() # for profiling
data=np.random.randn(10,10)
im=plt.imshow(data)
for i in np.arange(1,200):
data=np.random.randn(10,10)
im.set_data(data)
fig.canvas.draw() # redraw the canvas
print 'FPS:' , 200/(time.time()-tstart)
win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate)
plt.show()
plt.imshow can accept a float array, uint8 array, or a PIL image.
So if you have a directory of PNG files, you could open them as PIL images and animate them like this:
import matplotlib
matplotlib.use('TkAgg') # do this before importing pylab
import matplotlib.pyplot as plt
import Image
import glob
fig = plt.figure()
ax = fig.add_subplot(111)
def animate():
filenames=sorted(glob.glob('*.png'))
im=plt.imshow(Image.open(filenames[0]))
for filename in filenames[1:]:
image=Image.open(filename)
im.set_data(image)
fig.canvas.draw()
win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate)
plt.show()
The best way I have found for this was with the command pylab.ion() after you import pylab.
Here is a script that does use show(), but which displays the different plots each time pylab.draw() is called, and which leaves the plot windows showing indefinitely. It uses simple input logic to decide when to close the figures (because using show() means pylab won't process clicks on the windows x button), but that should be simple to add to your gui as another button or as a text field.
import numpy as np
import pylab
pylab.ion()
def get_fig(fig_num, some_data, some_labels):
fig = pylab.figure(fig_num,figsize=(8,8),frameon=False)
ax = fig.add_subplot(111)
ax.set_ylim([0.1,0.8]); ax.set_xlim([0.1, 0.8]);
ax.set_title("Quarterly Stapler Thefts")
ax.pie(some_data, labels=some_labels, autopct='%1.1f%%', shadow=True);
return fig
my_labels = ("You", "Me", "Some guy", "Bob")
# To ensure first plot is always made.
do_plot = 1; num_plots = 0;
while do_plot:
num_plots = num_plots + 1;
data = np.random.rand(1,4).tolist()[0]
fig = get_fig(num_plots,data,my_labels)
fig.canvas.draw()
pylab.draw()
print "Close any of the previous plots? If yes, enter its number, otherwise enter 0..."
close_plot = raw_input()
if int(close_plot) > 0:
pylab.close(int(close_plot))
print "Create another random plot? 1 for yes; 0 for no."
do_plot = raw_input();
# Don't allow plots to go over 10.
if num_plots > 10:
do_plot = 0
pylab.show()
By modifying the basic logic here, I can have it close windows and plot images consecutively to simulate playing a movie, or I can maintain keyboard control over how it steps through the movie.
Note: This has worked for me across platforms and seems strictly superior to the window canvas manager approach above, and doesn't require the 'TkAgg' option.
I have implemented a handy script that just suits your need. Try it out here
Below is a example that show images together with its bounding box:
import os
import glob
from scipy.misc import imread
from matplotlib.pyplot import Rectangle
video_dir = 'YOUR-VIDEO-DIRECTORY'
img_files = glob.glob(os.path.join(video_dir, '*.jpg'))
box_files = glob.glob(os.path.join(video_dir, '*.txt'))
def redraw_fn(f, axes):
img = imread(img_files[f])
box = bbread(box_files[f]) # Define your own bounding box reading utility
x, y, w, h = box
if not redraw_fn.initialized:
im = axes.imshow(img, animated=True)
bb = Rectangle((x, y), w, h,
fill=False, # remove background
edgecolor="red")
axes.add_patch(bb)
redraw_fn.im = im
redraw_fn.bb = bb
redraw_fn.initialized = True
else:
redraw_fn.im.set_array(img)
redraw_fn.bb.set_xy((x, y))
redraw_fn.bb.set_width(w)
redraw_fn.bb.set_height(h)
redraw_fn.initialized = False
videofig(len(img_files), redraw_fn, play_fps=30)