Matplotlib: no effect of set_data in imshow for the plot - python

I have a strange error which I can't fix without your help. After I set an image with imshow in matplotlib it stays the same all the time even if I change it with the method set_data. Just take a look on this example:
import numpy as np
from matplotlib import pyplot as plt
def newevent(event):
haha[1,1] += 1
img.set_data(haha)
print img.get_array() # the data is change at this point
plt.draw()
haha = np.zeros((2,2))
img = plt.imshow(haha)
print img.get_array() # [[0,0],[0,0]]
plt.connect('button_press_event', newevent)
plt.show()
After I plot it, the method set_data doesn't change anything inside the plot. Can someone explain me why?
EDIT
Just added a few lines to point out what I actually want to do.
I want to redraw the data after I press a mouse button. I don't want to delete the whole figure, because it would be stupid if only one thing changes.

The problem is because you have not updated the pixel scaling after the first call.
When you instantiate imshow, it sets vmin and vmax from the initial data, and never touches it again. In your code, it sets both vmin and vmax to 0, since your data, haha = zeros((2,2)), is zero everywhere.
Your new event should include autoscaling with img.autoscale() , or explicitly set new scaling terms by setting img.norm.vmin/vmax to something you prefer.
The function to set the new vmin and vmax is:
img.set_clim(vmin=new_vim, vmax=new_vmax)

Does this give you the output you expect?
import numpy as np
from matplotlib import pyplot as plt
haha = np.zeros((2,2))
img = plt.imshow(haha)
print img.get_array() # [[0,0],[0,0]]
haha[1,1] += 1
img.set_data(haha)
img = plt.imshow(haha) # <<------- added this line
print img.get_array() # [[0,0],[0,1]]
plt.show()
When I display the plot twice (once before the change to haha, and at the end), it does change.

Related

Updating pyplot graph in real time

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

How can i remove a plot with Python?

I have some data that I plotted with Python but now I want to erase the plots but not the figure itself.
I have some thing like this :
import numpy as np
import pylab as plt
a = np.array([1,2,3,4,5,6,7,8,9,10])
b = np.array([1,2,3,4,5,6,7,8,9,10])
c = plt.plot(a,b,'r.')
So to clear this I tried this :
a = np.array([])
b = np.array([])
c = plt.plot(a,b,'r.')
but it does not work. What is the best way to accomplish this?
You can use the remove method of the returned plot object. This is true of any plot object that inherits from Artist.
c = plt.plot(a,b,'r.')
for handle in c:
handle.remove()
To have axes with the same values of your a, b arrays, you can do:
import matplotlib.pyplot as plt
plt.clf() # To clear the figure.
plt.axis([1,10,1,10])
From here:
When to use cla(), clf() or close() for clearing a plot in matplotlib?
plt.cla() clears an axis, i.e. the currently active axis in the
current figure. It leaves the other axes untouched.
plt.clf() clears the entire current figure with all its axes, but
leaves the window opened, such that it may be reused for other plots.
plt.close() closes a window, which will be the current window, if not
specified otherwise.
Also if you prefer doing it line by line, you can remove them like this even if you've lost original references:
for l in ax.get_lines():
xval = l.get_xdata()[0]
if (xval == my_criteria):
l.remove()
or for all, simply:
for l in ax.get_lines():
l.remove()
likewise you can do the same indexing by y values.

How do I add a colorbar to an image while saving it?

I have a 2 dimensional matrix, say img which I want to save as in image, using the 'hot' colormap. The following code
import matplotlib.pyplot as plt
plt.imsave("out.jpg", img, cmap = 'hot')
allows me to do this. However, I want to save the colorbar along with the image, it is important for me to know what the value at each pixel was, roughly. Let's assume that I have a lot of space available on the right.
I know that it can be done in an "easy" way, by creating a figure using plt.imshow(), adding a colorbar using plt.colorbar() and then saving it using plt.savefig(), but that also saves the "grey region" around the figure and the colorbar appears separately. Also, this has issues with the figure size, in case scaling is involved. I want the colorbar to appear ON the image, and the resultant output image to be of the same size as the matrix.
Also, if anyone has any other suggestions about visualizing a matrix as an image, (where the values are not bounded), they would be appreciated.
I'm not sure how to add a colorbar to your image before you save it like you want to, and I'm not even sure if that's possible. But I can provide you with a way to plot your image, add the colorbar, and save it, but format it the way you want. Try something like the following
import matplotlib.pyplot as plt
fig = plt.figure(figsize = (W,H)) # Your image (W)idth and (H)eight in inches
# Stretch image to full figure, removing "grey region"
plt.subplots_adjust(left = 0, right = 1, top = 1, bottom = 0)
im = plt.imshow('out.jpg') # Show the image
pos = fig.add_axes([0.93,0.1,0.02,0.35]) # Set colorbar position in fig
fig.colorbar(im, cax=pos) # Create the colorbar
plt.savefig('out.jpg')
I can't guarantee that this will work without having something to test it on, but it should be in the right direction for what you want.

How do I update the extent of imshow in matplotlib?

When updating an "imshow" plot in matplotlib, it's best to use im.set_data, rather than using ax.imshow repeatedly in the loop. But what if the extent of the data is changing? Is it possible to update the extent of the data on each iteration of the loop?
Here is an example:
import numpy as np
import matplotlib.pyplot as plt
import time
ax = plt.subplot(111)
plt.ion()
plt.show()
count = 0
for size in np.linspace(1,3,10):
x = np.linspace(-size,size,100)
y = np.linspace(-size,size,100)
X,Y = np.meshgrid(x,y)
R = (X**2+Y**2)**0.5
Z = np.sin(R)/R
ext =(-size,size,-size,size)
if count == 0:
im = plt.imshow(Z,extent=ext)
else:
im.set_data(Z)
# Update the extent of the data
plt.draw()
plt.pause(0.5)
ax.set_xlim(-size,size)
ax.set_ylim(-size,size)
count += 1
plt.ioff()
plt.show()
The colored region should take up the entire axes if I could update the extent properly.
In your example, im.set_extent(ext).
More generally, though, almost any kwarg you can pass in to a matplotlib artist during initialization will have get_foo and set_foo methods. (That's actually how initialization works and how artist.set(...) and plt.setp works, as well.)
If you're looking for how to change a given property, the first place to look is a set_<name> method.
There are exceptions to this. For example, scatter returns a Collection, so you need to call set_offsets instead of set_xy to change the x, y data. Generally speaking, though, it's consistent.

How to update matplotlib's imshow() window interactively?

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()

Categories

Resources