I want to update a single Matplotlib figure with each timestep in my Notebook. I have referenced numerous API resources and examples forum answers here on StackOverflow, however, all of the proposed code either did not graph or displayed a new figure with each timestep, like this answer,
import matplotlib.pyplot as plt
import time
import random
from collections import deque
import numpy as np
# simulates input from serial port
def random_gen():
while True:
val = random.randint(1,10)
yield val
time.sleep(0.1)
a1 = deque([0]*100)
ax = plt.axes(xlim=(0, 20), ylim=(0, 10))
d = random_gen()
line, = plt.plot(a1)
plt.ion()
plt.ylim([0,10])
plt.show()
for i in range(0,20):
a1.appendleft(next(d))
datatoplot = a1.pop()
line.set_ydata(a1)
plt.draw()
print a1[0]
i += 1
time.sleep(0.1)
plt.pause(0.0001) #add this it will be OK.
and this answer.
import numpy as np
import matplotlib.pyplot as plt
plt.axis([0, 10, 0, 1])
for i in range(10):
y = np.random.random()
plt.scatter(i, y)
plt.pause(0.1)
How can I update a figure with each timestep in Python, via Matplotlib or possibly other means? I appreciate your perspectives.
Thank you :)
Real-time drawing by entering the interactive mode of matplotlib.
If you only use plt.show() to draw, the program will stop executing the subsequent program, so open the drawing window through plt.ion() to enter the interactive mode, use the program plt.plot() to draw in real time, after the drawing is completed, use plt .ioff() exits the interactive mode and uses plt.show() to display the final image data. If plt.show() is not added at the end, it will flash back.
import matplotlib.pyplot as plt
import numpy as np
ax=[]
ay=[]
bx=[]
by=[]
num=0
plt.ion()
# plt.rcParams['savefig.dpi'] = 200
# plt.rcParams['figure.dpi'] = 200
plt.rcParams['figure.figsize'] = (10, 10)
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['lines.linewidth'] = 0.5
while num<100:
plt.clf()
plt.suptitle("TITLE",fontsize=30)
g1=np.random.random()
ax.append(num)
ay.append(g1)
agraphic=plt.subplot(2,1,1)
agraphic.set_title('TABLE1')
agraphic.set_xlabel('x',fontsize=10)
agraphic.set_ylabel('y', fontsize=20)
plt.plot(ax,ay,'g-')
#table2
bx.append(num)
by.append(g1)
bgraghic=plt.subplot(2, 1, 2)
bgraghic.set_title('TABLE2')
bgraghic.plot(bx,by,'r^')
plt.pause(0.4)
if num == 15:
plt.savefig('picture.png', dpi=300)
#break
num=num+1
plt.ioff()
plt.show()
I am trying to change content of an image interactively using a slider (e.g. for applying a threshold operation with different values).
My code is as follows:
#%matplotlib ipympl
%matplotlib widget
import matplotlib.pyplot as plt
import cv2
import numpy as np
import ipywidgets as widgets
from ipywidgets import HBox, IntSlider
from IPython.display import Image
def update_lines(change):
ret,thresh2 = cv2.threshold(img_gray,change.new,255,cv2.THRESH_BINARY)
plt.imshow(thresh2)
fig.canvas.flush_events()
image = cv2.imread("Untitled.jpg")
img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret,thresh2 = cv2.threshold(img_gray,30,255,cv2.THRESH_BINARY)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
slider = IntSlider(
orientation='vertical',
step=1,
value=127,
min=0,
max=255
)
display(HBox([slider, fig.canvas]))
slider.observe(update_lines, names='value')
When executing my code, I have an unexpected behavior: the figure is displayed twice, the first time when I do fig = plt.figure() and the second time when I do display(HBox([slider, fig.canvas])) => see The figure is displayed twice.
How can I display the image only into the HBox ?
When I change the value with the slider, I have the following result => After changing value
It seems that matplotlib cannot directly be persuaded to plot the figure at the figure() call, but it's possible to encapsulate it in an Output widget (taken from here):
output = widgets.Output()
with output:
fig = plt.figure()
# fill figure with content here
display(HBox([slider, output]))
That way, the plot is correctly displayed once.
I've been trying to iterate over files in a folder and show them for two seconds each using this code:
import time
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.image import imread
import glob
import cv2
im = []
for filepath in glob.iglob(r'C:\Users\dan20\Pictures\wedding\beeri and adn. photo/*.jpg'):
a = imread(filepath)
b = cv2.resize(a, dsize = (456,304))
im += [b]
fig,ax = plt.subplots()
for i in im:
time.sleep(2)
ax.axis('off')
ax.imshow(i)
plt.show()
For some reason I can't see the images as long as i use time.sleep().
How can I make each picture to appear for N amount of times?
How about using plt.pause :
import numpy as np
from matplotlib import pyplot as plt
im = [np.random.random((9,9)) for i in range(1,6)]
fig,ax = plt.subplots()
for i in im:
ax.cla()
ax.imshow(i)
ax.axis('off')
plt.pause(2)
which gives :
I'm using Python 3.6 in jupyter notebook. plt.close does not close plot. I tried with plt.ion() also and many other ways.
I want to display image, then wait for pause or input() and then remove the previous image and show the new one.
import matplotlib.pyplot as plt
from time import sleep
from scipy import eye
plt.imshow(eye(3))
plt.show()
sleep(1)
plt.close()
Here is an example that shows a sequence of plots, each for one second. Essential are the commants plt.show(block = False) and plt.pause(1) instead of sleep(1):
import numpy as np
import matplotlib.pyplot as plt
def show_image(n):
fig, ax = plt.subplots()
x = np.linspace(0,1,100)
y = x**n
ax.plot(x,y, label = 'x**{}'.format(n))
ax.legend()
plt.show(block=False)
plt.pause(1)
plt.close(fig)
for i in range(10):
show_image(i)
If I understand correctly, what you want is to show a plot, wait 1 second, then let it close automatically.
This would be achieved as follows.
import matplotlib.pyplot as plt
from scipy import eye
plt.imshow(eye(3))
def show_and_close(sec):
timer = plt.gcf().canvas.new_timer(interval=sec*1000)
timer.add_callback(lambda : plt.close())
timer.single_shot = True
timer.start()
plt.show()
show_and_close(1)
Short version: is there a Python method for displaying an image which shows, in real time, the pixel indices and intensities? So that as I move the cursor over the image, I have a continually updated display such as pixel[103,214] = 198 (for grayscale) or pixel[103,214] = (138,24,211) for rgb?
Long version:
Suppose I open a grayscale image saved as an ndarray im and display it with imshow from matplotlib:
im = plt.imread('image.png')
plt.imshow(im,cm.gray)
What I get is the image, and in the bottom right of the window frame, an interactive display of the pixel indices. Except that they're not quite, as the values are not integers: x=134.64 y=129.169 for example.
If I set the display with correct resolution:
plt.axis('equal')
the x and y values are still not integers.
The imshow method from the spectral package does a better job:
import spectral as spc
spc.imshow(im)
Then in the bottom right I now have pixel=[103,152] for example.
However, none of these methods also shows the pixel values. So I have two questions:
Can the imshow from matplotlib (and the imshow from scikit-image) be coerced into showing the correct (integer) pixel indices?
Can any of these methods be extended to show the pixel values as well?
There a couple of different ways to go about this.
You can monkey-patch ax.format_coord, similar to this official example. I'm going to use a slightly more "pythonic" approach here that doesn't rely on global variables. (Note that I'm assuming no extent kwarg was specified, similar to the matplotlib example. To be fully general, you need to do a touch more work.)
import numpy as np
import matplotlib.pyplot as plt
class Formatter(object):
def __init__(self, im):
self.im = im
def __call__(self, x, y):
z = self.im.get_array()[int(y), int(x)]
return 'x={:.01f}, y={:.01f}, z={:.01f}'.format(x, y, z)
data = np.random.random((10,10))
fig, ax = plt.subplots()
im = ax.imshow(data, interpolation='none')
ax.format_coord = Formatter(im)
plt.show()
Alternatively, just to plug one of my own projects, you can use mpldatacursor for this. If you specify hover=True, the box will pop up whenever you hover over an enabled artist. (By default it only pops up when clicked.) Note that mpldatacursor does handle the extent and origin kwargs to imshow correctly.
import numpy as np
import matplotlib.pyplot as plt
import mpldatacursor
data = np.random.random((10,10))
fig, ax = plt.subplots()
ax.imshow(data, interpolation='none')
mpldatacursor.datacursor(hover=True, bbox=dict(alpha=1, fc='w'))
plt.show()
Also, I forgot to mention how to show the pixel indices. In the first example, it's just assuming that i, j = int(y), int(x). You can add those in place of x and y, if you'd prefer.
With mpldatacursor, you can specify them with a custom formatter. The i and j arguments are the correct pixel indices, regardless of the extent and origin of the image plotted.
For example (note the extent of the image vs. the i,j coordinates displayed):
import numpy as np
import matplotlib.pyplot as plt
import mpldatacursor
data = np.random.random((10,10))
fig, ax = plt.subplots()
ax.imshow(data, interpolation='none', extent=[0, 1.5*np.pi, 0, np.pi])
mpldatacursor.datacursor(hover=True, bbox=dict(alpha=1, fc='w'),
formatter='i, j = {i}, {j}\nz = {z:.02g}'.format)
plt.show()
An absolute bare-bones "one-liner" to do this: (without relying on datacursor)
def val_shower(im):
return lambda x,y: '%dx%d = %d' % (x,y,im[int(y+.5),int(x+.5)])
plt.imshow(image)
plt.gca().format_coord = val_shower(ims)
It puts the image in closure so makes sure if you have multiple images each will display its own values.
All of the examples that I have seen only work if your x and y extents start from 0. Here is code that uses your image extents to find the z value.
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
d = np.array([[i+j for i in range(-5, 6)] for j in range(-5, 6)])
im = ax.imshow(d)
im.set_extent((-5, 5, -5, 5))
def format_coord(x, y):
"""Format the x and y string display."""
imgs = ax.get_images()
if len(imgs) > 0:
for img in imgs:
try:
array = img.get_array()
extent = img.get_extent()
# Get the x and y index spacing
x_space = np.linspace(extent[0], extent[1], array.shape[1])
y_space = np.linspace(extent[3], extent[2], array.shape[0])
# Find the closest index
x_idx= (np.abs(x_space - x)).argmin()
y_idx= (np.abs(y_space - y)).argmin()
# Grab z
z = array[y_idx, x_idx]
return 'x={:1.4f}, y={:1.4f}, z={:1.4f}'.format(x, y, z)
except (TypeError, ValueError):
pass
return 'x={:1.4f}, y={:1.4f}, z={:1.4f}'.format(x, y, 0)
return 'x={:1.4f}, y={:1.4f}'.format(x, y)
# end format_coord
ax.format_coord = format_coord
If you are using PySide/PyQT here is an example to have a mouse hover tooltip for the data
import matplotlib
matplotlib.use("Qt4Agg")
matplotlib.rcParams["backend.qt4"] = "PySide"
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# Mouse tooltip
from PySide import QtGui, QtCore
mouse_tooltip = QtGui.QLabel()
mouse_tooltip.setFrameShape(QtGui.QFrame.StyledPanel)
mouse_tooltip.setWindowFlags(QtCore.Qt.ToolTip)
mouse_tooltip.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)
mouse_tooltip.show()
def show_tooltip(msg):
msg = msg.replace(', ', '\n')
mouse_tooltip.setText(msg)
pos = QtGui.QCursor.pos()
mouse_tooltip.move(pos.x()+20, pos.y()+15)
mouse_tooltip.adjustSize()
fig.canvas.toolbar.message.connect(show_tooltip)
# Show the plot
plt.show()
with Jupyter you can do so either with datacursor(myax)or by ax.format_coord.
Sample code:
%matplotlib nbagg
import numpy as np
import matplotlib.pyplot as plt
X = 10*np.random.rand(5,3)
fig,ax = plt.subplots()
myax = ax.imshow(X, cmap=cm.jet,interpolation='nearest')
ax.set_title('hover over the image')
datacursor(myax)
plt.show()
the datacursor(myax) can also be replaced with ax.format_coord = lambda x,y : "x=%g y=%g" % (x, y)
In case you, like me, work on Google Colab, this solutions do not work as Colab disabled interactive feature of images for matplotlib.
Then you might simply use Plotly:
https://plotly.com/python/imshow/
import plotly.express as px
import numpy as np
img_rgb = np.array([[[255, 0, 0], [0, 255, 0], [0, 0, 255]],
[[0, 255, 0], [0, 0, 255], [255, 0, 0]]
], dtype=np.uint8)
fig = px.imshow(img_rgb)
fig.show()
Matplotlib has built-in interactive plot which logs pixel values at the corner of the screen.
To setup first install pip install ipympl
Then use either %matplotlib notebook or %matplotlib widget instead of %matplotlib inline
The drawback with plotly or Bokeh is that they don't work on Pycharm.
For more information take a look at the doc
To get interactive pixel information of an image use the module imagetoolbox
To download the module open the command prompt and write
pip install imagetoolbox
Write the given code to get interactive pixel information of an image
enter image description here
Output:enter image description here