I am trying to change content of an image interactively using a slider (e.g. for applying a median operation with different kernel sizes).
While this works well if I only show one resulting image (cf commented lines), I run into trouble when using the subplot function, since the image will not get updated.
What am I missing?
%matplotlib inline
from ipywidgets import interact, widgets
import matplotlib.pyplot as plt
import warnings
from skimage.morphology import disk
from skimage.filters import rank
from skimage.color import rgb2gray
import skimage.data
def f(Median_Size):
selem = disk(int(Median_Size))
with warnings.catch_warnings():
warnings.simplefilter("ignore")
img_median = rank.median(img_gray, selem=selem)
ax_neu.imshow(img_median, cmap="gray")
fig.canvas.draw()
#plt.imshow(img_median, cmap="gray") #This would work
#plt.show()
image = skimage.data.camera() #plt.imread("Test.png")
img_gray = rgb2gray(image)
fig = plt.figure(figsize=(6, 4))
ax_orig = fig.add_subplot(121)
ax_neu = fig.add_subplot(122)
ax_orig.imshow(img_gray, cmap="gray")
ax_neu.imshow(img_gray, cmap="gray")
interact(f, Median_Size=widgets.IntSlider(min=1,max=21,step=2,value=1))
Using %matplotlib notebook
Instead of the inline backend, you may use the notebook backend. This will allow to call figure.canvas.draw() as expected from running the code as a script. Replace the line %matplotlib inline by
%matplotlib notebook
and restart the Kernel.
Using display
You may display the newly changed figure after it has been changed. The drawback is that it creates the output twice. A workaround would then be to put interact in a new cell and capture the output from the first cell.
%%capture
%matplotlib inline
from ipywidgets import interact, widgets
from IPython.display import display
import matplotlib.pyplot as plt
import warnings
from skimage.morphology import disk
from skimage.filters import rank
from skimage.color import rgb2gray
import skimage.data
def f(Median_Size):
selem = disk(int(Median_Size))
with warnings.catch_warnings():
warnings.simplefilter("ignore")
img_median = rank.median(img_gray, selem=selem)
ax_neu.imshow(img_median, cmap="gray")
fig.canvas.draw()
display(fig)
image = skimage.data.camera() #plt.imread("Test.png")
img_gray = rgb2gray(image)
fig = plt.figure(figsize=(6, 4))
ax_orig = fig.add_subplot(121)
ax_neu = fig.add_subplot(122)
ax_orig.imshow(img_gray, cmap="gray")
ax_neu.imshow(img_gray, cmap="gray")
In a new cell
interact(f, Median_Size=widgets.IntSlider(min=1,max=21,step=2,value=1));
The output would then look like:
Related
The following code display the image and audio in the top-bottom style:
Here is the test code:
import librosa
import matplotlib.pyplot as plt
import IPython.display as ipd
def plot_it(name, audio, sample_rate):
plt.figure(figsize=(8, 1))
plt.plot(audio)
plt.gca().set_title(name)
plt.show()
ipd.display(ipd.Audio(data=audio, rate=sample_rate))
Is it possible for changing the "top-bottom" style to "left-right" style for displaying the audio at the right side of the plt figure?
You can use a GridspecLayout which is similar to matplotlib's GridSpec. In order to direct to output into the needed grid cells, you can capture it using the Output widget:
import librosa
import matplotlib.pyplot as plt
import IPython.display as ipd
from ipywidgets import Output, GridspecLayout
def plot_it(name, audio, sample_rate):
grid = GridspecLayout(1, 2, align_items='center')
out = Output()
with out:
fig, ax = plt.subplots(figsize=(8, 1))
ax.plot(audio)
ax.set_title(name)
plt.close(fig)
ipd.display(ax.figure)
grid[0, 0] = out
out = Output()
with out:
ipd.display(ipd.Audio(data=audio, rate=sample_rate))
grid[0, 1] = out
ipd.display(grid)
name = 'nutcracker'
filename = librosa.example(name)
y, sr = librosa.load(filename)
plot_it(name, y, sr)
(It is essential to close the figure, otherwise you'll have double output of the figure. This is easier to do this using the OOP than the pyplot interface, that's why I changed your matplotlib code a bit)
I am trying to add an image as a layer on top of a figure to show a location of a few oceanographic weather buoys on a map.
It works for me up to a certain offset. When I go over a certain tuple number, my whole image gets dislocated and a large white space shows on the bottom. I am using bbox_inches=tight to save the figure the way I need.
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from matplotlib.offsetbox import OffsetImage
from netCDF4 import Dataset as netcdf_dataset
import cartopy.crs as ccrs
varfigsizex=6
varfigsizey=7
fig = plt.figure(figsize=(varfigsizex, varfigsizey))
varllcrnrlon2=-121
varurcrnrlon2=-117
varllcrnrlat2=32.12
varurcrnrlat2=34.7
ax = plt.axes(projection=ccrs.PlateCarree())
ax.set_extent((varllcrnrlon2,varurcrnrlon2,varllcrnrlat2,varurcrnrlat2))
ax.coastlines()
buoy_img = mpimg.imread('buoy_img.png')
add_buoy = OffsetImage(buoy_img, zoom=0.4)
add_buoy.set_offset((230,720))
add_buoy.set_zorder(10)
ax.add_artist(add_buoy)
add_buoy_2 = OffsetImage(buoy_img, zoom=0.4)
add_buoy_2.set_offset((400,720))
add_buoy_2.set_zorder(10)
ax.add_artist(add_buoy_2)
add_buoy_3 = OffsetImage(buoy_img, zoom=0.4)
add_buoy_3.set_offset((650,590))
add_buoy_3.set_zorder(10)
ax.add_artist(add_buoy_3)
add_buoy_4 = OffsetImage(buoy_img, zoom=0.4)
add_buoy_4.set_offset((800,600))
add_buoy_4.set_zorder(10)
ax.add_artist(add_buoy_4)
name_file=''.join(['ca_buoy.png'])
fig.savefig(name_file, bbox_inches='tight', pad_inches=0, format='png', dpi=300)
When I add additional layer images that I need, for example with set_offset((1200,100):
add_buoy_5 = OffsetImage(buoy_img, zoom=0.4)
add_buoy_5.set_offset((1200,100))
add_buoy_5.set_zorder(10)
ax.add_artist(add_buoy_5)
this is what happens:
I would like some help to understand why this happens, and how to properly fix my code.
I made an animation from the list of images saved as numpy arrays.
Then I want to add a text onto the animation like like a subtitle whose text changes for each frame but placing plt.text(some_string) only adds the string at the first iteration and it does not work if I change the passed string in the loop. Below is my attempt. Please note HTML is just for Jupyter Lab.
import matplotlib.animation as animation
from PIL import Image
from IPython.display import HTML
import matplotlib.pyplot as plt
folderName = "hogehoge"
picList = glob.glob(folderName + "\*.npy")
fig = plt.figure()
ims = []
for i in range(len(picList)):
plt.text(10, 10, i) # This does not add the text properly
tmp = Image.fromarray(np.load(picList[i]))
ims.append(plt.imshow(tmp))
ani = animation.ArtistAnimation(fig, ims, interval=200)
HTML(ani.to_jshtml())
You have also to add the text object to the list of artists for each frame:
import matplotlib.animation as animation
from PIL import Image
from IPython.display import HTML
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ims = []
for i in range(10):
artists = ax.plot(np.random.rand(10), np.random.rand(10))
text = ax.text(x=0.5, y=0.5, s=i)
artists.append(text)
ims.append(artists)
ani = animation.ArtistAnimation(fig, ims, interval=200)
HTML(ani.to_jshtml())
I have a python animation script (using matplotlib's funcAnimation), which runs in Spyder but not in Jupyter. I have tried following various suggestions such as adding "%matplotlib inline" and changing the matplotlib backend to "Qt4agg", all without success. I have also tried running several example animations (from Jupyter tutorials), none of which have worked. Sometimes I get an error message and sometimes the plot appears, but does not animate. Incidentally, I have gotten pyplot.plot() to work using "%matplotlib inline".
Does anyone know of a working Jupyter notebook with a simple inline animation example that uses funcAnimation.
[Note: I am on Windows 7]
notebook backend
'Inline' means that the plots are shown as png graphics. Those png images cannot be animated. While in principle one could build an animation by successively replacing the png images, this is probably undesired.
A solution is to use the notebook backend, which is fully compatible with FuncAnimation as it renders the matplotlib figure itself:
%matplotlib notebook
jsanimation
From matplotlib 2.1 on, we can create an animation using JavaScript. This is similar to the ani.to_html5() solution, except that it does not require any video codecs.
from IPython.display import HTML
HTML(ani.to_jshtml())
Some complete example:
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
t = np.linspace(0,2*np.pi)
x = np.sin(t)
fig, ax = plt.subplots()
ax.axis([0,2*np.pi,-1,1])
l, = ax.plot([],[])
def animate(i):
l.set_data(t[:i], x[:i])
ani = matplotlib.animation.FuncAnimation(fig, animate, frames=len(t))
from IPython.display import HTML
HTML(ani.to_jshtml())
Alternatively, make the jsanimation the default for showing animations,
plt.rcParams["animation.html"] = "jshtml"
Then at the end simply state ani to obtain the animation.
Also see this answer for a complete overview.
There is a simple example within this tutorial here: http://louistiao.me/posts/notebooks/embedding-matplotlib-animations-in-jupyter-notebooks/
To summarise the tutorial above, you basically need something like this:
from matplotlib import animation
from IPython.display import HTML
# <insert animation setup code here>
anim = animation.FuncAnimation() # With arguments of course!
HTML(anim.to_html5_video())
However...
I had a lot of trouble getting that to work. Essentially, the problem was that the above uses (by default) ffmpeg and the x264 codec in the background but these were not configured correctly on my machine. The solution was to uninstall them and rebuild them from source with the correct configuration. For more details, see the question I asked about it with a working answer from Andrew Heusser: Animations in ipython (jupyter) notebook - ValueError: I/O operation on closed file
So, try the to_html5_video solution above first, and if it doesn't work then also try the uninstall / rebuild of ffmpeg and x264.
Another option:
import matplotlib.animation
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams["animation.html"] = "jshtml"
plt.rcParams['figure.dpi'] = 150
plt.ioff()
fig, ax = plt.subplots()
x= np.linspace(0,10,100)
def animate(t):
plt.cla()
plt.plot(x-t,x)
plt.xlim(0,10)
matplotlib.animation.FuncAnimation(fig, animate, frames=10)
Here is the answer that I put together from multiple sources including the official examples. I tested with the latest versions of Jupyter and Python.
Download FFmpeg ( http://ffmpeg.zeranoe.com/builds/ )
Install FFmpeg making sure that you update the environmental variable ( http://www.wikihow.com/Install-FFmpeg-on-Windows ).
Run this script in Jupyter below. The variable imageList is the only thing that you need to modify. It is an list of images (your input).
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython.display import HTML
#=========================================
# Create Fake Images using Numpy
# You don't need this in your code as you have your own imageList.
# This is used as an example.
imageList = []
x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
for i in range(60):
x += np.pi / 15.
y += np.pi / 20.
imageList.append(np.sin(x) + np.cos(y))
#=========================================
# Animate Fake Images (in Jupyter)
def getImageFromList(x):
return imageList[x]
fig = plt.figure(figsize=(10, 10))
ims = []
for i in range(len(imageList)):
im = plt.imshow(getImageFromList(i), animated=True)
ims.append([im])
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000)
plt.close()
# Show the animation
HTML(ani.to_html5_video())
#=========================================
# Save animation as video (if required)
# ani.save('dynamic_images.mp4')
If you have a list of images and want to animate through them, you can use something like this:
from keras.preprocessing.image import load_img, img_to_array
from matplotlib import animation
from IPython.display import HTML
import glob
%matplotlib inline
def plot_images(img_list):
def init():
img.set_data(img_list[0])
return (img,)
def animate(i):
img.set_data(img_list[i])
return (img,)
fig = figure()
ax = fig.gca()
img = ax.imshow(img_list[0])
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=len(img_list), interval=20, blit=True)
return anim
imgs = [img_to_array(load_img(i)) for i in glob.glob('*.jpg')]
HTML(plot_images(imgs).to_html5_video())
Thank to Kolibril. I finally can run animation on Jupyter and Google Colab.
I modify some code which will generate animation of drawing random line instead.
import matplotlib.animation
import matplotlib.pyplot as plt
from itertools import count
import random
plt.rcParams["animation.html"] = "jshtml"
plt.rcParams['figure.dpi'] = 150
fig, ax = plt.subplots()
x_value = []
y_value = []
index = count();
def animate(t):
x_value.append(next(index))
y_value.append(random.randint(0,10))
ax.cla()
ax.plot(x_value,y_value)
ax.set_xlim(0,10)
matplotlib.animation.FuncAnimation(fig, animate, frames=10, interval = 500)
enter image description here
In this Kaggle Kernel https://www.kaggle.com/asindico/new-york-taxi-exploration I can't manage to show a matplotlib animation using on Basemap and hexbin.
Here is the code snippet
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib import cm
from matplotlib import animation, rc
from IPython.display import HTML
%matplotlib inline
#40.758896, -73.985130.
west, south, east, north = -74.1, 40.60, -73.80, 40.9
day=1
df_day=df[((df.pickup_datetime<'2016-01-'+str(day+1))&
(df.pickup_datetime>='2016-01-'+str(day)))]
fig,axarr = plt.subplots()
m = Basemap(projection='merc', llcrnrlat=south, urcrnrlat=north,
llcrnrlon=west, urcrnrlon=east, lat_ts=south, resolution='i',ax=axarr)
def init():
df_time= df_day[((df_day.pu_hour>=0)&(df_day.pu_hour<0))]
x, y = m(df_time['pickup_longitude'].values, df_time['pickup_latitude'].values)
m.hexbin(x, y, gridsize=300, bins='log', cmap=cm.YlOrRd_r);
return (m,)
def animate(i):
df_time= df_day[((df_day.pu_hour>=i)&(df_day.pu_hour<i+1))]
x, y = m(df_time['pickup_longitude'].values, df_time['pickup_latitude'].values)
m.hexbin(x, y, gridsize=300, bins='log', cmap=cm.YlOrRd_r);
return (m,)
anim = animation.FuncAnimation(fig, animate, init_func=init,frames=5, interval=1,blit=False,repeat=True)
fig.set_size_inches(12,10)
anim.save('pickup_animation.gif', writer='imagemagick', fps=2)
To display the animated gif inside the notebook you may use
from IPython.display import Image, display
display(Image(url='pickup_animation.gif'))
You may also show the animation as html5 video
from IPython.display import HTML
HTML(anim.to_html5_video())
You may also show the matplotlib animation directly, using the notebook backend instead of the inline backend
%matplotlib notebook